Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Cai <jcai@gitlab.com>2023-02-14 07:34:22 +0300
committerJohn Cai <jcai@gitlab.com>2023-02-16 07:40:05 +0300
commit85a97c985f990b037be757aad7027b29ceaa2732 (patch)
tree0c917b2d3c4a36a2c7624524fc7d5be28f1ed8ad
parent3ff62a602b1c60a2639880635995fd0f8290e3d5 (diff)
localrepo: Allow unrelated histories in merge tree
In Git, by default git-merge(1) and git-merge_tree(1) throw an error when an attempted merge is made on two commits that do not have a shared ancestor. There are times when we want to allow this. For example, if a project has a default branch `master` that has some history in it, then the project decides to create a new branch `main` and wants to merge `master` back into `main`, the two branches will not have shared history. Create an option that allows these kinds of merges.
-rw-r--r--internal/git/localrepo/merge.go40
-rw-r--r--internal/git/localrepo/merge_test.go61
2 files changed, 95 insertions, 6 deletions
diff --git a/internal/git/localrepo/merge.go b/internal/git/localrepo/merge.go
index f3dc32a92..60a2608cb 100644
--- a/internal/git/localrepo/merge.go
+++ b/internal/git/localrepo/merge.go
@@ -12,22 +12,50 @@ import (
"gitlab.com/gitlab-org/gitaly/v15/internal/helper/text"
)
+type mergeTreeConfig struct {
+ allowUnrelatedHistories bool
+}
+
+// MergeTreeOption is a function that sets a config in mergeTreeConfig.
+type MergeTreeOption func(*mergeTreeConfig)
+
+// WithAllowUnrelatedHistories lets MergeTree accept two commits that do not
+// share a common ancestor.
+func WithAllowUnrelatedHistories() MergeTreeOption {
+ return func(options *mergeTreeConfig) {
+ options.allowUnrelatedHistories = true
+ }
+}
+
// MergeTree calls git-merge-tree(1) with arguments, and parses the results from
// stdout.
func (repo *Repo) MergeTree(
ctx context.Context,
ours, theirs string,
+ mergeTreeOptions ...MergeTreeOption,
) (git.ObjectID, error) {
+ var config mergeTreeConfig
+
+ for _, option := range mergeTreeOptions {
+ option(&config)
+ }
+
+ flags := []git.Option{
+ git.Flag{Name: "--write-tree"},
+ git.Flag{Name: "--name-only"},
+ }
+
+ if config.allowUnrelatedHistories {
+ flags = append(flags, git.Flag{Name: "--allow-unrelated-histories"})
+ }
+
var stdout, stderr bytes.Buffer
err := repo.ExecAndWait(
ctx,
git.Command{
- Name: "merge-tree",
- Flags: []git.Option{
- git.Flag{Name: "--write-tree"},
- git.Flag{Name: "--name-only"},
- },
- Args: []string{ours, theirs},
+ Name: "merge-tree",
+ Flags: flags,
+ Args: []string{ours, theirs},
},
git.WithStderr(&stderr),
git.WithStdout(&stdout),
diff --git a/internal/git/localrepo/merge_test.go b/internal/git/localrepo/merge_test.go
index f4755635b..11112d72d 100644
--- a/internal/git/localrepo/merge_test.go
+++ b/internal/git/localrepo/merge_test.go
@@ -210,6 +210,67 @@ func TestMergeTree(t *testing.T) {
}
})
}
+
+ t.Run("allow unrelated histories", func(t *testing.T) {
+ ctx := testhelper.Context(t)
+
+ repoProto, repoPath := gittest.CreateRepository(t, ctx, cfg, gittest.CreateRepositoryConfig{
+ SkipCreationViaService: true,
+ })
+ repo := NewTestRepo(t, cfg, repoProto)
+
+ tree1 := gittest.WriteTree(t, cfg, repoPath, []gittest.TreeEntry{
+ {
+ Mode: "100644",
+ Path: "file1",
+ Content: "foo",
+ },
+ })
+ tree2 := gittest.WriteTree(t, cfg, repoPath, []gittest.TreeEntry{
+ {
+ Mode: "100644",
+ Path: "file2",
+ Content: "baz",
+ },
+ })
+ ours := gittest.WriteCommit(t, cfg, repoPath,
+ gittest.WithTree(tree1),
+ gittest.WithAuthorName("Woody"),
+ gittest.WithCommitterName("Woody"),
+ )
+ theirs := gittest.WriteCommit(t, cfg, repoPath,
+ gittest.WithTree(tree2),
+ gittest.WithAuthorName("Buzz"),
+ gittest.WithCommitterName("Buzz"),
+ )
+
+ mergeTreeResult, err := repo.MergeTree(
+ ctx,
+ string(ours),
+ string(theirs),
+ WithAllowUnrelatedHistories(),
+ )
+ require.NoError(t, err)
+
+ gittest.RequireTree(
+ t,
+ cfg,
+ repoPath,
+ string(mergeTreeResult),
+ []gittest.TreeEntry{
+ {
+ Mode: "100644",
+ Path: "file1",
+ Content: "foo",
+ },
+ {
+ Mode: "100644",
+ Path: "file2",
+ Content: "baz",
+ },
+ },
+ )
+ })
}
func TestParseResult(t *testing.T) {