Skip to content

Commit 1d03712

Browse files
rebase: Add wrapper for git_rebase_inmemory_index() (#900) (#905)
* rebase: Fix missing initialization of the repo pointer While the `Rebase` structure has a pointer to the repository the rebase is creatde in, this pointer isn't ever initialized. Fix this. * rebase: Add wrapper for `git_rebase_inmemory_index()` Add a new wrapper for `git_rebase_inmemory_index()`, which can be used to retrieve the index for an in-memory rebase. Co-authored-by: Patrick Steinhardt <[email protected]> (cherry picked from commit e7d1b2b) Co-authored-by: Patrick Steinhardt <[email protected]>
1 parent 4941b3d commit 1d03712

File tree

2 files changed

+99
-4
lines changed

2 files changed

+99
-4
lines changed

rebase.go

+25-4
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ func (r *Repository) InitRebase(branch *AnnotatedCommit, upstream *AnnotatedComm
253253
return nil, MakeGitError(ret)
254254
}
255255

256-
return newRebaseFromC(ptr, cOpts), nil
256+
return newRebaseFromC(ptr, r, cOpts), nil
257257
}
258258

259259
// OpenRebase opens an existing rebase that was previously started by either an invocation of InitRebase or by another client.
@@ -275,7 +275,7 @@ func (r *Repository) OpenRebase(opts *RebaseOptions) (*Rebase, error) {
275275
return nil, MakeGitError(ret)
276276
}
277277

278-
return newRebaseFromC(ptr, cOpts), nil
278+
return newRebaseFromC(ptr, r, cOpts), nil
279279
}
280280

281281
// OperationAt gets the rebase operation specified by the given index.
@@ -327,6 +327,27 @@ func (rebase *Rebase) Next() (*RebaseOperation, error) {
327327
return newRebaseOperationFromC(ptr), nil
328328
}
329329

330+
// InmemoryIndex gets the index produced by the last operation, which is the
331+
// result of `Next()` and which will be committed by the next invocation of
332+
// `Commit()`. This is useful for resolving conflicts in an in-memory rebase
333+
// before committing them.
334+
//
335+
// This is only applicable for in-memory rebases; for rebases within a working
336+
// directory, the changes were applied to the repository's index.
337+
func (rebase *Rebase) InmemoryIndex() (*Index, error) {
338+
runtime.LockOSThread()
339+
defer runtime.UnlockOSThread()
340+
341+
var ptr *C.git_index
342+
err := C.git_rebase_inmemory_index(&ptr, rebase.ptr)
343+
runtime.KeepAlive(rebase)
344+
if err < 0 {
345+
return nil, MakeGitError(err)
346+
}
347+
348+
return newIndexFromC(ptr, rebase.r), nil
349+
}
350+
330351
// Commit commits the current patch.
331352
// You must have resolved any conflicts that were introduced during the patch application from the Next() invocation.
332353
func (rebase *Rebase) Commit(ID *Oid, author, committer *Signature, message string) error {
@@ -392,8 +413,8 @@ func (r *Rebase) Free() {
392413
freeRebaseOptions(r.options)
393414
}
394415

395-
func newRebaseFromC(ptr *C.git_rebase, opts *C.git_rebase_options) *Rebase {
396-
rebase := &Rebase{ptr: ptr, options: opts}
416+
func newRebaseFromC(ptr *C.git_rebase, repo *Repository, opts *C.git_rebase_options) *Rebase {
417+
rebase := &Rebase{ptr: ptr, r: repo, options: opts}
397418
runtime.SetFinalizer(rebase, (*Rebase).Free)
398419
return rebase
399420
}

rebase_test.go

+74
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,80 @@ import (
1414

1515
// Tests
1616

17+
func TestRebaseInMemoryWithConflict(t *testing.T) {
18+
repo := createTestRepo(t)
19+
defer cleanupTestRepo(t, repo)
20+
seedTestRepo(t, repo)
21+
22+
// Create two branches with common history, where both modify "common-file"
23+
// in a conflicting way.
24+
_, err := commitSomething(repo, "common-file", "a\nb\nc\n", commitOptions{})
25+
checkFatal(t, err)
26+
checkFatal(t, createBranch(repo, "branch-a"))
27+
checkFatal(t, createBranch(repo, "branch-b"))
28+
29+
checkFatal(t, repo.SetHead("refs/heads/branch-a"))
30+
_, err = commitSomething(repo, "common-file", "1\nb\nc\n", commitOptions{})
31+
checkFatal(t, err)
32+
33+
checkFatal(t, repo.SetHead("refs/heads/branch-b"))
34+
_, err = commitSomething(repo, "common-file", "x\nb\nc\n", commitOptions{})
35+
checkFatal(t, err)
36+
37+
branchA, err := repo.LookupBranch("branch-a", BranchLocal)
38+
checkFatal(t, err)
39+
onto, err := repo.AnnotatedCommitFromRef(branchA.Reference)
40+
checkFatal(t, err)
41+
42+
// We then rebase "branch-b" onto "branch-a" in-memory, which should result
43+
// in a conflict.
44+
rebase, err := repo.InitRebase(nil, nil, onto, &RebaseOptions{InMemory: 1})
45+
checkFatal(t, err)
46+
47+
_, err = rebase.Next()
48+
checkFatal(t, err)
49+
50+
index, err := rebase.InmemoryIndex()
51+
checkFatal(t, err)
52+
53+
// We simply resolve the conflict and commit the rebase.
54+
if !index.HasConflicts() {
55+
t.Fatal("expected index to have conflicts")
56+
}
57+
58+
conflict, err := index.Conflict("common-file")
59+
checkFatal(t, err)
60+
61+
resolvedBlobID, err := repo.CreateBlobFromBuffer([]byte("resolved contents"))
62+
checkFatal(t, err)
63+
64+
resolvedEntry := *conflict.Our
65+
resolvedEntry.Id = resolvedBlobID
66+
checkFatal(t, index.Add(&resolvedEntry))
67+
checkFatal(t, index.RemoveConflict("common-file"))
68+
69+
var commitID Oid
70+
checkFatal(t, rebase.Commit(&commitID, signature(), signature(), "rebased message"))
71+
checkFatal(t, rebase.Finish())
72+
73+
// And then assert that we can look up the new merge commit, and that the
74+
// "common-file" has the expected contents.
75+
commit, err := repo.LookupCommit(&commitID)
76+
checkFatal(t, err)
77+
if commit.Message() != "rebased message" {
78+
t.Fatalf("unexpected commit message %q", commit.Message())
79+
}
80+
81+
tree, err := commit.Tree()
82+
checkFatal(t, err)
83+
84+
blob, err := repo.LookupBlob(tree.EntryByName("common-file").Id)
85+
checkFatal(t, err)
86+
if string(blob.Contents()) != "resolved contents" {
87+
t.Fatalf("unexpected resolved blob contents %q", string(blob.Contents()))
88+
}
89+
}
90+
1791
func TestRebaseAbort(t *testing.T) {
1892
// TEST DATA
1993

0 commit comments

Comments
 (0)