From 470029181ca2fcf0289f1b89a7a62fd2e8f6fb4b Mon Sep 17 00:00:00 2001 From: Shady Rafehi Date: Tue, 4 Jun 2024 15:21:28 +1000 Subject: [PATCH] Skip fetch if revision to check out exists locally --- reposerver/repository/repository.go | 17 +++++++++++++---- util/git/client.go | 16 ++++++++++++++++ util/git/mocks/Client.go | 18 ++++++++++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/reposerver/repository/repository.go b/reposerver/repository/repository.go index ac738cd513d196..6a8c9817cba23e 100644 --- a/reposerver/repository/repository.go +++ b/reposerver/repository/repository.go @@ -2432,10 +2432,19 @@ func checkoutRevision(gitClient git.Client, revision string, submoduleEnabled bo return status.Errorf(codes.Internal, "Failed to initialize git repo: %v", err) } - // Fetching with no revision first. Fetching with an explicit version can cause repo bloat. https://github.com/argoproj/argo-cd/issues/8845 - err = gitClient.Fetch("") - if err != nil { - return status.Errorf(codes.Internal, "Failed to fetch default: %v", err) + revisionPresent := gitClient.IsRevisionPresent(revision) + + log.WithFields(map[string]interface{}{ + "skipFetch": revisionPresent, + }).Infof("Checking out revision %v", revision) + + // Fetching can be skipped if the revision is already present locally. + if !revisionPresent { + // Fetching with no revision first. Fetching with an explicit version can cause repo bloat. https://github.com/argoproj/argo-cd/issues/8845 + err = gitClient.Fetch("") + if err != nil { + return status.Errorf(codes.Internal, "Failed to fetch default: %v", err) + } } err = gitClient.Checkout(revision, submoduleEnabled) diff --git a/util/git/client.go b/util/git/client.go index b356f95639d432..dbdebed4b9a556 100644 --- a/util/git/client.go +++ b/util/git/client.go @@ -78,6 +78,7 @@ type Client interface { VerifyCommitSignature(string) (string, error) IsAnnotatedTag(string) bool ChangedFiles(revision string, targetRevision string) ([]string, error) + IsRevisionPresent(revision string) bool } type EventHandlers struct { @@ -357,6 +358,21 @@ func (m *nativeGitClient) fetch(revision string) error { return err } +// IsRevisionPresent checks to see if the given revision already exists locally. +func (m *nativeGitClient) IsRevisionPresent(revision string) bool { + if revision == "" { + return false + } + + cmd := exec.Command("git", "cat-file", "-t", revision) + out, err := m.runCmdOutput(cmd, runOpts{SkipErrorLogging: true}) + if out == "commit" && err == nil { + return true + } else { + return false + } +} + // Fetch fetches latest updates from origin func (m *nativeGitClient) Fetch(revision string) error { if m.OnFetch != nil { diff --git a/util/git/mocks/Client.go b/util/git/mocks/Client.go index 9701ec987eb199..798c07465431af 100644 --- a/util/git/mocks/Client.go +++ b/util/git/mocks/Client.go @@ -142,6 +142,24 @@ func (_m *Client) IsAnnotatedTag(_a0 string) bool { return r0 } +// IsRevisionPresent provides a mock function with given fields: revision +func (_m *Client) IsRevisionPresent(revision string) bool { + ret := _m.Called(revision) + + if len(ret) == 0 { + panic("no return value specified for IsRevisionPresent") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(string) bool); ok { + r0 = rf(revision) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + // LsFiles provides a mock function with given fields: path, enableNewGitFileGlobbing func (_m *Client) LsFiles(path string, enableNewGitFileGlobbing bool) ([]string, error) { ret := _m.Called(path, enableNewGitFileGlobbing)