Browse Source

Add runActionDelete planning

Vadim Markovtsev 6 years ago
parent
commit
0b7101095a
2 changed files with 111 additions and 57 deletions
  1. 75 38
      internal/core/pipeline.go
  2. 36 19
      internal/core/pipeline_test.go

+ 75 - 38
internal/core/pipeline.go

@@ -619,6 +619,7 @@ const (
 	runActionCommit = 0
 	runActionFork = iota
 	runActionMerge = iota
+	runActionDelete = iota
 )
 
 type runAction struct {
@@ -982,7 +983,7 @@ func generatePlan(
 	return plan
 }
 
-// optimizePlan removes "dead" nodes.
+// optimizePlan removes "dead" nodes and inserts `runActionDelete` disposal steps.
 //
 // |   *
 // *  /
@@ -991,56 +992,92 @@ func generatePlan(
 // *
 //
 func optimizePlan(plan []runAction) []runAction {
-	lives := map[int][]int{}
+	// lives maps branch index to the number of commits in that branch
+	lives := map[int]int{}
+	// lastMentioned maps branch index to the index inside `plan` when that branch was last used
+	lastMentioned := map[int]int{}
 	for i, p := range plan {
-		if p.Action == runActionCommit {
-			lives[p.Items[0]] = append(lives[p.Items[0]], i)
+		firstItem := p.Items[0]
+		switch p.Action {
+		case runActionCommit:
+			lives[firstItem]++
+			lastMentioned[firstItem] = i
+		case runActionFork:
+			lastMentioned[firstItem] = i
+		case runActionMerge:
+			for _, item := range p.Items {
+				lastMentioned[item] = i
+			}
 		}
 	}
 	branchesToDelete := map[int]bool{}
 	for key, life := range lives {
-		if len(life) == 1 {
+		if life == 1 {
 			branchesToDelete[key] = true
+			delete(lastMentioned, key)
 		}
 	}
-	if len(branchesToDelete) == 0 {
+	var optimizedPlan []runAction
+	lastMentionedArr := make([][2]int, 0, len(lastMentioned) + 1)
+	for key, val := range lastMentioned {
+		if val != len(plan) - 1 {
+			lastMentionedArr = append(lastMentionedArr, [2]int{val, key})
+		}
+	}
+	if len(lastMentionedArr) == 0 && len(branchesToDelete) == 0 {
+		// early return - we have nothing to optimize
 		return plan
 	}
-	var optimizedPlan []runAction
-	for _, p := range plan {
-		switch p.Action {
-		case runActionCommit:
-			if !branchesToDelete[p.Items[0]] {
-				optimizedPlan = append(optimizedPlan, p)
-			}
-		case runActionFork:
-			var newBranches []int
-			for _, b := range p.Items {
-				if !branchesToDelete[b] {
-					newBranches = append(newBranches, b)
+	sort.Slice(lastMentionedArr, func(i, j int) bool { 
+		return lastMentionedArr[i][0] < lastMentionedArr[j][0]
+	})
+	lastMentionedArr = append(lastMentionedArr, [2]int{len(plan)-1, -1})
+	prevpi := -1
+	for _, pair := range lastMentionedArr {
+		for pi := prevpi + 1; pi <= pair[0]; pi++ {
+			p := plan[pi]
+			switch p.Action {
+			case runActionCommit:
+				if !branchesToDelete[p.Items[0]] {
+					optimizedPlan = append(optimizedPlan, p)
 				}
-			}
-			if len(newBranches) > 1 {
-				optimizedPlan = append(optimizedPlan, runAction{
-					Action: runActionFork,
-					Commit: p.Commit,
-					Items:  newBranches,
-				})
-			}
-		case runActionMerge:
-			var newBranches []int
-			for _, b := range p.Items {
-				if !branchesToDelete[b] {
-					newBranches = append(newBranches, b)
+			case runActionFork:
+				var newBranches []int
+				for _, b := range p.Items {
+					if !branchesToDelete[b] {
+						newBranches = append(newBranches, b)
+					}
+				}
+				if len(newBranches) > 1 {
+					optimizedPlan = append(optimizedPlan, runAction{
+						Action: runActionFork,
+						Commit: p.Commit,
+						Items:  newBranches,
+					})
+				}
+			case runActionMerge:
+				var newBranches []int
+				for _, b := range p.Items {
+					if !branchesToDelete[b] {
+						newBranches = append(newBranches, b)
+					}
+				}
+				if len(newBranches) > 1 {
+					optimizedPlan = append(optimizedPlan, runAction{
+						Action: runActionMerge,
+						Commit: p.Commit,
+						Items:  newBranches,
+					})
 				}
 			}
-			if len(newBranches) > 1 {
-				optimizedPlan = append(optimizedPlan, runAction{
-					Action: runActionMerge,
-					Commit: p.Commit,
-					Items:  newBranches,
-				})
-			}
+		}
+		if pair[1] >= 0 {
+			prevpi = pair[0]
+			optimizedPlan = append(optimizedPlan, runAction{
+				Action: runActionDelete,
+				Commit: nil,
+				Items:  []int{pair[1]},
+			})
 		}
 	}
 	// single commit can be detected as redundant

+ 36 - 19
internal/core/pipeline_test.go

@@ -361,6 +361,19 @@ func TestConfigurationOptionFormatDefault(t *testing.T) {
 	assert.Equal(t, opt.FormatDefault(), "0.5")
 }
 
+func TestPrepareRunPlanTiny(t *testing.T) {
+	rootCommit, err := test.Repository.CommitObject(plumbing.NewHash(
+		"cce947b98a050c6d356bc6ba95030254914027b1"))
+	if err != nil {
+		t.Fatal(err)
+	}
+	plan := prepareRunPlan([]*object.Commit{rootCommit})
+	assert.Len(t, plan, 1)
+	assert.Equal(t, runActionCommit, plan[0].Action)
+	assert.Equal(t, 0, plan[0].Items[0])
+	assert.Equal(t, "cce947b98a050c6d356bc6ba95030254914027b1", plan[0].Commit.Hash.String())
+}
+
 func TestPrepareRunPlanSmall(t *testing.T) {
 	cit, err := test.Repository.Log(&git.LogOptions{From: plumbing.ZeroHash})
 	if err != nil {
@@ -400,25 +413,25 @@ func TestPrepareRunPlanSmall(t *testing.T) {
 }
 
 func TestPrepareRunPlanBig(t *testing.T) {
-	cases := [][6]int {
-		{2017, 8, 9, 0, 0, 0},
-		{2017, 8, 10, 0, 0, 0},
-		{2017, 8, 24, 1, 1, 1},
-		{2017, 9, 19, 1-2, 1, 1},
-		{2017, 9, 23, 1-2, 1, 1},
-		{2017, 12, 8, 1, 1, 1},
-		{2017, 12, 9, 1, 1, 1},
-		{2017, 12, 10, 1, 1, 1},
-		{2017, 12, 11, 2, 2, 2},
-		{2017, 12, 19, 4, 4, 4},
-		{2017, 12, 27, 4, 4, 4},
-		{2018, 1, 10, 4, 4, 4},
-		{2018, 1, 16, 4, 4, 4},
-		{2018, 1, 18, 7, 6, 7},
-		{2018, 1, 23, 8, 6, 8},
-		{2018, 3, 12, 9, 7, 9},
-		{2018, 5, 13, 9, 7, 9},
-		{2018, 5, 16, 13, 9, 13},
+	cases := [][7]int {
+		{2017, 8, 9, 0, 0, 0, 0},
+		{2017, 8, 10, 0, 0, 0, 0},
+		{2017, 8, 24, 1, 1, 1, 1},
+		{2017, 9, 19, 1-2, 1, 1, 1},
+		{2017, 9, 23, 1-2, 1, 1, 1},
+		{2017, 12, 8, 1, 1, 1, 1},
+		{2017, 12, 9, 1, 1, 1, 1},
+		{2017, 12, 10, 1, 1, 1, 1},
+		{2017, 12, 11, 2, 2, 2, 2},
+		{2017, 12, 19, 4, 4, 4, 4},
+		{2017, 12, 27, 4, 4, 4, 4},
+		{2018, 1, 10, 4, 4, 4, 4},
+		{2018, 1, 16, 4, 4, 4, 4},
+		{2018, 1, 18, 7, 6, 7, 7},
+		{2018, 1, 23, 8, 6, 8, 8},
+		{2018, 3, 12, 9, 7, 9, 9},
+		{2018, 5, 13, 9, 7, 9, 9},
+		{2018, 5, 16, 13, 9, 13, 13},
 	}
 	for _, testCase := range cases {
 		cit, err := test.Repository.Log(&git.LogOptions{From: plumbing.ZeroHash})
@@ -449,6 +462,7 @@ func TestPrepareRunPlanBig(t *testing.T) {
 		numCommits := 0
 		numForks := 0
 		numMerges := 0
+		numDeletes := 0
 		for _, p := range plan {
 			switch p.Action {
 			case runActionCommit:
@@ -457,10 +471,13 @@ func TestPrepareRunPlanBig(t *testing.T) {
 				numForks++
 			case runActionMerge:
 				numMerges++
+			case runActionDelete:
+				numDeletes++
 			}
 		}
 		assert.Equal(t, numCommits, len(commits)+testCase[3], fmt.Sprintf("commits %v", testCase))
 		assert.Equal(t, numForks, testCase[4], fmt.Sprintf("forks %v", testCase))
 		assert.Equal(t, numMerges, testCase[5], fmt.Sprintf("merges %v", testCase))
+		assert.Equal(t, numMerges, testCase[6], fmt.Sprintf("deletes %v", testCase))
 	}
 }