diff options
author | Patrick Steinhardt <psteinhardt@gitlab.com> | 2021-01-13 12:59:44 +0300 |
---|---|---|
committer | Patrick Steinhardt <psteinhardt@gitlab.com> | 2021-01-26 10:07:59 +0300 |
commit | 16af76ac3004eee3ed9a92e19b3769e73f610088 (patch) | |
tree | b3fdfd37b683c29d397d86135eac0dde0952f252 | |
parent | 232df1394c114a6ef2d844d572e0493bf4a6e60d (diff) |
git2go: Implement handling of AllowConflicts for UserMergeToRef
While we ported the UserMergeToRef RPC to Go, a new flag
"allow_conflicts" was added to its request. What it does is that instead
of raising an error when there's any conflict, the RPC will now commit
the conflicting file together with its conflict markers.
Given that both efforts kind of crossed, the parameter isn't supported
by the Go implementation nowadays. This commit lays the groundwork to
support it.
-rw-r--r-- | cmd/gitaly-git2go/merge.go | 74 | ||||
-rw-r--r-- | internal/git2go/merge.go | 3 |
2 files changed, 76 insertions, 1 deletions
diff --git a/cmd/gitaly-git2go/merge.go b/cmd/gitaly-git2go/merge.go index 907e717c0..dc9efd808 100644 --- a/cmd/gitaly-git2go/merge.go +++ b/cmd/gitaly-git2go/merge.go @@ -12,6 +12,7 @@ import ( "time" git "github.com/libgit2/git2go/v30" + "gitlab.com/gitlab-org/gitaly/cmd/gitaly-git2go/conflicts" "gitlab.com/gitlab-org/gitaly/internal/git2go" ) @@ -64,7 +65,13 @@ func (cmd *mergeSubcommand) Run(context.Context, io.Reader, io.Writer) error { defer index.Free() if index.HasConflicts() { - return errors.New("could not auto-merge due to conflicts") + if !request.AllowConflicts { + return errors.New("could not auto-merge due to conflicts") + } + + if err := resolveConflicts(repo, index); err != nil { + return fmt.Errorf("could not resolve conflicts: %w", err) + } } tree, err := index.WriteTreeTo(repo) @@ -88,3 +95,68 @@ func (cmd *mergeSubcommand) Run(context.Context, io.Reader, io.Writer) error { return nil } + +func resolveConflicts(repo *git.Repository, index *git.Index) error { + // We need to get all conflicts up front as resolving conflicts as we + // iterate breaks the iterator. + indexConflicts, err := getConflicts(index) + if err != nil { + return err + } + + for _, conflict := range indexConflicts { + merge, err := conflicts.Merge(repo, conflict) + if err != nil { + return err + } + + mergedBlob, err := repo.CreateBlobFromBuffer(merge.Contents) + if err != nil { + return err + } + + mergedIndexEntry := git.IndexEntry{ + Path: merge.Path, + Mode: git.Filemode(merge.Mode), + Id: mergedBlob, + } + + if err := index.Add(&mergedIndexEntry); err != nil { + return err + } + + if err := index.RemoveConflict(merge.Path); err != nil { + return err + } + } + + if index.HasConflicts() { + return errors.New("index still has conflicts") + } + + return nil +} + +func getConflicts(index *git.Index) ([]git.IndexConflict, error) { + var conflicts []git.IndexConflict + + iterator, err := index.ConflictIterator() + if err != nil { + return nil, err + } + defer iterator.Free() + + for { + conflict, err := iterator.Next() + if err != nil { + if git.IsErrorCode(err, git.ErrIterOver) { + break + } + return nil, err + } + + conflicts = append(conflicts, conflict) + } + + return conflicts, nil +} diff --git a/internal/git2go/merge.go b/internal/git2go/merge.go index 7659930a1..e92856810 100644 --- a/internal/git2go/merge.go +++ b/internal/git2go/merge.go @@ -37,6 +37,9 @@ type MergeCommand struct { Ours string `json:"ours"` // Theirs is the commit into which ours is to be merged. Theirs string `json:"theirs"` + // AllowConflicts controls whether conflicts are allowed. If they are, + // then conflicts will be committed as part of the result. + AllowConflicts bool `json:"allow_conflicts"` } // MergeResult contains results from a merge. |