Explorar o código

Add --first-parent

Signed-off-by: Vadim Markovtsev <vadim@sourced.tech>
Vadim Markovtsev %!s(int64=6) %!d(string=hai) anos
pai
achega
141fb04211
Modificáronse 4 ficheiros con 59 adicións e 16 borrados
  1. 6 3
      cmd/hercules/root.go
  2. 1 1
      doc.go
  3. 32 10
      internal/core/pipeline.go
  4. 20 2
      internal/core/pipeline_test.go

+ 6 - 3
cmd/hercules/root.go

@@ -151,6 +151,7 @@ targets can be added using the --plugin system.`,
 	Args: cobra.RangeArgs(1, 2),
 	Args: cobra.RangeArgs(1, 2),
 	Run: func(cmd *cobra.Command, args []string) {
 	Run: func(cmd *cobra.Command, args []string) {
 		flags := cmd.Flags()
 		flags := cmd.Flags()
+		firstParent, _ := flags.GetBool("first-parent")
 		commitsFile, _ := flags.GetString("commits")
 		commitsFile, _ := flags.GetString("commits")
 		protobuf, _ := flags.GetBool("pb")
 		protobuf, _ := flags.GetBool("pb")
 		profile, _ := flags.GetBool("profile")
 		profile, _ := flags.GetBool("profile")
@@ -199,7 +200,7 @@ targets can be added using the --plugin system.`,
 		var err error
 		var err error
 		if commitsFile == "" {
 		if commitsFile == "" {
 			fmt.Fprint(os.Stderr, "git log...\r")
 			fmt.Fprint(os.Stderr, "git log...\r")
-			commits, err = pipeline.Commits()
+			commits, err = pipeline.Commits(firstParent)
 		} else {
 		} else {
 			commits, err = hercules.LoadCommitsFromFile(commitsFile, repository)
 			commits, err = hercules.LoadCommitsFromFile(commitsFile, repository)
 		}
 		}
@@ -389,10 +390,12 @@ func init() {
 	rootCmd.MarkFlagFilename("plugin")
 	rootCmd.MarkFlagFilename("plugin")
 	rootFlags := rootCmd.Flags()
 	rootFlags := rootCmd.Flags()
 	rootFlags.String("commits", "", "Path to the text file with the "+
 	rootFlags.String("commits", "", "Path to the text file with the "+
-		"commit history to follow instead of the default rev-list "+
-		"--first-parent. The format is the list of hashes, each hash on a "+
+		"commit history to follow instead of the default `git log`. " +
+		"The format is the list of hashes, each hash on a "+
 		"separate line. The first hash is the root.")
 		"separate line. The first hash is the root.")
 	rootCmd.MarkFlagFilename("commits")
 	rootCmd.MarkFlagFilename("commits")
+	rootFlags.Bool("first-parent", false, "Follow only the first parent in the commit history - " +
+		"\"git log --first-parent\".")
 	rootFlags.Bool("pb", false, "The output format will be Protocol Buffers instead of YAML.")
 	rootFlags.Bool("pb", false, "The output format will be Protocol Buffers instead of YAML.")
 	rootFlags.Bool("quiet", !terminal.IsTerminal(int(os.Stdin.Fd())),
 	rootFlags.Bool("quiet", !terminal.IsTerminal(int(os.Stdin.Fd())),
 		"Do not print status updates to stderr.")
 		"Do not print status updates to stderr.")

+ 1 - 1
doc.go

@@ -29,7 +29,7 @@ Then add the required analysis:
 This call will add all the needed intermediate pipeline items. Then link and execute the analysis tree:
 This call will add all the needed intermediate pipeline items. Then link and execute the analysis tree:
 
 
   pipeline.Initialize(nil)
   pipeline.Initialize(nil)
-  result, err := pipeline.Run(pipeline.Commits())
+  result, err := pipeline.Run(pipeline.Commits(false))
 
 
 Finally extract the result:
 Finally extract the result:
 
 

+ 32 - 10
internal/core/pipeline.go

@@ -364,15 +364,18 @@ func (pipeline *Pipeline) Len() int {
 }
 }
 
 
 // Commits returns the list of commits from the history similar to `git log` over the HEAD.
 // Commits returns the list of commits from the history similar to `git log` over the HEAD.
