diff options
author | Will Chandler <wchandler@gitlab.com> | 2023-05-23 19:04:20 +0300 |
---|---|---|
committer | Will Chandler <wchandler@gitlab.com> | 2023-05-23 19:04:20 +0300 |
commit | 42b707da5e621f5e43d910f7eecf7887acde8b86 (patch) | |
tree | 3c54c91102f1efc5eb1fb21f534c37552e2302c4 | |
parent | c5238107b9d331c75918d6752c51d895e803a083 (diff) | |
parent | e65535f8b5b157a6c04f5fa6c302400f3f20dd8c (diff) |
Merge branch 'smh-forced-reference-updates' into 'master'
Allow forcing reference updates in TransactionManager
Closes #5141
See merge request https://gitlab.com/gitlab-org/gitaly/-/merge_requests/5800
Merged-by: Will Chandler <wchandler@gitlab.com>
Approved-by: Toon Claes <toon@gitlab.com>
Approved-by: Will Chandler <wchandler@gitlab.com>
Co-authored-by: Sami Hiltunen <shiltunen@gitlab.com>
-rw-r--r-- | internal/gitaly/transaction_manager.go | 45 | ||||
-rw-r--r-- | internal/gitaly/transaction_manager_test.go | 87 |
2 files changed, 111 insertions, 21 deletions
diff --git a/internal/gitaly/transaction_manager.go b/internal/gitaly/transaction_manager.go index 4fecda296..4e2060dcf 100644 --- a/internal/gitaly/transaction_manager.go +++ b/internal/gitaly/transaction_manager.go @@ -93,6 +93,9 @@ func (index LogIndex) String() string { // ReferenceUpdate describes the state of a reference's old and new tip in an update. type ReferenceUpdate struct { + // Force indicates this is a forced reference update. If set, the reference is pointed + // to the new value regardless of the old value. + Force bool // OldOID is the old OID the reference is expected to point to prior to updating it. // If the reference does not point to the old value, the reference verification fails. OldOID git.ObjectID @@ -1027,7 +1030,7 @@ func packFilePathForLogIndex(repoPath string, index LogIndex) string { // It returns the write-ahead log entry for the transaction if it was successfully verified. func (mgr *TransactionManager) verifyReferences(ctx context.Context, transaction *Transaction) ([]*gitalypb.LogEntry_ReferenceUpdate, error) { var referenceUpdates []*gitalypb.LogEntry_ReferenceUpdate - for referenceName, tips := range transaction.referenceUpdates { + for referenceName, update := range transaction.referenceUpdates { // 'git update-ref' doesn't ensure the loose references end up in the // refs directory so we enforce that here. if !strings.HasPrefix(referenceName.String(), "refs/") { @@ -1043,33 +1046,35 @@ func (mgr *TransactionManager) verifyReferences(ctx context.Context, transaction } } - actualOldTip, err := transaction.stagingRepository.ResolveRevision(ctx, referenceName.Revision()) - if errors.Is(err, git.ErrReferenceNotFound) { - objectHash, err := transaction.stagingRepository.ObjectHash(ctx) - if err != nil { - return nil, fmt.Errorf("object hash: %w", err) - } - - actualOldTip = objectHash.ZeroOID - } else if err != nil { - return nil, fmt.Errorf("resolve revision: %w", err) - } + if !update.Force { + actualOldTip, err := transaction.stagingRepository.ResolveRevision(ctx, referenceName.Revision()) + if errors.Is(err, git.ErrReferenceNotFound) { + objectHash, err := transaction.stagingRepository.ObjectHash(ctx) + if err != nil { + return nil, fmt.Errorf("object hash: %w", err) + } - if tips.OldOID != actualOldTip { - if transaction.skipVerificationFailures { - continue + actualOldTip = objectHash.ZeroOID + } else if err != nil { + return nil, fmt.Errorf("resolve revision: %w", err) } - return nil, ReferenceVerificationError{ - ReferenceName: referenceName, - ExpectedOID: tips.OldOID, - ActualOID: actualOldTip, + if update.OldOID != actualOldTip { + if transaction.skipVerificationFailures { + continue + } + + return nil, ReferenceVerificationError{ + ReferenceName: referenceName, + ExpectedOID: update.OldOID, + ActualOID: actualOldTip, + } } } referenceUpdates = append(referenceUpdates, &gitalypb.LogEntry_ReferenceUpdate{ ReferenceName: []byte(referenceName), - NewOid: []byte(tips.NewOID), + NewOid: []byte(update.NewOID), }) } diff --git a/internal/gitaly/transaction_manager_test.go b/internal/gitaly/transaction_manager_test.go index b8fb7c073..92eb248d3 100644 --- a/internal/gitaly/transaction_manager_test.go +++ b/internal/gitaly/transaction_manager_test.go @@ -2629,6 +2629,91 @@ func TestTransactionManager(t *testing.T) { RepositoryDoesntExist: true, }, }, + { + desc: "forced reference creation succeeds", + steps: steps{ + StartManager{}, + Begin{}, + Commit{ + ReferenceUpdates: ReferenceUpdates{ + "refs/heads/main": {Force: true, NewOID: setup.Commits.First.OID}, + }, + }, + }, + expectedState: StateAssertion{ + DefaultBranch: "refs/heads/main", + References: []git.Reference{{Name: "refs/heads/main", Target: setup.Commits.First.OID.String()}}, + Database: DatabaseState{ + string(keyAppliedLogIndex(relativePath)): LogIndex(1).toProto(), + }, + }, + }, + { + desc: "forced reference update succeeds", + steps: steps{ + StartManager{}, + Begin{ + TransactionID: 1, + }, + Commit{ + TransactionID: 1, + ReferenceUpdates: ReferenceUpdates{ + "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID}, + }, + }, + Begin{ + TransactionID: 2, + ExpectedSnapshot: Snapshot{ + ReadIndex: 1, + }, + }, + Commit{ + TransactionID: 2, + ReferenceUpdates: ReferenceUpdates{ + "refs/heads/main": {Force: true, NewOID: setup.Commits.Second.OID}, + }, + }, + }, + expectedState: StateAssertion{ + DefaultBranch: "refs/heads/main", + References: []git.Reference{{Name: "refs/heads/main", Target: setup.Commits.Second.OID.String()}}, + Database: DatabaseState{ + string(keyAppliedLogIndex(relativePath)): LogIndex(2).toProto(), + }, + }, + }, + { + desc: "forced reference deletion succeeds", + steps: steps{ + StartManager{}, + Begin{ + TransactionID: 1, + }, + Commit{ + TransactionID: 1, + ReferenceUpdates: ReferenceUpdates{ + "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID}, + }, + }, + Begin{ + TransactionID: 2, + ExpectedSnapshot: Snapshot{ + ReadIndex: 1, + }, + }, + Commit{ + TransactionID: 2, + ReferenceUpdates: ReferenceUpdates{ + "refs/heads/main": {Force: true, NewOID: setup.ObjectHash.ZeroOID}, + }, + }, + }, + expectedState: StateAssertion{ + Database: DatabaseState{ + string(keyAppliedLogIndex(relativePath)): LogIndex(2).toProto(), + }, + }, + }, } type invalidReferenceTestCase struct { @@ -2657,7 +2742,7 @@ func TestTransactionManager(t *testing.T) { Begin{}, Commit{ ReferenceUpdates: ReferenceUpdates{ - tc.referenceName: {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID}, + tc.referenceName: {Force: true, NewOID: setup.Commits.First.OID}, }, ExpectedError: err, }, |