fork of go-git with some jj specific features
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Remove empty dirs when cleaning with Dir opt.

Signed-off-by: kuba-- <kuba@sourced.tech>

kuba-- 0167dabb 005d5dc9

+89 -32
+36 -18
storage/filesystem/dotgit/dotgit.go
··· 61 61 // type is not zero-value-safe, use the New function to initialize it. 62 62 type DotGit struct { 63 63 fs billy.Filesystem 64 + 65 + // incoming object directory information 66 + incomingChecked bool 67 + incomingDirName string 64 68 } 65 69 66 70 // New returns a DotGit value ready to be used. The path argument must ··· 279 283 return d.fs.Join(objectsPath, hash[0:2], hash[2:40]) 280 284 } 281 285 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 287 294 func (d *DotGit) incomingObjectPath(h plumbing.Hash) string { 288 295 hString := h.String() 289 - directoryContents, err := d.fs.ReadDir(objectsPath) 290 - if err != nil { 296 + 297 + if d.incomingDirName == "" { 291 298 return d.fs.Join(objectsPath, hString[0:2], hString[2:40]) 292 299 } 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 + } 297 315 } 316 + 317 + d.incomingChecked = true 298 318 } 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 != "" 303 321 } 304 322 305 323 // Object returns a fs.File pointing the object file, if exists 306 324 func (d *DotGit) Object(h plumbing.Hash) (billy.File, error) { 307 325 obj1, err1 := d.fs.Open(d.objectPath(h)) 308 - if os.IsNotExist(err1) { 326 + if os.IsNotExist(err1) && d.hasIncomingObjects() { 309 327 obj2, err2 := d.fs.Open(d.incomingObjectPath(h)) 310 328 if err2 != nil { 311 329 return obj1, err1 ··· 318 336 // ObjectStat returns a os.FileInfo pointing the object file, if exists 319 337 func (d *DotGit) ObjectStat(h plumbing.Hash) (os.FileInfo, error) { 320 338 obj1, err1 := d.fs.Stat(d.objectPath(h)) 321 - if os.IsNotExist(err1) { 339 + if os.IsNotExist(err1) && d.hasIncomingObjects() { 322 340 obj2, err2 := d.fs.Stat(d.incomingObjectPath(h)) 323 341 if err2 != nil { 324 342 return obj1, err1 ··· 331 349 // ObjectDelete removes the object file, if exists 332 350 func (d *DotGit) ObjectDelete(h plumbing.Hash) error { 333 351 err1 := d.fs.Remove(d.objectPath(h)) 334 - if os.IsNotExist(err1) { 352 + if os.IsNotExist(err1) && d.hasIncomingObjects() { 335 353 err2 := d.fs.Remove(d.incomingObjectPath(h)) 336 354 if err2 != nil { 337 355 return err1
+44 -14
worktree.go
··· 713 713 } 714 714 715 715 // Clean the worktree by removing untracked files. 716 + // An empty dir could be removed - this is what `git clean -f -d .` does. 716 717 func (w *Worktree) Clean(opts *CleanOptions) error { 717 718 s, err := w.Status() 718 719 if err != nil { 719 720 return err 720 721 } 721 722 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" { 727 734 continue 728 735 } 729 736 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 { 734 750 return err 735 751 } 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 + } 736 760 } 737 761 } 738 762 763 + if opts.Dir { 764 + return doCleanDirectories(w.Filesystem, dir) 765 + } 739 766 return nil 740 767 } 741 768 ··· 881 908 return err 882 909 } 883 910 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) 886 918 if err != nil { 887 919 return err 888 920 } 889 - 890 921 if len(files) == 0 { 891 - fs.Remove(path) 922 + return fs.Remove(dir) 892 923 } 893 - 894 924 return nil 895 925 }
+9
worktree_test.go
··· 1591 1591 1592 1592 c.Assert(len(status), Equals, 1) 1593 1593 1594 + fi, err := fs.Lstat("pkgA") 1595 + c.Assert(err, IsNil) 1596 + c.Assert(fi.IsDir(), Equals, true) 1597 + 1594 1598 // Clean with Dir: true. 1595 1599 err = wt.Clean(&CleanOptions{Dir: true}) 1596 1600 c.Assert(err, IsNil) ··· 1599 1603 c.Assert(err, IsNil) 1600 1604 1601 1605 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 + 1602 1611 } 1603 1612 1604 1613 func (s *WorktreeSuite) TestAlternatesRepo(c *C) {