diff --git a/changelogs/unreleased/avar-use-update-ref-delete-and-create.yml b/changelogs/unreleased/avar-use-update-ref-delete-and-create.yml new file mode 100644 index 0000000000000000000000000000000000000000..240c413358c447346a8e5983adcc65321425138e --- /dev/null +++ b/changelogs/unreleased/avar-use-update-ref-delete-and-create.yml @@ -0,0 +1,5 @@ +--- +title: 'Cleaner & DeleteRefs: use safer ''update-ref delete'' with argument' +merge_request: 2929 +author: +type: fixed diff --git a/internal/git/updateref/updateref.go b/internal/git/updateref/updateref.go index 7230acc05e4b48cdcabbd35f05766817eed98584..f820986262e8e91b2f11087cd06e706a51476e98 100644 --- a/internal/git/updateref/updateref.go +++ b/internal/git/updateref/updateref.go @@ -65,6 +65,20 @@ func New(ctx context.Context, repo repository.GitRepo, opts ...UpdaterOpt) (*Upd return &Updater{repo: repo, cmd: cmd}, nil } +// CreateUpdateDelete dispatches to Create()/Update()/Delete() as needed +// +// The underlying "update-ref" does the same dispatching depending on +// what it get passed. +func (u *Updater) CreateUpdateDelete(ref, newvalue, oldvalue string) error { + if newvalue == git.NullSHA { + return u.Delete(ref, oldvalue) + } else if oldvalue == git.NullSHA { + return u.Create(ref, newvalue) + } else { + return u.Update(ref, newvalue, oldvalue) + } +} + // Create commands the reference to be created with the sha specified in value func (u *Updater) Create(ref, value string) error { _, err := fmt.Fprintf(u.cmd, "create %s\x00%s\x00", ref, value) @@ -79,8 +93,8 @@ func (u *Updater) Update(ref, newvalue, oldvalue string) error { } // Delete commands the reference to be removed from the repository -func (u *Updater) Delete(ref string) error { - _, err := fmt.Fprintf(u.cmd, "delete %s\x00\x00", ref) +func (u *Updater) Delete(ref string, oldvalue string) error { + _, err := fmt.Fprintf(u.cmd, "delete %s\x00%s\x00", ref, oldvalue) return err } diff --git a/internal/git/updateref/updateref_test.go b/internal/git/updateref/updateref_test.go index 270c04de340cb41969ff52bda5fadc622efde980..ba6074a2754e51ad64d900dd982545f8eeceb173 100644 --- a/internal/git/updateref/updateref_test.go +++ b/internal/git/updateref/updateref_test.go @@ -105,7 +105,9 @@ func TestDelete(t *testing.T) { ref := "refs/heads/feature" - require.NoError(t, updater.Delete(ref)) + refValue, err := git.NewRepository(testRepo).GetReference(ctx, ref) + require.NoError(t, err) + require.NoError(t, updater.Delete(ref, refValue.Target)) require.NoError(t, updater.Wait()) // check the ref was removed diff --git a/internal/gitaly/service/cleanup/internalrefs/cleaner.go b/internal/gitaly/service/cleanup/internalrefs/cleaner.go index 8e75649da86ea0e95209cfad6096b4236931e836..b9c17ed8d4c3478822d4e4392bddbca2540ec409 100644 --- a/internal/gitaly/service/cleanup/internalrefs/cleaner.go +++ b/internal/gitaly/service/cleanup/internalrefs/cleaner.go @@ -10,6 +10,7 @@ import ( "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus" log "github.com/sirupsen/logrus" "gitlab.com/gitlab-org/gitaly/internal/git" + "gitlab.com/gitlab-org/gitaly/internal/git/repository" "gitlab.com/gitlab-org/gitaly/internal/git/updateref" "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb" ) @@ -32,6 +33,7 @@ type ForEachFunc func(ctx context.Context, oldOID, newOID string, isInternalRef // references pointing to a commit that has been rewritten will simply be // removed. type Cleaner struct { + repo repository.GitRepo ctx context.Context forEach ForEachFunc @@ -57,7 +59,7 @@ func NewCleaner(ctx context.Context, repo *gitalypb.Repository, forEach ForEachF return nil, err } - return &Cleaner{ctx: ctx, table: table, updater: updater, forEach: forEach}, nil + return &Cleaner{repo: repo, ctx: ctx, table: table, updater: updater, forEach: forEach}, nil } // ApplyObjectMap processes an object map file generated by git filter-repo, or @@ -117,7 +119,11 @@ func (c *Cleaner) processEntry(ctx context.Context, oldSHA, newSHA string) error // Remove the internal refs pointing to oldSHA for _, ref := range refs { - if err := c.updater.Delete(ref); err != nil { + refValue, err := git.NewRepository(c.repo).GetReference(c.ctx, ref) + if err != nil { + return err + } + if err := c.updater.Delete(ref, refValue.Target); err != nil { return err } } diff --git a/internal/gitaly/service/operations/update_with_hooks.go b/internal/gitaly/service/operations/update_with_hooks.go index ba90634bc46eceff5bac37ae99b53a8ea36c0851..937b00f4011efd68ece762819a520d6b85632d5b 100644 --- a/internal/gitaly/service/operations/update_with_hooks.go +++ b/internal/gitaly/service/operations/update_with_hooks.go @@ -80,7 +80,7 @@ func (s *Server) updateReferenceWithHooks(ctx context.Context, repo *gitalypb.Re return err } - if err := updater.Update(reference, newrev, oldrev); err != nil { + if err := updater.CreateUpdateDelete(reference, newrev, oldrev); err != nil { return err } diff --git a/internal/gitaly/service/ref/delete_refs.go b/internal/gitaly/service/ref/delete_refs.go index 6444c5743a04937cbe5b6a6d2f5fc98d1503b259..6c5e196e7843a2d962c433eae57ac5f0bbbb57a7 100644 --- a/internal/gitaly/service/ref/delete_refs.go +++ b/internal/gitaly/service/ref/delete_refs.go @@ -34,7 +34,11 @@ func (s *server) DeleteRefs(ctx context.Context, in *gitalypb.DeleteRefsRequest) } for _, ref := range refnames { - if err := updater.Delete(ref); err != nil { + refValue, err := git.NewRepository(in.Repository).GetReference(ctx, ref) + if err != nil { + return &gitalypb.DeleteRefsResponse{GitError: err.Error()}, nil + } + if err := updater.Delete(ref, refValue.Target); err != nil { return &gitalypb.DeleteRefsResponse{GitError: err.Error()}, nil } }