diff --git a/worktree.go b/worktree.go index 2a498e1f4..3a47849db 100644 --- a/worktree.go +++ b/worktree.go @@ -309,13 +309,20 @@ func (w *Worktree) ResetSparsely(opts *ResetOptions, dirs []string) error { return err } + var removedFiles []string if opts.Mode == MixedReset || opts.Mode == MergeReset || opts.Mode == HardReset { - if err := w.resetIndex(t, dirs, opts.Files); err != nil { + if removedFiles, err = w.resetIndex(t, dirs, opts.Files); err != nil { return err } } - if opts.Mode == MergeReset || opts.Mode == HardReset { + if opts.Mode == MergeReset && len(removedFiles) > 0 { + if err := w.resetWorktree(t, removedFiles); err != nil { + return err + } + } + + if opts.Mode == HardReset { if err := w.resetWorktree(t, opts.Files); err != nil { return err } @@ -369,23 +376,24 @@ func (w *Worktree) Reset(opts *ResetOptions) error { return w.ResetSparsely(opts, nil) } -func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) error { +func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) ([]string, error) { idx, err := w.r.Storer.Index() if err != nil { - return err + return nil, err } b := newIndexBuilder(idx) changes, err := w.diffTreeWithStaging(t, true) if err != nil { - return err + return nil, err } + var removedFiles []string for _, ch := range changes { a, err := ch.Action() if err != nil { - return err + return nil, err } var name string @@ -396,7 +404,7 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) err name = ch.To.String() e, err = t.FindEntry(name) if err != nil { - return err + return nil, err } case merkletrie.Delete: name = ch.From.String() @@ -410,6 +418,7 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) err } b.Remove(name) + removedFiles = append(removedFiles, name) if e == nil { continue } @@ -428,7 +437,7 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) err idx.SkipUnless(dirs) } - return w.r.Storer.SetIndex(idx) + return removedFiles, w.r.Storer.SetIndex(idx) } func inFiles(files []string, v string) bool { diff --git a/worktree_test.go b/worktree_test.go index c550205c1..b85f4d8c3 100644 --- a/worktree_test.go +++ b/worktree_test.go @@ -764,6 +764,39 @@ func (s *WorktreeSuite) TestCheckoutBranch() { s.True(status.IsClean()) } +func (s *WorktreeSuite) TestCheckoutBranchUntracked() { + w := &Worktree{ + r: s.Repository, + Filesystem: memfs.New(), + } + + uf, err := w.Filesystem.Create("untracked_file") + s.NoError(err) + _, err = uf.Write([]byte("don't delete me")) + s.NoError(err) + + err = w.Checkout(&CheckoutOptions{ + Branch: "refs/heads/branch", + }) + s.NoError(err) + + head, err := w.r.Head() + s.NoError(err) + s.Equal("refs/heads/branch", head.Name().String()) + + status, err := w.Status() + s.NoError(err) + // The untracked file should still be there, so it's not clean + s.False(status.IsClean()) + s.True(status.IsUntracked("untracked_file")) + err = w.Filesystem.Remove("untracked_file") + s.NoError(err) + status, err = w.Status() + s.NoError(err) + // After deleting the untracked file it should now be clean + s.True(status.IsClean()) +} + func (s *WorktreeSuite) TestCheckoutCreateWithHash() { w := &Worktree{ r: s.Repository, @@ -1130,15 +1163,28 @@ func (s *WorktreeSuite) TestResetWithUntracked() { err := w.Checkout(&CheckoutOptions{}) s.NoError(err) - err = util.WriteFile(fs, "foo", nil, 0o755) + err = util.WriteFile(fs, "foo", []byte("bar"), 0o755) s.NoError(err) err = w.Reset(&ResetOptions{Mode: MergeReset, Commit: commit}) s.NoError(err) + contents, err := util.ReadFile(fs, "foo") + s.NoError(err) + s.Equal("bar", string(contents)) + status, err := w.Status() s.NoError(err) - s.True(status.IsClean()) + for file, st := range status { + if file == "foo" { + s.Equal(Untracked, st.Worktree) + s.Equal(Untracked, st.Staging) + continue + } + if st.Worktree != Unmodified || st.Staging != Unmodified { + s.Failf("file %s not unmodified", file) + } + } } func (s *WorktreeSuite) TestResetSoft() {