This is an automated email from the ASF dual-hosted git repository.
klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/devlake.git
The following commit(s) were added to refs/heads/main by this push:
new 71dd14f75 fix(gitextractor): disable git auto gc/maintenance while
cloning (#9218)
71dd14f75 is described below
commit 71dd14f75cd5e761a4d7a305a1d4ae7d7f137c00
Author: Dirk <[email protected]>
AuthorDate: Sun Oct 18 04:50:40 2026 +0200
fix(gitextractor): disable git auto gc/maintenance while cloning (#9218)
git fetch ends with `git maintenance run --auto` (older git: `git gc
--auto`), which runs detached by default. During an incremental
collection the last fetch can leave a background repack running while
libgit2 already scans objects/pack/, so a temporary
.tmp-<pid>-pack-*.idx disappears under it and Collect Commits fails
intermittently with "could not find ... to open".
Pass -c gc.auto=0 -c gc.autoDetach=false -c maintenance.auto=false to
every git command run by GitcliCloner. The clone is a temporary
directory, so automatic housekeeping has no benefit there; the
explicit `git repack -d` in deepen() still runs synchronously.
Add a regression test that traces an incremental clone with GIT_TRACE
and asserts that git starts neither `maintenance run` nor `gc --auto`.
Signed-off-by: DoDiODev <[email protected]>
---
.../plugins/gitextractor/parser/clone_gitcli.go | 8 ++++++
.../gitextractor/parser/clone_gitcli_test.go | 29 ++++++++++++++++++++++
2 files changed, 37 insertions(+)
diff --git a/backend/plugins/gitextractor/parser/clone_gitcli.go
b/backend/plugins/gitextractor/parser/clone_gitcli.go
index f61dbc10d..82fc9d57a 100644
--- a/backend/plugins/gitextractor/parser/clone_gitcli.go
+++ b/backend/plugins/gitextractor/parser/clone_gitcli.go
@@ -94,6 +94,14 @@ func NewGitcliCloner(ctx plugin.SubTaskContext, localDir
string) (*GitcliCloner,
func (g *GitcliCloner) prepareSync() errors.Error {
taskData := g.taskData
+ // Disable automatic (detached) gc/maintenance after clone/fetch.
Otherwise git may
+ // repack the repo in the background while libgit2 is already reading
it, which
+ // makes temporary pack files (.tmp-<pid>-pack-*.idx) vanish and the
collection fail.
+ g.globalConfigArgs = append(g.globalConfigArgs,
+ "-c", "gc.auto=0",
+ "-c", "gc.autoDetach=false",
+ "-c", "maintenance.auto=false",
+ )
if *taskData.Options.SkipCommitStat {
g.syncArgs = append(g.syncArgs, "--filter=blob:none")
}
diff --git a/backend/plugins/gitextractor/parser/clone_gitcli_test.go
b/backend/plugins/gitextractor/parser/clone_gitcli_test.go
index 49b6ea32d..790980f64 100644
--- a/backend/plugins/gitextractor/parser/clone_gitcli_test.go
+++ b/backend/plugins/gitextractor/parser/clone_gitcli_test.go
@@ -126,6 +126,35 @@ func TestIncrementalCloneKeepsCommitsBehindMergeBoundary(t
*testing.T) {
assert.True(t, newest.Before(*cloner.since), "shallow boundary %s
should be older than since", newest)
}
+// git fetch may start `git maintenance run --auto` / `git gc --auto` in the
+// background. That repacks the repo while libgit2 is already reading it, so
+// temporary pack files (.tmp-<pid>-pack-*.idx) vanish under its feet and the
+// collection fails with "could not find ... to open".
+func TestCloneDoesNotStartAutoMaintenance(t *testing.T) {
+ if _, err := exec.LookPath("git"); err != nil {
+ t.Skip("git is not installed")
+ }
+ origin, _ := buildMergeBoundaryRepo(t)
+ cloner := newTestCloner(t, origin, time.Date(2024, 1, 2, 12, 0, 0, 0,
time.UTC))
+ skipCommitStat := false
+ cloner.taskData.Options.SkipCommitStat = &skipCommitStat
+ require.NoError(t, cloner.prepareSync())
+ traceFile := filepath.Join(t.TempDir(), "git-trace.log")
+ t.Setenv("GIT_TRACE", traceFile)
+
+ require.NoError(t, cloner.CloneRepo())
+
+ trace, e := os.ReadFile(traceFile)
+ require.NoError(t, e)
+ require.Contains(t, string(trace), "fetch", "the incremental clone
should run git fetch")
+ for _, line := range strings.Split(string(trace), "\n") {
+ if strings.Contains(line, "run_command:") {
+ assert.NotContains(t, line, "maintenance run", "git
must not start automatic maintenance")
+ assert.NotContains(t, line, "gc --auto", "git must not
start automatic gc")
+ }
+ }
+}
+
func TestNewestShallowCommitTimeIgnoresMissingCommits(t *testing.T) {
if _, err := exec.LookPath("git"); err != nil {
t.Skip("git is not installed")