| package git |
| |
| import ( |
| "errors" |
| "strconv" |
| "testing" |
| "time" |
| ) |
| |
| // Tests |
| |
| func TestRebaseAbort(t *testing.T) { |
| // TEST DATA |
| |
| // Inputs |
| branchName := "emile" |
| masterCommit := "something" |
| emileCommits := []string{ |
| "fou", |
| "barre", |
| } |
| |
| // Outputs |
| expectedHistory := []string{ |
| "Test rebase, Baby! " + emileCommits[1], |
| "Test rebase, Baby! " + emileCommits[0], |
| "This is a commit\n", |
| } |
| |
| // TEST |
| repo := createTestRepo(t) |
| seedTestRepo(t, repo) |
| |
| // Setup a repo with 2 branches and a different tree |
| err := setupRepoForRebase(repo, masterCommit, branchName) |
| checkFatal(t, err) |
| defer cleanupTestRepo(t, repo) |
| |
| // Create several commits in emile |
| for _, commit := range emileCommits { |
| _, err = commitSomething(repo, commit, commit) |
| checkFatal(t, err) |
| } |
| |
| // Check history |
| actualHistory, err := commitMsgsList(repo) |
| checkFatal(t, err) |
| assertStringList(t, expectedHistory, actualHistory) |
| |
| // Rebase onto master |
| rebase, err := performRebaseOnto(repo, "master") |
| checkFatal(t, err) |
| defer rebase.Free() |
| |
| // Abort rebase |
| rebase.Abort() |
| |
| // Check history is still the same |
| actualHistory, err = commitMsgsList(repo) |
| checkFatal(t, err) |
| assertStringList(t, expectedHistory, actualHistory) |
| } |
| |
| func TestRebaseNoConflicts(t *testing.T) { |
| // TEST DATA |
| |
| // Inputs |
| branchName := "emile" |
| masterCommit := "something" |
| emileCommits := []string{ |
| "fou", |
| "barre", |
| "ouich", |
| } |
| |
| // Outputs |
| expectedHistory := []string{ |
| "Test rebase, Baby! " + emileCommits[2], |
| "Test rebase, Baby! " + emileCommits[1], |
| "Test rebase, Baby! " + emileCommits[0], |
| "Test rebase, Baby! " + masterCommit, |
| "This is a commit\n", |
| } |
| |
| // TEST |
| repo := createTestRepo(t) |
| seedTestRepo(t, repo) |
| |
| // Setup a repo with 2 branches and a different tree |
| err := setupRepoForRebase(repo, masterCommit, branchName) |
| checkFatal(t, err) |
| defer cleanupTestRepo(t, repo) |
| |
| // Create several commits in emile |
| for _, commit := range emileCommits { |
| _, err = commitSomething(repo, commit, commit) |
| checkFatal(t, err) |
| } |
| |
| // Rebase onto master |
| rebase, err := performRebaseOnto(repo, "master") |
| checkFatal(t, err) |
| defer rebase.Free() |
| |
| // Finish the rebase properly |
| err = rebase.Finish() |
| checkFatal(t, err) |
| |
| // Check history is in correct order |
| actualHistory, err := commitMsgsList(repo) |
| checkFatal(t, err) |
| assertStringList(t, expectedHistory, actualHistory) |
| |
| } |
| |
| // Utils |
| func setupRepoForRebase(repo *Repository, masterCommit, branchName string) error { |
| // Create a new branch from master |
| err := createBranch(repo, branchName) |
| if err != nil { |
| return err |
| } |
| |
| // Create a commit in master |
| _, err = commitSomething(repo, masterCommit, masterCommit) |
| if err != nil { |
| return err |
| } |
| |
| // Switch to emile |
| err = repo.SetHead("refs/heads/" + branchName) |
| if err != nil { |
| return err |
| } |
| |
| // Check master commit is not in emile branch |
| if entryExists(repo, masterCommit) { |
| return errors.New(masterCommit + " entry should not exist in " + branchName + " branch.") |
| } |
| |
| return nil |
| } |
| |
| func performRebaseOnto(repo *Repository, branch string) (*Rebase, error) { |
| master, err := repo.LookupBranch(branch, BranchLocal) |
| if err != nil { |
| return nil, err |
| } |
| defer master.Free() |
| |
| onto, err := repo.AnnotatedCommitFromRef(master.Reference) |
| if err != nil { |
| return nil, err |
| } |
| defer onto.Free() |
| |
| rebase, err := repo.RebaseInit(nil, nil, onto, nil) |
| if err != nil { |
| return nil, err |
| } |
| |
| opCount := int(rebase.OperationCount()) |
| |
| for op := 0; op < opCount; op++ { |
| operation, err := rebase.Next() |
| if err != nil { |
| return nil, err |
| } |
| |
| commit, err := repo.LookupCommit(operation.ID) |
| if err != nil { |
| return nil, err |
| } |
| defer commit.Free() |
| |
| err = rebase.Commit(operation.ID, signature(), signature(), commit.Message()) |
| if err != nil { |
| return nil, err |
| } |
| } |
| |
| return rebase, nil |
| } |
| |
| func createBranch(repo *Repository, branch string) error { |
| commit, err := headCommit(repo) |
| if err != nil { |
| return err |
| } |
| defer commit.Free() |
| _, err = repo.CreateBranch(branch, commit, false) |
| if err != nil { |
| return err |
| } |
| |
| return nil |
| } |
| |
| func signature() *Signature { |
| return &Signature{ |
| Name: "Emile", |
| Email: "emile@emile.com", |
| When: time.Now(), |
| } |
| } |
| |
| func headCommit(repo *Repository) (*Commit, error) { |
| head, err := repo.Head() |
| if err != nil { |
| return nil, err |
| } |
| defer head.Free() |
| |
| commit, err := repo.LookupCommit(head.Target()) |
| if err != nil { |
| return nil, err |
| } |
| |
| return commit, nil |
| } |
| |
| func headTree(repo *Repository) (*Tree, error) { |
| headCommit, err := headCommit(repo) |
| if err != nil { |
| return nil, err |
| } |
| defer headCommit.Free() |
| |
| tree, err := headCommit.Tree() |
| if err != nil { |
| return nil, err |
| } |
| |
| return tree, nil |
| } |
| |
| func commitSomething(repo *Repository, something, content string) (*Oid, error) { |
| headCommit, err := headCommit(repo) |
| if err != nil { |
| return nil, err |
| } |
| defer headCommit.Free() |
| |
| index, err := NewIndex() |
| if err != nil { |
| return nil, err |
| } |
| defer index.Free() |
| |
| blobOID, err := repo.CreateBlobFromBuffer([]byte(content)) |
| if err != nil { |
| return nil, err |
| } |
| |
| entry := &IndexEntry{ |
| Mode: FilemodeBlob, |
| Id: blobOID, |
| Path: something, |
| } |
| |
| if err := index.Add(entry); err != nil { |
| return nil, err |
| } |
| |
| newTreeOID, err := index.WriteTreeTo(repo) |
| if err != nil { |
| return nil, err |
| } |
| |
| newTree, err := repo.LookupTree(newTreeOID) |
| if err != nil { |
| return nil, err |
| } |
| defer newTree.Free() |
| |
| if err != nil { |
| return nil, err |
| } |
| commit, err := repo.CreateCommit("HEAD", signature(), signature(), "Test rebase, Baby! "+something, newTree, headCommit) |
| if err != nil { |
| return nil, err |
| } |
| |
| opts := &CheckoutOpts{ |
| Strategy: CheckoutRemoveUntracked | CheckoutForce, |
| } |
| err = repo.CheckoutIndex(index, opts) |
| if err != nil { |
| return nil, err |
| } |
| |
| return commit, nil |
| } |
| |
| func entryExists(repo *Repository, file string) bool { |
| headTree, err := headTree(repo) |
| if err != nil { |
| return false |
| } |
| defer headTree.Free() |
| |
| _, err = headTree.EntryByPath(file) |
| |
| return err == nil |
| } |
| |
| func commitMsgsList(repo *Repository) ([]string, error) { |
| head, err := headCommit(repo) |
| if err != nil { |
| return nil, err |
| } |
| defer head.Free() |
| |
| var commits []string |
| |
| parent := head.Parent(0) |
| defer parent.Free() |
| commits = append(commits, head.Message(), parent.Message()) |
| |
| for parent.ParentCount() != 0 { |
| parent = parent.Parent(0) |
| defer parent.Free() |
| commits = append(commits, parent.Message()) |
| } |
| |
| return commits, nil |
| } |
| |
| func assertStringList(t *testing.T, expected, actual []string) { |
| if len(expected) != len(actual) { |
| t.Fatal("Lists are not the same size, expected " + strconv.Itoa(len(expected)) + |
| ", got " + strconv.Itoa(len(actual))) |
| } |
| for index, element := range expected { |
| if element != actual[index] { |
| t.Error("Expected element " + strconv.Itoa(index) + " to be " + element + ", got " + actual[index]) |
| } |
| } |
| } |