|
@@ -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 {
|
|
@@ -77,6 +77,8 @@ const (
|
|
|
runActionFork = iota
|
|
|
// runActionMerge merges several branches together
|
|
|
runActionMerge = iota
|
|
|
+ // runActionEmerge starts a root branch
|
|
|
+ runActionEmerge = iota
|
|
|
// runActionDelete removes the branch as it is no longer needed
|
|
|
runActionDelete = iota
|
|
|
)
|
|
@@ -130,7 +132,6 @@ func getMasterBranch(branches map[int][]PipelineItem) []PipelineItem {
|
|
|
func prepareRunPlan(commits []*object.Commit) []runAction {
|
|
|
hashes, dag := buildDag(commits)
|
|
|
leaveRootComponent(hashes, dag)
|
|
|
- numParents := bindNumParents(hashes, dag)
|
|
|
mergedDag, mergedSeq := mergeDag(hashes, dag)
|
|
|
orderNodes := bindOrderNodes(mergedDag)
|
|
|
collapseFastForwards(orderNodes, hashes, mergedDag, dag, mergedSeq)
|
|
@@ -143,7 +144,7 @@ func prepareRunPlan(commits []*object.Commit) []runAction {
|
|
|
}
|
|
|
}
|
|
|
fmt.Printf("}\n")*/
|
|
|
- plan := generatePlan(orderNodes, numParents, hashes, mergedDag, dag, mergedSeq)
|
|
|
+ plan := generatePlan(orderNodes, hashes, mergedDag, dag, mergedSeq)
|
|
|
plan = optimizePlan(plan)
|
|
|
/*for _, p := range plan {
|
|
|
firstItem := p.Items[0]
|
|
@@ -186,26 +187,6 @@ func buildDag(commits []*object.Commit) (
|
|
|
return hashes, dag
|
|
|
}
|
|
|
|
|
|
-// bindNumParents returns curried "numParents" function.
|
|
|
-func bindNumParents(
|
|
|
- hashes map[string]*object.Commit,
|
|
|
- dag map[plumbing.Hash][]*object.Commit) func(c *object.Commit) int {
|
|
|
- return func(c *object.Commit) int {
|
|
|
- r := 0
|
|
|
- for _, parent := range c.ParentHashes {
|
|
|
- if p, exists := hashes[parent.String()]; exists {
|
|
|
- for _, pc := range dag[p.Hash] {
|
|
|
- if pc.Hash == c.Hash {
|
|
|
- r++
|
|
|
- break
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return r
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
// leaveRootComponent runs connected components analysis and throws away everything
|
|
|
// but the part which grows from the root.
|
|
|
func leaveRootComponent(
|
|
@@ -306,18 +287,24 @@ func bindOrderNodes(mergedDag map[plumbing.Hash][]*object.Commit) orderer {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// mergeDag turns sequences of consecutive commits into single nodes.
|
|
|
-func mergeDag(
|
|
|
- hashes map[string]*object.Commit,
|
|
|
- dag map[plumbing.Hash][]*object.Commit) (
|
|
|
- mergedDag, mergedSeq map[plumbing.Hash][]*object.Commit) {
|
|
|
-
|
|
|
+// inverts `dag`
|
|
|
+func buildParents(dag map[plumbing.Hash][]*object.Commit) map[plumbing.Hash][]plumbing.Hash {
|
|
|
parents := map[plumbing.Hash][]plumbing.Hash{}
|
|
|
for key, vals := range dag {
|
|
|
for _, val := range vals {
|
|
|
parents[val.Hash] = append(parents[val.Hash], key)
|
|
|
}
|
|
|
}
|
|
|
+ return parents
|
|
|
+}
|
|
|
+
|
|
|
+// mergeDag turns sequences of consecutive commits into single nodes.
|
|
|
+func mergeDag(
|
|
|
+ hashes map[string]*object.Commit,
|
|
|
+ dag map[plumbing.Hash][]*object.Commit) (
|
|
|
+ mergedDag, mergedSeq map[plumbing.Hash][]*object.Commit) {
|
|
|
+
|
|
|
+ parents := buildParents(dag)
|
|
|
mergedDag = map[plumbing.Hash][]*object.Commit{}
|
|
|
mergedSeq = map[plumbing.Hash][]*object.Commit{}
|
|
|
visited := map[plumbing.Hash]bool{}
|
|
@@ -357,12 +344,7 @@ func collapseFastForwards(
|
|
|
orderNodes orderer, hashes map[string]*object.Commit,
|
|
|
mergedDag, dag, mergedSeq map[plumbing.Hash][]*object.Commit) {
|
|
|
|
|
|
- parents := map[plumbing.Hash][]plumbing.Hash{}
|
|
|
- for key, vals := range mergedDag {
|
|
|
- for _, val := range vals {
|
|
|
- parents[val.Hash] = append(parents[val.Hash], key)
|
|
|
- }
|
|
|
- }
|
|
|
+ parents := buildParents(mergedDag)
|
|
|
processed := map[plumbing.Hash]bool{}
|
|
|
for _, strkey := range orderNodes(false, true) {
|
|
|
key := hashes[strkey].Hash
|
|
@@ -473,18 +455,24 @@ func collapseFastForwards(
|
|
|
|
|
|
// generatePlan creates the list of actions from the commit DAG.
|
|
|
func generatePlan(
|
|
|
- orderNodes orderer, numParents func(c *object.Commit) int,
|
|
|
- hashes map[string]*object.Commit,
|
|
|
+ orderNodes orderer, hashes map[string]*object.Commit,
|
|
|
mergedDag, dag, mergedSeq map[plumbing.Hash][]*object.Commit) []runAction {
|
|
|
|
|
|
+ parents := buildParents(dag)
|
|
|
var plan []runAction
|
|
|
branches := map[plumbing.Hash]int{}
|
|
|
branchers := map[plumbing.Hash]map[plumbing.Hash]int{}
|
|
|
- counter := 1
|
|
|
- for seqIndex, name := range orderNodes(false, true) {
|
|
|
+ counter := 0
|
|
|
+ for _, name := range orderNodes(false, true) {
|
|
|
commit := hashes[name]
|
|
|
- if seqIndex == 0 {
|
|
|
- branches[commit.Hash] = 0
|
|
|
+ if len(parents[commit.Hash]) == 0 {
|
|
|
+ branches[commit.Hash] = counter
|
|
|
+ plan = append(plan, runAction{
|
|
|
+ Action: runActionEmerge,
|
|
|
+ Commit: commit,
|
|
|
+ Items: []int{counter},
|
|
|
+ })
|
|
|
+ counter++
|
|
|
}
|
|
|
var branch int
|
|
|
{
|
|
@@ -504,16 +492,13 @@ func generatePlan(
|
|
|
|
|
|
}
|
|
|
appendMergeIfNeeded := func() {
|
|
|
- if numParents(commit) < 2 {
|
|
|
+ if len(parents[commit.Hash]) < 2 {
|
|
|
return
|
|
|
}
|
|
|
// merge after the merge commit (the first in the sequence)
|
|
|
var items []int
|
|
|
minBranch := 1 << 31
|
|
|
- for _, parent := range commit.ParentHashes {
|
|
|
- if _, exists := hashes[parent.String()]; !exists {
|
|
|
- continue
|
|
|
- }
|
|
|
+ for _, parent := range parents[commit.Hash] {
|
|
|
parentBranch := -1
|
|
|
if parents, exists := branchers[commit.Hash]; exists {
|
|
|
if inheritedBranch, exists := parents[parent]; exists {
|
|
@@ -522,6 +507,10 @@ func generatePlan(
|
|
|
}
|
|
|
if parentBranch == -1 {
|
|
|
parentBranch = branches[parent]
|
|
|
+ if parentBranch == -1 {
|
|
|
+ log.Panicf("parent %s > %s does not have a branch assigned",
|
|
|
+ parent.String(), commit.Hash.String())
|
|
|
+ }
|
|
|
}
|
|
|
if len(dag[parent]) == 1 && minBranch > parentBranch {
|
|
|
minBranch = parentBranch
|
|
@@ -531,6 +520,7 @@ func generatePlan(
|
|
|
appendCommit(commit, parentBranch)
|
|
|
}
|
|
|
}
|
|
|
+ // there should be no duplicates in items
|
|
|
if minBranch < 1 << 31 {
|
|
|
branch = minBranch
|
|
|
branches[commit.Hash] = minBranch
|
|
@@ -606,12 +596,19 @@ func optimizePlan(plan []runAction) []runAction {
|
|
|
case runActionCommit:
|
|
|
lives[firstItem]++
|
|
|
lastMentioned[firstItem] = i
|
|
|
+ if firstItem == -1 {
|
|
|
+ log.Panicf("commit %s does not have an assigned branch",
|
|
|
+ p.Commit.Hash.String())
|
|
|
+ }
|
|
|
case runActionFork:
|
|
|
lastMentioned[firstItem] = i
|
|
|
case runActionMerge:
|
|
|
for _, item := range p.Items {
|
|
|
lastMentioned[item] = i
|
|
|
}
|
|
|
+ case runActionEmerge:
|
|
|
+ lives[firstItem]++
|
|
|
+ lastMentioned[firstItem] = i
|
|
|
}
|
|
|
}
|
|
|
branchesToDelete := map[int]bool{}
|
|
@@ -673,6 +670,8 @@ func optimizePlan(plan []runAction) []runAction {
|
|
|
Items: newBranches,
|
|
|
})
|
|
|
}
|
|
|
+ case runActionEmerge:
|
|
|
+ optimizedPlan = append(optimizedPlan, p)
|
|
|
}
|
|
|
}
|
|
|
if pair[1] >= 0 {
|