Skip to content

Commit f9abf94

Browse files
authored
HasPreviousCommit causes recursive load of commits unnecessarily (#14598)
This PR improves HasPreviousCommit to prevent the automatic and recursive loading of previous commits using git merge-base --is-ancestor and git rev-list Fix #13684 Signed-off-by: Andrew Thornton <[email protected]>
1 parent c0c59a4 commit f9abf94

File tree

2 files changed

+50
-13
lines changed

2 files changed

+50
-13
lines changed

modules/git/commit.go

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"bufio"
1010
"bytes"
1111
"container/list"
12+
"errors"
1213
"fmt"
1314
"image"
1415
"image/color"
@@ -17,6 +18,7 @@ import (
1718
_ "image/png" // for processing png images
1819
"io"
1920
"net/http"
21+
"os/exec"
2022
"strconv"
2123
"strings"
2224
)
@@ -264,23 +266,33 @@ func (c *Commit) CommitsBefore() (*list.List, error) {
264266

265267
// HasPreviousCommit returns true if a given commitHash is contained in commit's parents
266268
func (c *Commit) HasPreviousCommit(commitHash SHA1) (bool, error) {
267-
for i := 0; i < c.ParentCount(); i++ {
268-
commit, err := c.Parent(i)
269-
if err != nil {
270-
return false, err
271-
}
272-
if commit.ID == commitHash {
269+
this := c.ID.String()
270+
that := commitHash.String()
271+
272+
if this == that {
273+
return false, nil
274+
}
275+
276+
if err := CheckGitVersionAtLeast("1.8"); err == nil {
277+
_, err := NewCommand("merge-base", "--is-ancestor", that, this).RunInDir(c.repo.Path)
278+
if err == nil {
273279
return true, nil
274280
}
275-
commitInParentCommit, err := commit.HasPreviousCommit(commitHash)
276-
if err != nil {
277-
return false, err
278-
}
279-
if commitInParentCommit {
280-
return true, nil
281+
var exitError *exec.ExitError
282+
if errors.As(err, &exitError) {
283+
if exitError.ProcessState.ExitCode() == 1 && len(exitError.Stderr) == 0 {
284+
return false, nil
285+
}
281286
}
287+
return false, err
288+
}
289+
290+
result, err := NewCommand("rev-list", "--ancestry-path", "-n1", that+".."+this, "--").RunInDir(c.repo.Path)
291+
if err != nil {
292+
return false, err
282293
}
283-
return false, nil
294+
295+
return len(strings.TrimSpace(result)) > 0, nil
284296
}
285297

286298
// CommitsBeforeLimit returns num commits before current revision

modules/git/commit_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,28 @@ empty commit`, commitFromReader.Signature.Payload)
105105
commitFromReader.Signature.Payload += "\n\n"
106106
assert.EqualValues(t, commitFromReader, commitFromReader2)
107107
}
108+
109+
func TestHasPreviousCommit(t *testing.T) {
110+
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
111+
112+
repo, err := OpenRepository(bareRepo1Path)
113+
assert.NoError(t, err)
114+
115+
commit, err := repo.GetCommit("8006ff9adbf0cb94da7dad9e537e53817f9fa5c0")
116+
assert.NoError(t, err)
117+
118+
parentSHA := MustIDFromString("8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2")
119+
notParentSHA := MustIDFromString("2839944139e0de9737a044f78b0e4b40d989a9e3")
120+
121+
haz, err := commit.HasPreviousCommit(parentSHA)
122+
assert.NoError(t, err)
123+
assert.True(t, haz)
124+
125+
hazNot, err := commit.HasPreviousCommit(notParentSHA)
126+
assert.NoError(t, err)
127+
assert.False(t, hazNot)
128+
129+
selfNot, err := commit.HasPreviousCommit(commit.ID)
130+
assert.NoError(t, err)
131+
assert.False(t, selfNot)
132+
}

0 commit comments

Comments
 (0)