Example #1
0
func uiReset() {
	if w.field != nil {
		w.field.Destroy()
	}

	var err error
	w.field, err = gtk.TextViewNew()
	e.Exit(err)
	w.field.SetAcceptsTab(false)
	w.field.SetWrapMode(gtk.WRAP_WORD)
	w.fieldBuf, err = w.field.GetBuffer()
	e.Exit(err)
	setStyle(w.field, "fontBig")
	w.fieldWindow.Add(w.field)

	r := state.v2result
	textual := w.showActionsText.GetActive() || r.SquareSide == 0
	s := plural(r.NHeaps, "heap")
	if !textual || r.SquareSide <= 9 {
		s += fmt.Sprintf(" in a %d×%d layout", r.SquareSide, r.SquareSide)
	}
	s += fmt.Sprintf(". %s:\n", plural(len(r.Actions), "action"))
	if textual {
		s += fmt.Sprintf("%s.\n", r.String())
	}
	if w.showPermutation.GetActive() {
		s += fmt.Sprintf("Permutation: %v.\n", r.Perm)
	}
	w.fieldBuf.SetText(s)
	if !textual {
		addGraphicalActions()
	}
	w.field.ShowAll()
}
Example #2
0
func addGraphicalActions() {
	r := state.v2result
	side := r.SquareSide
	cells := make([]int, 1+side*side)
	areaSize := 4 + side*w.cellSize.GetValueAsInt()
	tail := w.fieldBuf.GetEndIter()
	for i := 0; i < len(r.Actions); i++ {
		for k, cell := range cells {
			if cell != cellEmpty && cell != cellOccupied {
				cells[k]--
			}
		}

		cells[r.Actions[i]] = cellCurrent

		if i+1 < len(r.Actions) && r.Actions[i+1] < 0 {
			i++
			cells[-r.Actions[i]] = cellMerged
		}

		area, err := gtk.DrawingAreaNew()
		e.Exit(err)
		area.SetSizeRequest(areaSize, areaSize)
		setStyle(area, "borderSolid")
		areaCells := make([]int, len(cells))
		copy(areaCells, cells)
		area.Connect("draw", func(area *gtk.DrawingArea, cr *cairo.Context) {
			drawTable(area, cr, side, areaCells)
		})
		w.field.AddChildAtAnchor(area, w.fieldBuf.CreateChildAnchor(tail))
	}
}
Example #3
0
func uiShuffle() {
	text, err := w.count.GetText()
	e.Exit(err)
	n, err := strconv.Atoi(text)
	if err != nil || n < 1 {
		return
	}
	state.v2result = v2shuffle(n)
	uiReset()
}
Example #4
0
func uiSetup() {
	var err error

	cssProvider, err = gtk.CssProviderNew()
	e.Exit(err)
	err = cssProvider.LoadFromData(css)
	e.Exit(err)

	w.count, err = gtk.SpinButtonNewWithRange(1, 999, 1)
	e.Exit(err)
	w.count.SetValue(1)

	shuffle, err := gtk.ButtonNewWithLabel("Shuffle")
	e.Exit(err)

	w.showActionsText, err = gtk.CheckButtonNewWithMnemonic("Show actions _text")
	e.Exit(err)
	w.showPermutation, err = gtk.CheckButtonNewWithMnemonic("Show _permutation")
	e.Exit(err)

	w.cellSize, err = gtk.SpinButtonNewWithRange(1, 999, 1)
	e.Exit(err)
	w.cellSize.SetValue(32)

	cellSizeLabel, err := gtk.LabelNew("Cell size:")
	e.Exit(err)

	w.count.Connect("changed", uiShuffle)
	w.count.Connect("activate", uiShuffle)
	shuffle.Connect("clicked", uiShuffle)
	w.showActionsText.Connect("toggled", uiReset)
	w.showPermutation.Connect("toggled", uiReset)
	w.cellSize.Connect("changed", uiReset)

	panel, err := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
	e.Exit(err)
	panel.SetSpacing(5)
	panel.Add(w.count)
	panel.Add(shuffle)
	panel.Add(w.showActionsText)
	panel.Add(w.showPermutation)
	panel.Add(cellSizeLabel)
	panel.Add(w.cellSize)

	w.fieldWindow, err = gtk.ScrolledWindowNew(nil, nil)
	e.Exit(err)

	layout, err := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
	e.Exit(err)
	layout.Add(panel)
	layout.PackStart(w.fieldWindow, true, true, 0)

	window, err := gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
	e.Exit(err)
	window.SetTitle(title)
	window.Add(layout)
	window.Connect("destroy", gtk.MainQuit)
	uiShuffle()
	window.ShowAll()
}
Example #5
0
func main() {
	log.SetFlags(0)
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, usage)
		flag.PrintDefaults()
	}
	flag.Parse()
	if *flRepoTag && *flTagRepo {
		fmt.Fprintln(os.Stderr, "-repo/tag is not compatible with -tag/repo")
		os.Exit(1)
	}
	mode := modes[*flMode]
	if mode == NoMode {
		fmt.Fprintln(os.Stderr, "Unknown mode: ", *flMode)
		os.Exit(1)
	}
	remotePaths := flag.Args()
	if len(remotePaths) < 1 {
		flag.Usage()
		os.Exit(1)
	}

	nSides := len(remotePaths)
	commitInfo := make(map[git.Oid]CommitInfo)
	finalBranchInfo := make(map[string][]BranchInfo)
	roots := []*git.Commit{}
	sideNames := make([]string, nSides)
	for i, path := range remotePaths {
		sideNames[i] = filepath.Base(path)
	}
	var sideNamesTree *Tree
	switch {
	case *flRepoTag:
		sideNamesTree = StringTree(sideNames)
	case *flTagRepo:
		sideNamesTree = StringTree(reverseStrings(sideNames))
	}

	repo, err := git.InitRepository(".", false)
	e.Exit(err)
	tagList, err := repo.Tags.List()
	e.Exit(err)
	if *flTagRepo {
		tagList = reverseStrings(tagList)
	}
	var relevantTagList []string

	for i, remotePath := range remotePaths {
		// Fetch.
		remotePath, err := filepath.Abs(remotePath)
		e.Exit(err)

		name := sideNames[i]
		remoteGlob := fmt.Sprint("refs/remotes/", name, "/*")
		switch {
		case *flRepoTag:
			sideNamesTree.Insert(name, true)
		case *flTagRepo:
			sideNamesTree.Insert(stringutil.Reverse(name), true)
		}

		remote, err := repo.Remotes.Lookup(name)
		if err != nil {
			log.Printf("fetching %q (%s)", name, err)
			remote, err = repo.Remotes.Create(name, remotePath)
			e.Exit(err)
			// Use "git fetch" because libgit2 fetch is horribly slow for big local repos.
			// err = remote.Fetch(nil, nil, "")
			_ = remote
			cmd := exec.Command("git", "fetch", name)
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			err = cmd.Run()
			e.Exit(err)
		}

		// For each commit to be rewritten, store it side number in commitInfo.
		log.Printf("walking %q", name)
		walker, err := repo.Walk()
		e.Exit(err)
		err = walker.PushGlob(remoteGlob)
		e.Exit(err)
		if *flTagRepo || *flRepoTag {
			key := name
			if *flTagRepo {
				key = stringutil.Reverse(name)
			}
			relevantTags := sideNamesTree.FilterStrings(key, tagList)
			for _, tagName := range relevantTags {
				if *flTagRepo {
					tagName = stringutil.Reverse(tagName)
				}
				ref, err := repo.References.Lookup("refs/tags/" + tagName)
				e.Exit(err)
				obj, err := ref.Peel(git.ObjectCommit)
				if err != nil {
					// Ignore tags that do not point to commits.
					continue
				}
				relevantTagList = append(relevantTagList, tagName)
				walker.Push(obj.Id())
				// Add the tag to the list of roots for rewrite.
				commit, err := repo.LookupCommit(obj.Id())
				e.Exit(err)
				roots = append(roots, commit)
			}
		}
		sharedCommitsCount := 0
		err = walker.Iterate(func(commit *git.Commit) bool {
			oid := *commit.Id()
			if _, ok := commitInfo[oid]; !ok {
				commitInfo[oid] = CommitInfo{Side: i}
			} else {
				sharedCommitsCount++
			}
			return true
		})
		e.Exit(err)
		if sharedCommitsCount > 0 {
			log.Printf("did not change side of %s", plural(sharedCommitsCount, "shared commit"))
		}

		// Update commitInfo with lists of commit branches.
		iter, err := repo.NewReferenceIteratorGlob(remoteGlob)
		e.Exit(err)
		for {
			ref, err := iter.Next()
			if git.IsErrorCode(err, git.ErrIterOver) {
				break
			}
			e.Exit(err)
			// Seed search with head commit.
			refCommit, err := repo.LookupCommit(ref.Target())
			e.Exit(err)
			roots = append(roots, refCommit)
			// Store commit branches.
			branchName, err := ref.Branch().Name()
			e.Exit(err)
			branchName = strings.TrimPrefix(branchName, name+"/")
			if mode == FinalMode && branchName != *flInterpose {
				// Add branches other than "master" for final merge.
				finalBranchInfo[branchName] = append(finalBranchInfo[branchName], BranchInfo{
					Side:   i,
					Commit: refCommit,
				})
				// Skip interposing.
				continue
			}
			walker.Reset()
			err = walker.Push(ref.Target())
			e.Exit(err)
			walker.SimplifyFirstParent()
			err = walker.Iterate(func(commit *git.Commit) bool {
				oid := *commit.Id()
				ci := commitInfo[oid]
				ci.Branches = append(ci.Branches, branchName)
				if mode == TotalMode && branchName == "master" {
					// Reduce risk of missing sibling trees
					// with our strategy that takes them
					// from the first parent by making
					// "master" the first parent.
					n := len(ci.Branches) - 1
					ci.Branches[0], ci.Branches[n] = ci.Branches[n], ci.Branches[0]
				}

				commitInfo[oid] = ci
				return true
			})
			e.Exit(err)
		}
	}

	log.Printf("composing %s (%s)", plural(nSides, "side"), plural(len(commitInfo), "commit"))
	tb, err := repo.TreeBuilder()
	e.Exit(err)
	emptyTreeOid, err := tb.Write()
	e.Exit(err)
	emptyTree, err := repo.LookupTree(emptyTreeOid)
	e.Exit(err)
	sig := &git.Signature{Name: "root", Email: "root"} // Deterministic signature.
	composedOid, err := repo.CreateCommit("", sig, sig, "", emptyTree, roots...)
	e.Exit(err)
	if *flVerbose {
		log.Println("virtual common head:", composedOid)
	}

	filterMapping := make(map[git.Oid]git.Oid) // Map old commits to new commits.
	newHeads := make(map[string]*git.Commit)   // Map new heads to new commits.

	var progress *uiprogress.Progress
	var bar *uiprogress.Bar
	if !*flVerbose {
		progress = uiprogress.New()
		progress.Out = os.Stderr
		bar = progress.AddBar(len(commitInfo))
		progress.Start()
	}

	// libgit2 chronological topological walker in fact is not chronological.
	// totalWalker, err := repo.Walk()
	totalWalker, err := NewReverseTopologicalDateOrderCommitWalker(repo)
	e.Exit(err)
	// totalWalker.Sorting(git.SortTopological | git.SortTime | git.SortReverse)
	totalWalker.Push(composedOid)
	err = totalWalker.Iterate(func(commit *git.Commit) bool {
		oid := *commit.Id()
		if oid == *composedOid {
			return true // Skip our composed oid (and immediately finish).
		}
		ci := commitInfo[oid]

		parents := []*git.Commit{}
		useParentsFrom := uint(0)
		if len(ci.Branches) > 0 {
			// Replace first parent (if any) with heads of relevant branches.
			useParentsFrom = 1
			usedHeads := map[git.Oid]bool{}
			for _, branch := range ci.Branches {
				head := newHeads[branch]
				if head != nil && !usedHeads[*head.Id()] {
					parents = append(parents, head)
					usedHeads[*head.Id()] = true
				}
			}
		}
		// Translate old parents.
		for i := useParentsFrom; i < commit.ParentCount(); i++ {
			oldParentOid := commit.Parent(uint(i)).Id()
			parentOid := filterMapping[*oldParentOid]
			newParent, err := repo.LookupCommit(&parentOid)
			if err != nil {
				e.Exit(fmt.Errorf("Error: %v parent %v in %q was not mapped yet", &oid, oldParentOid, sideNames[ci.Side]))
			}
			parents = append(parents, newParent)
		}
		// Update trees.
		var tb *git.TreeBuilder
		if len(parents) > 0 {
			parentTree, err := parents[0].Tree()
			e.Exit(err)
			tb, err = repo.TreeBuilderFromTree(parentTree)
			e.Exit(err)
		} else {
			tb, err = repo.TreeBuilder()
			e.Exit(err)
		}
		tree, err := commit.Tree()
		e.Exit(err)
		err = tb.Insert(sideNames[ci.Side], tree.Id(), int(git.FilemodeTree))
		e.Exit(err)
		newTreeID, err := tb.Write()
		e.Exit(err)
		newTree, err := repo.LookupTree(newTreeID)
		e.Exit(err)
		// Commit.
		newOid, err := repo.CreateCommit("", commit.Author(), commit.Committer(), commit.Message(), newTree, parents...)
		e.Exit(err)
		filterMapping[oid] = *newOid
		// Update cached heads.
		newCommit, err := repo.LookupCommit(newOid)
		e.Exit(err)
		for _, branch := range ci.Branches {
			newHeads[branch] = newCommit
		}

		if bar != nil {
			bar.Incr()
		}
		if *flVerbose {
			fmt.Println(commit.Committer().When, &oid, ci.Side, ci.Branches, "→", newOid, len(parents))
		}

		return true
	})
	e.Exit(err)
	if progress != nil {
		progress.Stop()
	}

	// Create finally-merged branches.
	sig, err = repo.DefaultSignature()
	e.Exit(err)
	for branchName, branchInfos := range finalBranchInfo {
		nParents := len(branchInfos)
		var commitID *git.Oid
		if nParents == 1 {
			oldOid := filterMapping[*branchInfos[0].Commit.Id()]
			commit, err := repo.LookupCommit(&oldOid)
			e.Exit(err)
			commitID = commit.Id()
		} else {
			parents := make([]*git.Commit, nParents)
			parentNames := make([]string, nParents)
			tb, err := repo.TreeBuilder()
			e.Exit(err)
			for k, branchInfo := range branchInfos {
				parentOid := filterMapping[*branchInfo.Commit.Id()]
				parentCommit, err := repo.LookupCommit(&parentOid)
				e.Exit(err)
				parents[k] = parentCommit
				name := sideNames[branchInfo.Side]
				parentNames[k] = name
				oldTree, err := branchInfo.Commit.Tree()
				e.Exit(err)
				err = tb.Insert(name, oldTree.Id(), int(git.FilemodeTree))
				e.Exit(err)
			}
			newTreeID, err := tb.Write()
			e.Exit(err)
			newTree, err := repo.LookupTree(newTreeID)
			e.Exit(err)
			message := fmt.Sprintf("Compose branch '%s' from %s", branchName, strings.Join(parentNames, ", "))
			commitID, err = repo.CreateCommit("", sig, sig, message, newTree, parents...)
			e.Exit(err)
		}
		_, err = repo.References.Create("refs/heads/"+branchName, commitID, true, "")
		e.Exit(err)
	}
	// Create heads for interposed branches.
	for headName, commit := range newHeads {
		_, err = repo.References.Create("refs/heads/"+headName, commit.Id(), true, "")
		e.Exit(err)
	}
	// Create tags.
	rewrittenTagsCount := 0
	for {
		// This loop supports rewriting tags of tags...
		rewrittenTags := false
		for _, tagName := range relevantTagList {
			ref, err := repo.References.Lookup("refs/tags/" + tagName)
			e.Exit(err)
			oldOid := *ref.Target()
			if newOid, ok := filterMapping[oldOid]; ok {
				// Lightweight tag pointing to a rewritten commit.
				commit, err := repo.LookupCommit(&newOid)
				e.Exit(err)
				_, err = repo.Tags.CreateLightweight(tagName, commit, true)
				e.Exit(err)
				rewrittenTags = true
				rewrittenTagsCount++
				if *flVerbose {
					fmt.Println("rewritten lightweight tag", tagName, &oldOid, "→", &newOid)
				}
			}
			tag, err := repo.LookupTag(&oldOid)
			if err == nil {
				// Annotated tag.
				oldOid = *tag.Target().Id()
				if newOid, ok := filterMapping[oldOid]; ok {
					commit, err := repo.LookupCommit(&newOid)
					e.Exit(err)
					// Currently git2go does not support overwriting annotated tags.
					_, err = repo.Tags.Create(tagName, commit, tag.Tagger(), tag.Message(), true)
					e.Exit(err)
					rewrittenTags = true
					rewrittenTagsCount++
					if *flVerbose {
						fmt.Println("rewritten annotated tag", tagName, &oldOid, "→", &newOid)
					}
				}
			}
		}
		if !rewrittenTags {
			break
		}
	}
	log.Printf("merged %s, interposed %s, rewritten %s",
		plural(len(finalBranchInfo), "branch"),
		plural(len(newHeads), "branch"),
		plural(rewrittenTagsCount, "tag"))
}
Example #6
0
func setStyle(widget IGetStyleContext, class string) {
	styleCtx, err := widget.GetStyleContext()
	e.Exit(err)
	styleCtx.AddProvider(cssProvider, gtk.STYLE_PROVIDER_PRIORITY_USER+1)
	styleCtx.AddClass(class)
}