Procházet zdrojové kódy

Fix branched pipeline construction in complex cases

Vadim Markovtsev před 6 roky
rodič
revize
e1fbb82bde
1 změnil soubory, kde provedl 54 přidání a 26 odebrání
  1. 54 26
      internal/core/forks.go

+ 54 - 26
internal/core/forks.go

@@ -8,7 +8,7 @@ import (
 	"gopkg.in/src-d/go-git.v4/plumbing/object"
 	"gopkg.in/src-d/go-git.v4/plumbing"
 	"gopkg.in/src-d/hercules.v4/internal/toposort"
-)
+	)
 
 // OneShotMergeProcessor provides the convenience method to consume merges only once.
 type OneShotMergeProcessor struct {
@@ -87,6 +87,8 @@ type runAction struct {
 	Items []int
 }
 
+type orderer = func(reverse, direction bool) []string
+
 func cloneItems(origin []PipelineItem, n int) [][]PipelineItem {
 	clones := make([][]PipelineItem, n)
 	for j := 0; j < n; j++ {
@@ -256,8 +258,8 @@ func leaveRootComponent(
 }
 
 // bindOrderNodes returns curried "orderNodes" function.
-func bindOrderNodes(mergedDag map[plumbing.Hash][]*object.Commit) func(reverse bool) []string {
-	return func(reverse bool) []string {
+func bindOrderNodes(mergedDag map[plumbing.Hash][]*object.Commit) orderer {
+	return func(reverse, direction bool) []string {
 		graph := toposort.NewGraph()
 		keys := make([]plumbing.Hash, 0, len(mergedDag))
 		for key := range mergedDag {
@@ -273,7 +275,11 @@ func bindOrderNodes(mergedDag map[plumbing.Hash][]*object.Commit) func(reverse b
 				return children[i].Hash.String() < children[j].Hash.String()
 			})
 			for _, c := range children {
-				graph.AddEdge(key.String(), c.Hash.String())
+				if !direction {
+					graph.AddEdge(key.String(), c.Hash.String())
+				} else {
+					graph.AddEdge(c.Hash.String(), key.String())
+				}
 			}
 		}
 		order, ok := graph.Toposort()
@@ -281,7 +287,7 @@ func bindOrderNodes(mergedDag map[plumbing.Hash][]*object.Commit) func(reverse b
 			// should never happen
 			panic("Could not topologically sort the DAG of commits")
 		}
-		if reverse {
+		if reverse != direction {
 			// one day this must appear in the standard library...
 			for i, j := 0, len(order)-1; i < len(order)/2; i, j = i+1, j-1 {
 				order[i], order[j] = order[j], order[i]
@@ -349,11 +355,10 @@ func mergeDag(
 
 // collapseFastForwards removes the fast forward merges.
 func collapseFastForwards(
-	orderNodes func(reverse bool) []string,
-	hashes map[string]*object.Commit,
+	orderNodes orderer, hashes map[string]*object.Commit,
 	mergedDag, dag, mergedSeq map[plumbing.Hash][]*object.Commit)  {
 
-	for _, strkey := range orderNodes(true) {
+	for _, strkey := range orderNodes(true, false) {
 		key := hashes[strkey].Hash
 		vals, exists := mergedDag[key]
 		if !exists {
@@ -389,15 +394,15 @@ func collapseFastForwards(
 
 // generatePlan creates the list of actions from the commit DAG.
 func generatePlan(
-	orderNodes func(reverse bool) []string,
-	numParents func(c *object.Commit) int,
+	orderNodes orderer, numParents func(c *object.Commit) int,
 	hashes map[string]*object.Commit,
 	mergedDag, dag, mergedSeq map[plumbing.Hash][]*object.Commit) []runAction {
 
 	var plan []runAction
 	branches := map[plumbing.Hash]int{}
+	branchers := map[plumbing.Hash]map[plumbing.Hash]int{}
 	counter := 1
-	for seqIndex, name := range orderNodes(false) {
+	for seqIndex, name := range orderNodes(false, true) {
 		commit := hashes[name]
 		if seqIndex == 0 {
 			branches[commit.Hash] = 0
@@ -417,6 +422,7 @@ func generatePlan(
 				Commit: c,
 				Items: []int{branch},
 			})
+
 		}
 		appendMergeIfNeeded := func() {
 			if numParents(commit) < 2 {
@@ -426,22 +432,31 @@ func generatePlan(
 			var items []int
 			minBranch := 1 << 31
 			for _, parent := range commit.ParentHashes {
-				if _, exists := hashes[parent.String()]; exists {
-					parentBranch := branches[parent]
-					if len(dag[parent]) == 1 && minBranch > parentBranch {
-						minBranch = parentBranch
-					}
-					items = append(items, parentBranch)
-					if parentBranch != branch {
-						appendCommit(commit, parentBranch)
+				if _, exists := hashes[parent.String()]; !exists {
+					continue
+				}
+				parentBranch := -1
+				if parents, exists := branchers[commit.Hash]; exists {
+					if inheritedBranch, exists := parents[parent]; exists {
+						parentBranch = inheritedBranch
 					}
 				}
+				if parentBranch == -1 {
+					parentBranch = branches[parent]
+				}
+				if len(dag[parent]) == 1 && minBranch > parentBranch {
+					minBranch = parentBranch
+				}
+				items = append(items, parentBranch)
+				if parentBranch != branch {
+					appendCommit(commit, parentBranch)
+				}
 			}
 			if minBranch < 1 << 31 {
 				branch = minBranch
 				branches[commit.Hash] = minBranch
 			} else if !branchExists() {
-				panic("!branchExists()")
+				log.Panicf("!branchExists(%s)", commit.Hash.String())
 			}
 			plan = append(plan, runAction{
 				Action: runActionMerge,
@@ -449,6 +464,7 @@ func generatePlan(
 				Items: items,
 			})
 		}
+		var head plumbing.Hash
 		if subseq, exists := mergedSeq[commit.Hash]; exists {
 			for subseqIndex, offspring := range subseq {
 				if branchExists() {
@@ -458,22 +474,34 @@ func generatePlan(
 					appendMergeIfNeeded()
 				}
 			}
-			branches[subseq[len(subseq)-1].Hash] = branch
+			head = subseq[len(subseq)-1].Hash
+			branches[head] = branch
+		} else {
+			head = commit.Hash
 		}
 		if len(mergedDag[commit.Hash]) > 1 {
-			branches[mergedDag[commit.Hash][0].Hash] = branch
 			children := []int{branch}
 			for i, child := range mergedDag[commit.Hash] {
-				if i > 0 {
+				if i == 0 {
+					branches[child.Hash] = branch
+					continue
+				}
+				if _, exists := branches[child.Hash]; !exists {
 					branches[child.Hash] = counter
-					children = append(children, counter)
-					counter++
 				}
+				parents := branchers[child.Hash]
+				if parents == nil {
+					parents = map[plumbing.Hash]int{}
+					branchers[child.Hash] = parents
+				}
+				parents[head] = counter
+				children = append(children, counter)
+				counter++
 			}
 			plan = append(plan, runAction{
 				Action: runActionFork,
 				Commit: nil,
-				Items: children,
+				Items:  children,
 			})
 		}
 	}