-func (pipeline *Pipeline) Commits() ([]*object.Commit, error) {
-	cit, err := pipeline.repository.Log(&git.LogOptions{From: plumbing.ZeroHash})
+// `firstParent` specifies whether to leave only the first parent after each merge
+// (`git log --first-parent`) - effectively decreasing the accuracy but increasing performance.
+func (pipeline *Pipeline) Commits(firstParent bool) ([]*object.Commit, error) {
+	var result []*object.Commit
+	repository := pipeline.repository
+	head, err := repository.Head()
 	if err != nil {
 	if err != nil {
 		if err == plumbing.ErrReferenceNotFound {
 		if err == plumbing.ErrReferenceNotFound {
-			refs, errr := pipeline.repository.References()
+			refs, errr := repository.References()
 			if errr != nil {
 			if errr != nil {
 				return nil, errors.Wrap(errr, "unable to list the references")
 				return nil, errors.Wrap(errr, "unable to list the references")
 			}
 			}
-			var head *plumbing.Reference
 			refs.ForEach(func(ref *plumbing.Reference) error {
 			refs.ForEach(func(ref *plumbing.Reference) error {
 				if strings.HasPrefix(ref.Name().String(), "refs/heads/HEAD/") {
 				if strings.HasPrefix(ref.Name().String(), "refs/heads/HEAD/") {
 					head = ref
 					head = ref
@@ -380,16 +383,35 @@ func (pipeline *Pipeline) Commits() ([]*object.Commit, error) {
 				}
 				}
 				return nil
 				return nil
 			})
 			})
-			if head != nil {
-				cit, err = pipeline.repository.Log(&git.LogOptions{From: head.Hash()})
-			}
 		}
 		}
-		if err != nil {
+		if head == nil && err != nil {
 			return nil, errors.Wrap(err, "unable to collect the commit history")
 			return nil, errors.Wrap(err, "unable to collect the commit history")
 		}
 		}
 	}
 	}
+
+	if firstParent {
+		commit, err := repository.CommitObject(head.Hash())
+		if err != nil {
+			panic(err)
+		}
+		// the first parent matches the head
+		for ; err != io.EOF; commit, err = commit.Parents().Next() {
+			if err != nil {
+				panic(err)
+			}
+			result = append(result, commit)
+		}
+		// reverse the order
+		for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 {
+			result[i], result[j] = result[j], result[i]
+		}
+		return result, nil
+	}
+	cit, err := repository.Log(&git.LogOptions{From: head.Hash()})
+	if err != nil {
+		return nil, errors.Wrap(err, "unable to collect the commit history")
+	}
 	defer cit.Close()
 	defer cit.Close()
-	var result []*object.Commit
 	cit.ForEach(func(commit *object.Commit) error {
 	cit.ForEach(func(commit *object.Commit) error {
 		result = append(result, commit)
 		result = append(result, commit)
 		return nil
 		return nil
@@ -545,7 +567,7 @@ func (pipeline *Pipeline) Initialize(facts map[string]interface{}) {
 	}
 	}
 	if _, exists := facts[ConfigPipelineCommits]; !exists {
 	if _, exists := facts[ConfigPipelineCommits]; !exists {
 		var err error
 		var err error
-		facts[ConfigPipelineCommits], err = pipeline.Commits()
+		facts[ConfigPipelineCommits], err = pipeline.Commits(false)
 		if err != nil {
 		if err != nil {
 			log.Panicf("failed to list the commits: %v", err)
 			log.Panicf("failed to list the commits: %v", err)
 		}
 		}

+ 20 - 2
internal/core/pipeline_test.go

@@ -285,9 +285,9 @@ func TestPipelineOnProgress(t *testing.T) {
 	assert.Equal(t, 4, progressOk)
 	assert.Equal(t, 4, progressOk)
 }
 }
 
 
-func TestPipelineCommits(t *testing.T) {
+func TestPipelineCommitsFull(t *testing.T) {
 	pipeline := NewPipeline(test.Repository)
 	pipeline := NewPipeline(test.Repository)
-	commits, err := pipeline.Commits()
+	commits, err := pipeline.Commits(false)
 	assert.Nil(t, err)
 	assert.Nil(t, err)
 	assert.True(t, len(commits) >= 100)
 	assert.True(t, len(commits) >= 100)
 	hashMap := map[plumbing.Hash]bool{}
 	hashMap := map[plumbing.Hash]bool{}
@@ -297,6 +297,24 @@ func TestPipelineCommits(t *testing.T) {
 	assert.Equal(t, len(commits), len(hashMap))
 	assert.Equal(t, len(commits), len(hashMap))
 	assert.Contains(t, hashMap, plumbing.NewHash(
 	assert.Contains(t, hashMap, plumbing.NewHash(
 		"cce947b98a050c6d356bc6ba95030254914027b1"))
 		"cce947b98a050c6d356bc6ba95030254914027b1"))
+	assert.Contains(t, hashMap, plumbing.NewHash(
+		"a3ee37f91f0d705ec9c41ae88426f0ae44b2fbc3"))
+}
+
+func TestPipelineCommitsFirstParent(t *testing.T) {
+	pipeline := NewPipeline(test.Repository)
+	commits, err := pipeline.Commits(true)
+	assert.Nil(t, err)
+	assert.True(t, len(commits) >= 100)
+	hashMap := map[plumbing.Hash]bool{}
+	for _, c := range commits {
+		hashMap[c.Hash] = true
+	}
+	assert.Equal(t, len(commits), len(hashMap))
+	assert.Contains(t, hashMap, plumbing.NewHash(
+		"cce947b98a050c6d356bc6ba95030254914027b1"))
+	assert.NotContains(t, hashMap, plumbing.NewHash(
+		"a3ee37f91f0d705ec9c41ae88426f0ae44b2fbc3"))
 }
 }
 
 
 func TestLoadCommitsFromFile(t *testing.T) {
 func TestLoadCommitsFromFile(t *testing.T) {