From 8a3df9da429cda4c8fce380e8e376bef9bc38ca0 Mon Sep 17 00:00:00 2001 From: Somtochiama Date: Wed, 4 May 2022 09:37:07 +0100 Subject: [PATCH] gogit: check if revision changed before cloning in checkout branch (#694) * Check if revision has changed in gogit CheckoutBranch Signed-off-by: Somtochi Onyekwere --- pkg/git/gogit/checkout.go | 39 +++++++++++++++++++++++++++++++++- pkg/git/gogit/checkout_test.go | 14 ++++++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/pkg/git/gogit/checkout.go b/pkg/git/gogit/checkout.go index c401e3dd..6c5a7064 100644 --- a/pkg/git/gogit/checkout.go +++ b/pkg/git/gogit/checkout.go @@ -26,8 +26,10 @@ import ( "github.com/Masterminds/semver/v3" extgogit "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" + "github.com/go-git/go-git/v5/storage/memory" "github.com/fluxcd/pkg/gitutil" "github.com/fluxcd/pkg/version" @@ -50,13 +52,14 @@ func CheckoutStrategyForOptions(_ context.Context, opts git.CheckoutOptions) git if branch == "" { branch = git.DefaultBranch } - return &CheckoutBranch{Branch: branch, RecurseSubmodules: opts.RecurseSubmodules} + return &CheckoutBranch{Branch: branch, RecurseSubmodules: opts.RecurseSubmodules, LastRevision: opts.LastRevision} } } type CheckoutBranch struct { Branch string RecurseSubmodules bool + LastRevision string } func (c *CheckoutBranch) Checkout(ctx context.Context, path, url string, opts *git.AuthOptions) (*git.Commit, error) { @@ -64,7 +67,31 @@ func (c *CheckoutBranch) Checkout(ctx context.Context, path, url string, opts *g if err != nil { return nil, fmt.Errorf("failed to construct auth method with options: %w", err) } + ref := plumbing.NewBranchReferenceName(c.Branch) + // check if previous revision has changed before attempting to clone + if c.LastRevision != "" { + config := &config.RemoteConfig{ + Name: git.DefaultOrigin, + URLs: []string{url}, + } + rem := extgogit.NewRemote(memory.NewStorage(), config) + refs, err := rem.List(&extgogit.ListOptions{ + Auth: authMethod, + }) + if err != nil { + return nil, fmt.Errorf("unable to list remote for '%s': %w", url, err) + } + + currentRevision := filterRefs(refs, ref) + if currentRevision != "" && currentRevision == c.LastRevision { + return nil, git.NoChangesError{ + Message: "no changes since last reconcilation", + ObservedRevision: currentRevision, + } + } + } + repo, err := extgogit.PlainCloneContext(ctx, path, false, &extgogit.CloneOptions{ URL: url, Auth: authMethod, @@ -333,3 +360,13 @@ func recurseSubmodules(recurse bool) extgogit.SubmoduleRescursivity { } return extgogit.NoRecurseSubmodules } + +func filterRefs(refs []*plumbing.Reference, currentRef plumbing.ReferenceName) string { + for _, ref := range refs { + if ref.Name().String() == currentRef.String() { + return fmt.Sprintf("%s/%s", currentRef.Short(), ref.Hash().String()) + } + } + + return "" +} diff --git a/pkg/git/gogit/checkout_test.go b/pkg/git/gogit/checkout_test.go index f74aaf74..7c296d66 100644 --- a/pkg/git/gogit/checkout_test.go +++ b/pkg/git/gogit/checkout_test.go @@ -72,6 +72,7 @@ func TestCheckoutBranch_Checkout(t *testing.T) { filesCreated map[string]string expectedCommit string expectedErr string + lastRevision string }{ { name: "Default branch", @@ -80,10 +81,18 @@ func TestCheckoutBranch_Checkout(t *testing.T) { expectedCommit: firstCommit.String(), }, { - name: "Other branch", + name: "skip clone if LastRevision hasn't changed", + branch: "master", + filesCreated: map[string]string{"branch": "init"}, + expectedErr: fmt.Sprintf("no changes since last reconcilation: observed revision 'master/%s'", firstCommit.String()), + lastRevision: fmt.Sprintf("master/%s", firstCommit.String()), + }, + { + name: "Other branch - revision has changed", branch: "test", filesCreated: map[string]string{"branch": "second"}, expectedCommit: secondCommit.String(), + lastRevision: fmt.Sprintf("master/%s", firstCommit.String()), }, { name: "Non existing branch", @@ -97,7 +106,8 @@ func TestCheckoutBranch_Checkout(t *testing.T) { g := NewWithT(t) branch := CheckoutBranch{ - Branch: tt.branch, + Branch: tt.branch, + LastRevision: tt.lastRevision, } tmpDir := t.TempDir()