| package git |
| |
| import ( |
| "fmt" |
| "io/ioutil" |
| "os" |
| "path" |
| "reflect" |
| "runtime" |
| "testing" |
| "time" |
| ) |
| |
| func TestStash(t *testing.T) { |
| repo := createTestRepo(t) |
| defer cleanupTestRepo(t, repo) |
| |
| prepareStashRepo(t, repo) |
| |
| sig := &Signature{ |
| Name: "Rand Om Hacker", |
| Email: "random@hacker.com", |
| When: time.Now(), |
| } |
| |
| stash1, err := repo.Stashes.Save(sig, "First stash", StashDefault) |
| checkFatal(t, err) |
| |
| _, err = repo.LookupCommit(stash1) |
| checkFatal(t, err) |
| |
| b, err := ioutil.ReadFile(pathInRepo(repo, "README")) |
| checkFatal(t, err) |
| if string(b) == "Update README goes to stash\n" { |
| t.Errorf("README still contains the uncommitted changes") |
| } |
| |
| if !fileExistsInRepo(repo, "untracked.txt") { |
| t.Errorf("untracked.txt doesn't exist in the repo; should be untracked") |
| } |
| |
| // Apply: default |
| |
| opts, err := DefaultStashApplyOptions() |
| checkFatal(t, err) |
| |
| err = repo.Stashes.Apply(0, opts) |
| checkFatal(t, err) |
| |
| b, err = ioutil.ReadFile(pathInRepo(repo, "README")) |
| checkFatal(t, err) |
| if string(b) != "Update README goes to stash\n" { |
| t.Errorf("README changes aren't here") |
| } |
| |
| // Apply: no stash for the given index |
| |
| err = repo.Stashes.Apply(1, opts) |
| if !IsErrorCode(err, ErrNotFound) { |
| t.Errorf("expecting GIT_ENOTFOUND error code %d, got %v", ErrNotFound, err) |
| } |
| |
| // Apply: callback stopped |
| |
| opts.ProgressCallback = func(progress StashApplyProgress) error { |
| if progress == StashApplyProgressCheckoutModified { |
| return fmt.Errorf("Stop") |
| } |
| return nil |
| } |
| |
| err = repo.Stashes.Apply(0, opts) |
| if err.Error() != "Stop" { |
| t.Errorf("expecting error 'Stop', got %v", err) |
| } |
| |
| // Create second stash with ignored files |
| |
| os.MkdirAll(pathInRepo(repo, "tmp"), os.ModeDir|os.ModePerm) |
| err = ioutil.WriteFile(pathInRepo(repo, "tmp/ignored.txt"), []byte("Ignore me\n"), 0644) |
| checkFatal(t, err) |
| |
| stash2, err := repo.Stashes.Save(sig, "Second stash", StashIncludeIgnored) |
| checkFatal(t, err) |
| |
| if fileExistsInRepo(repo, "tmp/ignored.txt") { |
| t.Errorf("tmp/ignored.txt should not exist anymore in the work dir") |
| } |
| |
| // Stash foreach |
| |
| expected := []stash{ |
| {0, "On master: Second stash", stash2.String()}, |
| {1, "On master: First stash", stash1.String()}, |
| } |
| checkStashes(t, repo, expected) |
| |
| // Stash pop |
| |
| opts, _ = DefaultStashApplyOptions() |
| err = repo.Stashes.Pop(1, opts) |
| checkFatal(t, err) |
| |
| b, err = ioutil.ReadFile(pathInRepo(repo, "README")) |
| checkFatal(t, err) |
| if string(b) != "Update README goes to stash\n" { |
| t.Errorf("README changes aren't here") |
| } |
| |
| expected = []stash{ |
| {0, "On master: Second stash", stash2.String()}, |
| } |
| checkStashes(t, repo, expected) |
| |
| // Stash drop |
| |
| err = repo.Stashes.Drop(0) |
| checkFatal(t, err) |
| |
| expected = []stash{} |
| checkStashes(t, repo, expected) |
| } |
| |
| type stash struct { |
| index int |
| msg string |
| id string |
| } |
| |
| func checkStashes(t *testing.T, repo *Repository, expected []stash) { |
| var actual []stash |
| |
| repo.Stashes.Foreach(func(index int, msg string, id *Oid) error { |
| stash := stash{index, msg, id.String()} |
| if len(expected) > len(actual) { |
| if s := expected[len(actual)]; s.id == "" { |
| stash.id = "" // don't check id |
| } |
| } |
| actual = append(actual, stash) |
| return nil |
| }) |
| |
| if len(expected) > 0 && !reflect.DeepEqual(expected, actual) { |
| // The failure happens at wherever we were called, not here |
| _, file, line, ok := runtime.Caller(1) |
| if !ok { |
| t.Fatalf("Unable to get caller") |
| } |
| t.Errorf("%v:%v: expecting %#v\ngot %#v", path.Base(file), line, expected, actual) |
| } |
| } |
| |
| func prepareStashRepo(t *testing.T, repo *Repository) { |
| seedTestRepo(t, repo) |
| |
| err := ioutil.WriteFile(pathInRepo(repo, ".gitignore"), []byte("tmp\n"), 0644) |
| checkFatal(t, err) |
| |
| sig := &Signature{ |
| Name: "Rand Om Hacker", |
| Email: "random@hacker.com", |
| When: time.Now(), |
| } |
| |
| idx, err := repo.Index() |
| checkFatal(t, err) |
| err = idx.AddByPath(".gitignore") |
| checkFatal(t, err) |
| treeID, err := idx.WriteTree() |
| checkFatal(t, err) |
| err = idx.Write() |
| checkFatal(t, err) |
| |
| currentBranch, err := repo.Head() |
| checkFatal(t, err) |
| currentTip, err := repo.LookupCommit(currentBranch.Target()) |
| checkFatal(t, err) |
| |
| message := "Add .gitignore\n" |
| tree, err := repo.LookupTree(treeID) |
| checkFatal(t, err) |
| _, err = repo.CreateCommit("HEAD", sig, sig, message, tree, currentTip) |
| checkFatal(t, err) |
| |
| err = ioutil.WriteFile(pathInRepo(repo, "README"), []byte("Update README goes to stash\n"), 0644) |
| checkFatal(t, err) |
| |
| err = ioutil.WriteFile(pathInRepo(repo, "untracked.txt"), []byte("Hello, World\n"), 0644) |
| checkFatal(t, err) |
| } |
| |
| func fileExistsInRepo(repo *Repository, name string) bool { |
| if _, err := os.Stat(pathInRepo(repo, name)); err != nil { |
| return false |
| } |
| return true |
| } |