Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rebase: Add wrapper for git_rebase_inmemory_index() (#900) #903

Merged
merged 1 commit into from
Feb 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions rebase.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ func (r *Repository) InitRebase(branch *AnnotatedCommit, upstream *AnnotatedComm
return nil, MakeGitError(ret)
}

return newRebaseFromC(ptr, cOpts), nil
return newRebaseFromC(ptr, r, cOpts), nil
}

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

return newRebaseFromC(ptr, cOpts), nil
return newRebaseFromC(ptr, r, cOpts), nil
}

// OperationAt gets the rebase operation specified by the given index.
Expand Down Expand Up @@ -255,6 +255,27 @@ func (rebase *Rebase) Next() (*RebaseOperation, error) {
return newRebaseOperationFromC(ptr), nil
}

// InmemoryIndex gets the index produced by the last operation, which is the
// result of `Next()` and which will be committed by the next invocation of
// `Commit()`. This is useful for resolving conflicts in an in-memory rebase
// before committing them.
//
// This is only applicable for in-memory rebases; for rebases within a working
// directory, the changes were applied to the repository's index.
func (rebase *Rebase) InmemoryIndex() (*Index, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()

var ptr *C.git_index
err := C.git_rebase_inmemory_index(&ptr, rebase.ptr)
runtime.KeepAlive(rebase)
if err < 0 {
return nil, MakeGitError(err)
}

return newIndexFromC(ptr, rebase.r), nil
}

// Commit commits the current patch.
// You must have resolved any conflicts that were introduced during the patch application from the Next() invocation.
func (rebase *Rebase) Commit(ID *Oid, author, committer *Signature, message string) error {
Expand Down Expand Up @@ -320,8 +341,8 @@ func (r *Rebase) Free() {
freeRebaseOptions(r.options)
}

func newRebaseFromC(ptr *C.git_rebase, opts *C.git_rebase_options) *Rebase {
rebase := &Rebase{ptr: ptr, options: opts}
func newRebaseFromC(ptr *C.git_rebase, repo *Repository, opts *C.git_rebase_options) *Rebase {
rebase := &Rebase{ptr: ptr, r: repo, options: opts}
runtime.SetFinalizer(rebase, (*Rebase).Free)
return rebase
}
74 changes: 74 additions & 0 deletions rebase_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,80 @@ import (

// Tests

func TestRebaseInMemoryWithConflict(t *testing.T) {
repo := createTestRepo(t)
defer cleanupTestRepo(t, repo)
seedTestRepo(t, repo)

// Create two branches with common history, where both modify "common-file"
// in a conflicting way.
_, err := commitSomething(repo, "common-file", "a\nb\nc\n", commitOptions{})
checkFatal(t, err)
checkFatal(t, createBranch(repo, "branch-a"))
checkFatal(t, createBranch(repo, "branch-b"))

checkFatal(t, repo.SetHead("refs/heads/branch-a"))
_, err = commitSomething(repo, "common-file", "1\nb\nc\n", commitOptions{})
checkFatal(t, err)

checkFatal(t, repo.SetHead("refs/heads/branch-b"))
_, err = commitSomething(repo, "common-file", "x\nb\nc\n", commitOptions{})
checkFatal(t, err)

branchA, err := repo.LookupBranch("branch-a", BranchLocal)
checkFatal(t, err)
onto, err := repo.AnnotatedCommitFromRef(branchA.Reference)
checkFatal(t, err)

// We then rebase "branch-b" onto "branch-a" in-memory, which should result
// in a conflict.
rebase, err := repo.InitRebase(nil, nil, onto, &RebaseOptions{InMemory: 1})
checkFatal(t, err)

_, err = rebase.Next()
checkFatal(t, err)

index, err := rebase.InmemoryIndex()
checkFatal(t, err)

// We simply resolve the conflict and commit the rebase.
if !index.HasConflicts() {
t.Fatal("expected index to have conflicts")
}

conflict, err := index.Conflict("common-file")
checkFatal(t, err)

resolvedBlobID, err := repo.CreateBlobFromBuffer([]byte("resolved contents"))
checkFatal(t, err)

resolvedEntry := *conflict.Our
resolvedEntry.Id = resolvedBlobID
checkFatal(t, index.Add(&resolvedEntry))
checkFatal(t, index.RemoveConflict("common-file"))

var commitID Oid
checkFatal(t, rebase.Commit(&commitID, signature(), signature(), "rebased message"))
checkFatal(t, rebase.Finish())

// And then assert that we can look up the new merge commit, and that the
// "common-file" has the expected contents.
commit, err := repo.LookupCommit(&commitID)
checkFatal(t, err)
if commit.Message() != "rebased message" {
t.Fatalf("unexpected commit message %q", commit.Message())
}

tree, err := commit.Tree()
checkFatal(t, err)

blob, err := repo.LookupBlob(tree.EntryByName("common-file").Id)
checkFatal(t, err)
if string(blob.Contents()) != "resolved contents" {
t.Fatalf("unexpected resolved blob contents %q", string(blob.Contents()))
}
}

func TestRebaseAbort(t *testing.T) {
// TEST DATA

Expand Down