Skip to content

Commit 0f4113d

Browse files
committed
merge: support CrissCrossMerge close #5
see: https://tonyg.github.io/revctrl.org/CrissCrossMerge.html
1 parent 77ae5a1 commit 0f4113d

File tree

6 files changed

+104
-21
lines changed

6 files changed

+104
-21
lines changed

.gitignore

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,9 @@ bin/*
1212
/*.sh
1313
local/*
1414
.DS_Store
15-
utils/viewport/viewport
16-
15+
*.gop1
16+
*.rej
1717
/out/
1818
/.vscode/
19-
2019
.idea/*
2120
vendor/*

pkg/command/command_merge_base.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,5 @@ func (c *MergeBase) Run(g *Globals) error {
5252
diev("At least two versions are required, eg: zeta merge-base A B")
5353
return ErrArgRequired
5454
}
55-
return r.MergeBase(context.Background(), c.Args)
55+
return r.MergeBase(context.Background(), c.Args, c.All)
5656
}

pkg/zeta/merge_tree.go

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,61 @@ func (o *MergeTreeOptions) format(result *odb.MergeResult) {
127127

128128
type mergeTreeResult struct {
129129
*odb.MergeResult
130-
Base plumbing.Hash
130+
b0 plumbing.Hash
131131
}
132132

133-
func (r *Repository) parseMergeBase(ctx context.Context, into, from, base *object.Commit, allowUnrelatedHistories bool) (plumbing.Hash, *object.Tree, error) {
133+
func (r *Repository) resolveAncestorTree0(ctx context.Context, into, from *object.Commit, mergeDriver odb.MergeDriver, allowUnrelatedHistories, textconv bool) (*object.Tree, error) {
134+
bases, err := into.MergeBase(ctx, from)
135+
if err != nil {
136+
die_error("merge-base '%s-%s': %v", from.Hash, into.Hash, err)
137+
return nil, err
138+
}
139+
var o *object.Tree
140+
switch len(bases) {
141+
case 0:
142+
if !allowUnrelatedHistories {
143+
r.DbgPrint("merge: merge from %s to %s refusing to merge unrelated histories", from.Hash, into.Hash)
144+
fmt.Fprintf(os.Stderr, "merge: %s\n", W("refusing to merge unrelated histories"))
145+
return nil, ErrUnrelatedHistories
146+
}
147+
return r.odb.EmptyTree(), nil
148+
case 1:
149+
if o, err = bases[0].Root(ctx); err != nil {
150+
die_error("resolve bases tree: %v", err)
151+
return nil, err
152+
}
153+
default:
154+
if o, err = r.resolveAncestorTree0(ctx, bases[0], bases[1], mergeDriver, allowUnrelatedHistories, textconv); err != nil {
155+
return nil, err
156+
}
157+
}
158+
a, err := into.Root(ctx)
159+
if err != nil {
160+
return nil, err
161+
}
162+
b, err := from.Root(ctx)
163+
if err != nil {
164+
return nil, err
165+
}
166+
result, err := r.odb.MergeTree(ctx, o, a, b, &odb.MergeOptions{
167+
Branch1: "Temporary merge branch 1",
168+
Branch2: "Temporary merge branch 2",
169+
DetectRenames: true,
170+
Textconv: textconv,
171+
MergeDriver: mergeDriver,
172+
TextGetter: r.readMissingText,
173+
})
174+
if err != nil {
175+
return nil, err
176+
}
177+
if len(result.Conflicts) != 0 {
178+
return nil, result
179+
}
180+
r.DbgPrint("make new merge-tree: %s", result.NewTree)
181+
return r.odb.Tree(ctx, result.NewTree)
182+
}
183+
184+
func (r *Repository) resolveAncestorTree(ctx context.Context, into, from, base *object.Commit, mergeDriver odb.MergeDriver, allowUnrelatedHistories, textconv bool) (plumbing.Hash, *object.Tree, error) {
134185
if base != nil {
135186
o, err := base.Root(ctx)
136187
if err != nil {
@@ -152,20 +203,29 @@ func (r *Repository) parseMergeBase(ctx context.Context, into, from, base *objec
152203
}
153204
return plumbing.ZeroHash, r.odb.EmptyTree(), nil
154205
}
155-
o, err := bases[0].Root(ctx)
206+
if len(bases) == 1 {
207+
o, err := bases[0].Root(ctx)
208+
if err != nil {
209+
die_error("resolve bases tree: %v", err)
210+
return plumbing.ZeroHash, nil, err
211+
}
212+
return bases[0].Hash, o, nil
213+
}
214+
o, err := r.resolveAncestorTree0(ctx, bases[0], bases[1], mergeDriver, allowUnrelatedHistories, textconv)
156215
if err != nil {
157-
die_error("resolve bases tree: %v", err)
158216
return plumbing.ZeroHash, nil, err
159217
}
218+
160219
return bases[0].Hash, o, nil
161220
}
162221

163222
func (r *Repository) mergeTree(ctx context.Context, into, from, base *object.Commit, branch1, branch2 string, allowUnrelatedHistories, textconv bool) (*mergeTreeResult, error) {
164-
baseID, o, err := r.parseMergeBase(ctx, into, from, base, allowUnrelatedHistories)
223+
mergeDriver := r.resolveMergeDriver()
224+
base0, o, err := r.resolveAncestorTree(ctx, into, from, base, mergeDriver, allowUnrelatedHistories, textconv)
165225
if err != nil {
166226
return nil, err
167227
}
168-
r.DbgPrint("merge from %s to %s base: %s", from.Hash, into.Hash, baseID)
228+
r.DbgPrint("merge from %s to %s base: %s", from.Hash, into.Hash, base0)
169229
a, err := from.Root(ctx)
170230
if err != nil {
171231
die_error("read tree '%s:' %v", from.Hash, err)
@@ -177,21 +237,21 @@ func (r *Repository) mergeTree(ctx context.Context, into, from, base *object.Com
177237
return nil, err
178238
}
179239
if a.Equal(b) {
180-
return &mergeTreeResult{MergeResult: &odb.MergeResult{NewTree: a.Hash}, Base: baseID}, nil
240+
return &mergeTreeResult{MergeResult: &odb.MergeResult{NewTree: a.Hash}, b0: base0}, nil
181241
}
182242
result, err := r.odb.MergeTree(ctx, o, a, b, &odb.MergeOptions{
183243
Branch1: branch1,
184244
Branch2: branch2,
185245
DetectRenames: true,
186246
Textconv: textconv,
187-
MergeDriver: r.resolveMergeDriver(),
247+
MergeDriver: mergeDriver,
188248
TextGetter: r.readMissingText,
189249
})
190250
if err != nil {
191251
die_error("merge-tree: %v", err)
192252
return nil, err
193253
}
194-
return &mergeTreeResult{MergeResult: result, Base: baseID}, nil
254+
return &mergeTreeResult{MergeResult: result, b0: base0}, nil
195255
}
196256

197257
func (r *Repository) MergeTree(ctx context.Context, opts *MergeTreeOptions) error {
@@ -214,6 +274,10 @@ func (r *Repository) MergeTree(ctx context.Context, opts *MergeTreeOptions) erro
214274
}
215275
result, err := r.mergeTree(ctx, c1, c2, base, opts.Branch1, opts.Branch2, opts.AllowUnrelatedHistories, opts.Textconv)
216276
if err != nil {
277+
if mr, ok := err.(*odb.MergeResult); ok {
278+
opts.format(mr)
279+
return ErrHasConflicts
280+
}
217281
return err
218282
}
219283
opts.format(result.MergeResult)

pkg/zeta/odb/merge.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,10 @@ type MergeResult struct {
350350
Messages []string `json:"messages,omitempty"`
351351
}
352352

353+
func (mr *MergeResult) Error() string {
354+
return "conflicts"
355+
}
356+
353357
func (d *ODB) mergeEntry(ctx context.Context, ch *ChangeEntry, opts *MergeOptions, result *MergeResult) (*TreeEntry, error) {
354358
// Both sides add
355359
if ch.Ancestor == nil {
@@ -490,7 +494,10 @@ func (d *ODB) unifiedText(ctx context.Context, oid plumbing.Hash, textConv bool)
490494
// MergeTree: three way merge tree
491495
func (d *ODB) MergeTree(ctx context.Context, o, a, b *object.Tree, opts *MergeOptions) (*MergeResult, error) {
492496
if opts.Branch1 == "" {
493-
opts.Branch1 = a.Hash.String()
497+
opts.Branch1 = "Branch1"
498+
}
499+
if opts.Branch2 == "" {
500+
opts.Branch2 = "Branch2"
494501
}
495502
if opts.MergeDriver == nil {
496503
opts.MergeDriver = diffmatchpatch.Merge // fallback

pkg/zeta/revision.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ func (r *Repository) IsAncestor(ctx context.Context, a, b string) error {
315315
return ErrNotAncestor
316316
}
317317

318-
func (r *Repository) MergeBase(ctx context.Context, revisions []string) error {
318+
func (r *Repository) MergeBase(ctx context.Context, revisions []string, all bool) error {
319319
commits := make([]*object.Commit, 0, len(revisions))
320320
for _, a := range revisions {
321321
cc, err := r.parseRevExhaustive(ctx, a)
@@ -330,20 +330,27 @@ func (r *Repository) MergeBase(ctx context.Context, revisions []string) error {
330330
return ErrAborting
331331
}
332332
c0 := commits[0]
333-
base := c0
333+
bases := make([]*object.Commit, 0, 2)
334+
var err error
335+
current := c0
334336
for i := 1; i < len(commits); i++ {
335337
rev := commits[i]
336-
bases, err := rev.MergeBase(ctx, base)
337-
if err != nil {
338+
if bases, err = rev.MergeBase(ctx, current); err != nil {
338339
die_error("merge-base: %v", err)
339340
return err
340341
}
341342
if len(bases) == 0 {
342343
fmt.Fprintln(os.Stderr, "merge-base: unrelated histories")
343344
return ErrUnrelatedHistories
344345
}
345-
base = bases[0]
346+
current = bases[0]
347+
}
348+
if all {
349+
for _, b := range bases {
350+
fmt.Fprintln(os.Stdout, b.Hash)
351+
}
352+
return nil
346353
}
347-
fmt.Fprintln(os.Stdout, base.Hash.String())
354+
fmt.Fprintln(os.Stdout, bases[0].Hash)
348355
return nil
349356
}

pkg/zeta/worktree_merge.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,12 @@ func (w *Worktree) mergeInternal(ctx context.Context, into, from plumbing.Hash,
260260
}
261261
result, err := w.mergeTree(ctx, c1, c2, nil, branch1, branch2, allowUnrelatedHistories, textconv)
262262
if err != nil {
263+
if mr, ok := err.(*odb.MergeResult); ok {
264+
for _, m := range mr.Messages {
265+
fmt.Fprintln(os.Stderr, m)
266+
}
267+
return plumbing.ZeroHash, ErrHasConflicts
268+
}
263269
return plumbing.ZeroHash, err
264270
}
265271
for _, m := range result.Messages {
@@ -275,7 +281,7 @@ func (w *Worktree) mergeInternal(ctx context.Context, into, from plumbing.Hash,
275281
parents := []plumbing.Hash{into}
276282
message := messageFn()
277283
if squash {
278-
if message, err = w.makeSquashMessage(ctx, from, result.Base, message); err != nil {
284+
if message, err = w.makeSquashMessage(ctx, from, result.b0, message); err != nil {
279285
return plumbing.ZeroHash, err
280286
}
281287
} else {

0 commit comments

Comments
 (0)