Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit 0167dab

Browse files
author
kuba--
committed
Remove empty dirs when cleaning with Dir opt.
Signed-off-by: kuba-- <kuba@sourced.tech>
1 parent 005d5dc commit 0167dab

File tree

3 files changed

+89
-32
lines changed

3 files changed

+89
-32
lines changed

storage/filesystem/dotgit/dotgit.go

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ var (
6161
// type is not zero-value-safe, use the New function to initialize it.
6262
type DotGit struct {
6363
fs billy.Filesystem
64+
65+
// incoming object directory information
66+
incomingChecked bool
67+
incomingDirName string
6468
}
6569

6670
// New returns a DotGit value ready to be used. The path argument must
@@ -279,33 +283,47 @@ func (d *DotGit) objectPath(h plumbing.Hash) string {
279283
return d.fs.Join(objectsPath, hash[0:2], hash[2:40])
280284
}
281285

282-
//incomingObjectPath is intended to add support for a git pre-recieve hook to be written
283-
//it adds support for go-git to find objects in an "incoming" directory, so that the library
284-
//can be used to write a pre-recieve hook that deals with the incoming objects.
285-
//More on git hooks found here : https://git-scm.com/docs/githooks
286-
//More on 'quarantine'/incoming directory here : https://git-scm.com/docs/git-receive-pack
286+
// incomingObjectPath is intended to add support for a git pre-receive hook
287+
// to be written it adds support for go-git to find objects in an "incoming"
288+
// directory, so that the library can be used to write a pre-receive hook
289+
// that deals with the incoming objects.
290+
//
291+
// More on git hooks found here : https://git-scm.com/docs/githooks
292+
// More on 'quarantine'/incoming directory here:
293+
// https://git-scm.com/docs/git-receive-pack
287294
func (d *DotGit) incomingObjectPath(h plumbing.Hash) string {
288295
hString := h.String()
289-
directoryContents, err := d.fs.ReadDir(objectsPath)
290-
if err != nil {
296+
297+
if d.incomingDirName == "" {
291298
return d.fs.Join(objectsPath, hString[0:2], hString[2:40])
292299
}
293-
var incomingDirName string
294-
for _, file := range directoryContents {
295-
if strings.Split(file.Name(), "-")[0] == "incoming" && file.IsDir() {
296-
incomingDirName = file.Name()
300+
301+
return d.fs.Join(objectsPath, d.incomingDirName, hString[0:2], hString[2:40])
302+
}
303+
304+
// hasIncomingObjects searches for an incoming directory and keeps its name
305+
// so it doesn't have to be found each time an object is accessed.
306+
func (d *DotGit) hasIncomingObjects() bool {
307+
if !d.incomingChecked {
308+
directoryContents, err := d.fs.ReadDir(objectsPath)
309+
if err == nil {
310+
for _, file := range directoryContents {
311+
if strings.HasPrefix(file.Name(), "incoming-") && file.IsDir() {
312+
d.incomingDirName = file.Name()
313+
}
314+
}
297315
}
316+
317+
d.incomingChecked = true
298318
}
299-
if incomingDirName == "" {
300-
return d.fs.Join(objectsPath, hString[0:2], hString[2:40])
301-
}
302-
return d.fs.Join(objectsPath, incomingDirName, hString[0:2], hString[2:40])
319+
320+
return d.incomingDirName != ""
303321
}
304322

305323
// Object returns a fs.File pointing the object file, if exists
306324
func (d *DotGit) Object(h plumbing.Hash) (billy.File, error) {
307325
obj1, err1 := d.fs.Open(d.objectPath(h))
308-
if os.IsNotExist(err1) {
326+
if os.IsNotExist(err1) && d.hasIncomingObjects() {
309327
obj2, err2 := d.fs.Open(d.incomingObjectPath(h))
310328
if err2 != nil {
311329
return obj1, err1
@@ -318,7 +336,7 @@ func (d *DotGit) Object(h plumbing.Hash) (billy.File, error) {
318336
// ObjectStat returns a os.FileInfo pointing the object file, if exists
319337
func (d *DotGit) ObjectStat(h plumbing.Hash) (os.FileInfo, error) {
320338
obj1, err1 := d.fs.Stat(d.objectPath(h))
321-
if os.IsNotExist(err1) {
339+
if os.IsNotExist(err1) && d.hasIncomingObjects() {
322340
obj2, err2 := d.fs.Stat(d.incomingObjectPath(h))
323341
if err2 != nil {
324342
return obj1, err1
@@ -331,7 +349,7 @@ func (d *DotGit) ObjectStat(h plumbing.Hash) (os.FileInfo, error) {
331349
// ObjectDelete removes the object file, if exists
332350
func (d *DotGit) ObjectDelete(h plumbing.Hash) error {
333351
err1 := d.fs.Remove(d.objectPath(h))
334-
if os.IsNotExist(err1) {
352+
if os.IsNotExist(err1) && d.hasIncomingObjects() {
335353
err2 := d.fs.Remove(d.incomingObjectPath(h))
336354
if err2 != nil {
337355
return err1

worktree.go

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -713,29 +713,56 @@ func (w *Worktree) readGitmodulesFile() (*config.Modules, error) {
713713
}
714714

715715
// Clean the worktree by removing untracked files.
716+
// An empty dir could be removed - this is what `git clean -f -d .` does.
716717
func (w *Worktree) Clean(opts *CleanOptions) error {
717718
s, err := w.Status()
718719
if err != nil {
719720
return err
720721
}
721722

722-
// Check Worktree status to be Untracked, obtain absolute path and delete.
723-
for relativePath, status := range s {
724-
// Check if the path contains a directory and if Dir options is false,
725-
// skip the path.
726-
if relativePath != filepath.Base(relativePath) && !opts.Dir {
723+
root := ""
724+
files, err := w.Filesystem.ReadDir(root)
725+
if err != nil {
726+
return err
727+
}
728+
return w.doClean(s, opts, root, files)
729+
}
730+
731+
func (w *Worktree) doClean(status Status, opts *CleanOptions, dir string, files []os.FileInfo) error {
732+
for _, fi := range files {
733+
if fi.Name() == ".git" {
727734
continue
728735
}
729736

730-
// Remove the file only if it's an untracked file.
731-
if status.Worktree == Untracked {
732-
absPath := filepath.Join(w.Filesystem.Root(), relativePath)
733-
if err := os.Remove(absPath); err != nil {
737+
// relative path under the root
738+
path := filepath.Join(dir, fi.Name())
739+
if fi.IsDir() {
740+
if !opts.Dir {
741+
continue
742+
}
743+
744+
subfiles, err := w.Filesystem.ReadDir(path)
745+
if err != nil {
746+
return err
747+
}
748+
err = w.doClean(status, opts, path, subfiles)
749+
if err != nil {
734750
return err
735751
}
752+
} else {
753+
// check if file is 'Untracked'
754+
s, ok := (status)[filepath.ToSlash(path)]
755+
if ok && s.Worktree == Untracked {
756+
if err := w.Filesystem.Remove(path); err != nil {
757+
return err
758+
}
759+
}
736760
}
737761
}
738762

763+
if opts.Dir {
764+
return doCleanDirectories(w.Filesystem, dir)
765+
}
739766
return nil
740767
}
741768

@@ -881,15 +908,18 @@ func rmFileAndDirIfEmpty(fs billy.Filesystem, name string) error {
881908
return err
882909
}
883910

884-
path := filepath.Dir(name)
885-
files, err := fs.ReadDir(path)
911+
dir := filepath.Dir(name)
912+
return doCleanDirectories(fs, dir)
913+
}
914+
915+
// doCleanDirectories removes empty subdirs (without files)
916+
func doCleanDirectories(fs billy.Filesystem, dir string) error {
917+
files, err := fs.ReadDir(dir)
886918
if err != nil {
887919
return err
888920
}
889-
890921
if len(files) == 0 {
891-
fs.Remove(path)
922+
return fs.Remove(dir)
892923
}
893-
894924
return nil
895925
}

worktree_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,6 +1591,10 @@ func (s *WorktreeSuite) TestClean(c *C) {
15911591

15921592
c.Assert(len(status), Equals, 1)
15931593

1594+
fi, err := fs.Lstat("pkgA")
1595+
c.Assert(err, IsNil)
1596+
c.Assert(fi.IsDir(), Equals, true)
1597+
15941598
// Clean with Dir: true.
15951599
err = wt.Clean(&CleanOptions{Dir: true})
15961600
c.Assert(err, IsNil)
@@ -1599,6 +1603,11 @@ func (s *WorktreeSuite) TestClean(c *C) {
15991603
c.Assert(err, IsNil)
16001604

16011605
c.Assert(len(status), Equals, 0)
1606+
1607+
// An empty dir should be deleted, as well.
1608+
_, err = fs.Lstat("pkgA")
1609+
c.Assert(err, ErrorMatches, ".*(no such file or directory.*|.*file does not exist)*.")
1610+
16021611
}
16031612

16041613
func (s *WorktreeSuite) TestAlternatesRepo(c *C) {

0 commit comments

Comments
 (0)