Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f848aaf

Browse files
authored
git: worktree: check for empty parent dirs during Reset (Fixes #670) (#671)
When we delete dir1/dir2/file1, we currently check if dir2 becomes empty with the deletion of file1, and if so, we delete dir2. If dir1 becomes empty with the deletion of dir2, we don't notice that, and dir1 is left behind. This commit adds a loop to check each parent directory in the file path for emptiness, removing empty directories along the way until a non-empty directory is found (or an error occurs).
1 parent 5dabd83 commit f848aaf

File tree

2 files changed

+90
-10
lines changed

2 files changed

+90
-10
lines changed

worktree.go

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ func (w *Worktree) checkoutChange(ch merkletrie.Change, t *object.Tree, idx *ind
410410

411411
isSubmodule = e.Mode == filemode.Submodule
412412
case merkletrie.Delete:
413-
return rmFileAndDirIfEmpty(w.Filesystem, ch.From.String())
413+
return rmFileAndDirsIfEmpty(w.Filesystem, ch.From.String())
414414
}
415415

416416
if isSubmodule {
@@ -778,8 +778,10 @@ func (w *Worktree) doClean(status Status, opts *CleanOptions, dir string, files
778778
}
779779

780780
if opts.Dir && dir != "" {
781-
return doCleanDirectories(w.Filesystem, dir)
781+
_, err := removeDirIfEmpty(w.Filesystem, dir)
782+
return err
782783
}
784+
783785
return nil
784786
}
785787

@@ -920,25 +922,52 @@ func findMatchInFile(file *object.File, treeName string, opts *GrepOptions) ([]G
920922
return grepResults, nil
921923
}
922924

923-
func rmFileAndDirIfEmpty(fs billy.Filesystem, name string) error {
925+
// will walk up the directory tree removing all encountered empty
926+
// directories, not just the one containing this file
927+
func rmFileAndDirsIfEmpty(fs billy.Filesystem, name string) error {
924928
if err := util.RemoveAll(fs, name); err != nil {
925929
return err
926930
}
927931

928932
dir := filepath.Dir(name)
929-
return doCleanDirectories(fs, dir)
933+
for {
934+
removed, err := removeDirIfEmpty(fs, dir)
935+
if err != nil {
936+
return err
937+
}
938+
939+
if !removed {
940+
// directory was not empty and not removed,
941+
// stop checking parents
942+
break
943+
}
944+
945+
// move to parent directory
946+
dir = filepath.Dir(dir)
947+
}
948+
949+
return nil
930950
}
931951

932-
// doCleanDirectories removes empty subdirs (without files)
933-
func doCleanDirectories(fs billy.Filesystem, dir string) error {
952+
// removeDirIfEmpty will remove the supplied directory `dir` if
953+
// `dir` is empty
954+
// returns true if the directory was removed
955+
func removeDirIfEmpty(fs billy.Filesystem, dir string) (bool, error) {
934956
files, err := fs.ReadDir(dir)
935957
if err != nil {
936-
return err
958+
return false, err
937959
}
938-
if len(files) == 0 {
939-
return fs.Remove(dir)
960+
961+
if len(files) > 0 {
962+
return false, nil
940963
}
941-
return nil
964+
965+
err = fs.Remove(dir)
966+
if err != nil {
967+
return false, err
968+
}
969+
970+
return true, nil
942971
}
943972

944973
type indexBuilder struct {

worktree_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package git
33
import (
44
"bytes"
55
"context"
6+
"errors"
67
"io"
78
"io/ioutil"
89
"os"
@@ -2210,6 +2211,56 @@ func (s *WorktreeSuite) TestGrep(c *C) {
22102211
}
22112212
}
22122213

2214+
func (s *WorktreeSuite) TestResetLingeringDirectories(c *C) {
2215+
dir, clean := s.TemporalDir()
2216+
defer clean()
2217+
2218+
commitOpts := &CommitOptions{Author: &object.Signature{
2219+
Name: "foo",
2220+
2221+
When: time.Now(),
2222+
}}
2223+
2224+
repo, err := PlainInit(dir, false)
2225+
c.Assert(err, IsNil)
2226+
2227+
w, err := repo.Worktree()
2228+
c.Assert(err, IsNil)
2229+
2230+
os.WriteFile(filepath.Join(dir, "README"), []byte("placeholder"), 0o644)
2231+
2232+
_, err = w.Add(".")
2233+
c.Assert(err, IsNil)
2234+
2235+
initialHash, err := w.Commit("Initial commit", commitOpts)
2236+
c.Assert(err, IsNil)
2237+
2238+
os.MkdirAll(filepath.Join(dir, "a", "b"), 0o755)
2239+
os.WriteFile(filepath.Join(dir, "a", "b", "1"), []byte("1"), 0o644)
2240+
2241+
_, err = w.Add(".")
2242+
c.Assert(err, IsNil)
2243+
2244+
_, err = w.Commit("Add file in nested sub-directories", commitOpts)
2245+
c.Assert(err, IsNil)
2246+
2247+
// reset to initial commit, which should remove a/b/1, a/b, and a
2248+
err = w.Reset(&ResetOptions{
2249+
Commit: initialHash,
2250+
Mode: HardReset,
2251+
})
2252+
c.Assert(err, IsNil)
2253+
2254+
_, err = os.Stat(filepath.Join(dir, "a", "b", "1"))
2255+
c.Assert(errors.Is(err, os.ErrNotExist), Equals, true)
2256+
2257+
_, err = os.Stat(filepath.Join(dir, "a", "b"))
2258+
c.Assert(errors.Is(err, os.ErrNotExist), Equals, true)
2259+
2260+
_, err = os.Stat(filepath.Join(dir, "a"))
2261+
c.Assert(errors.Is(err, os.ErrNotExist), Equals, true)
2262+
}
2263+
22132264
func (s *WorktreeSuite) TestAddAndCommit(c *C) {
22142265
expectedFiles := 2
22152266

0 commit comments

Comments
 (0)