55 "errors"
66 "io"
77 "os"
8+ "path"
9+ "path/filepath"
810
911 "gopkg.in/src-d/go-billy.v4/util"
1012 "gopkg.in/src-d/go-git.v4/plumbing"
2426 // the worktree.
2527 ErrDestinationExists = errors .New ("destination exists" )
2628 // ErrGlobNoMatches in an AddGlob if the glob pattern does not match any
27- // file in the worktree.ErrNotDirectory
29+ // files in the worktree.
2830 ErrGlobNoMatches = errors .New ("glob pattern did not match any files" )
29- // ErrNotDirectory in an AddDirectory if the path is not a directory.
30- ErrNotDirectory = errors .New ("path is not a directory" )
3131)
3232
3333// Status returns the working tree status.
@@ -252,8 +252,12 @@ func diffTreeIsEquals(a, b noder.Hasher) bool {
252252
253253// Add adds the file contents of a file in the worktree to the index. if the
254254// file is already staged in the index no error is returned. If a file deleted
255- // from the Workspace is given, the file is removed from the index.
255+ // from the Workspace is given, the file is removed from the index. If a
256+ // directory given, adds the files and all his sub-directories recursively in
257+ // the worktree to the index. If any of the files is already staged in the index
258+ // no error is returned. When path is a file, the blob.Hash is returned.
256259func (w * Worktree ) Add (path string ) (plumbing.Hash , error ) {
260+ // TODO(mcuadros): remove plumbing.Hash from signature at v5.
257261 s , err := w .Status ()
258262 if err != nil {
259263 return plumbing .ZeroHash , err
@@ -264,67 +268,41 @@ func (w *Worktree) Add(path string) (plumbing.Hash, error) {
264268 return plumbing .ZeroHash , err
265269 }
266270
267- added , h , err := w .doAdd (idx , s , path )
268- if err != nil {
269- return h , err
270- }
271-
272- if ! added {
273- return h , nil
274- }
271+ var h plumbing.Hash
272+ var added bool
275273
276- return h , w .r .Storer .SetIndex (idx )
277- }
278-
279- // AddDirectory adds the files contents of a directory and all his
280- // sub-directories recursively in the worktree to the index. If any of the
281- // file is already staged in the index no error is returned.
282- func (w * Worktree ) AddDirectory (path string ) error {
283274 fi , err := w .Filesystem .Lstat (path )
284- if err != nil {
285- return err
286- }
287-
288- if ! fi .IsDir () {
289- return ErrNotDirectory
290- }
291-
292- s , err := w .Status ()
293- if err != nil {
294- return err
295- }
296-
297- idx , err := w .r .Storer .Index ()
298- if err != nil {
299- return err
275+ if err != nil || ! fi .IsDir () {
276+ added , h , err = w .doAddFile (idx , s , path )
277+ } else {
278+ added , err = w .doAddDirectory (idx , s , path )
300279 }
301280
302- added , err := w .doAddDirectory (idx , s , path )
303281 if err != nil {
304- return err
282+ return h , err
305283 }
306284
307285 if ! added {
308- return nil
286+ return h , nil
309287 }
310288
311- return w .r .Storer .SetIndex (idx )
289+ return h , w .r .Storer .SetIndex (idx )
312290}
313291
314- func (w * Worktree ) doAddDirectory (idx * index.Index , s Status , path string ) (added bool , err error ) {
315- files , err := w .Filesystem .ReadDir (path )
292+ func (w * Worktree ) doAddDirectory (idx * index.Index , s Status , directory string ) (added bool , err error ) {
293+ files , err := w .Filesystem .ReadDir (directory )
316294 if err != nil {
317295 return false , err
318296 }
319297
320298 for _ , file := range files {
321- name := w . Filesystem . Join (path , file .Name ())
299+ name := path . Join (directory , file .Name ())
322300
323301 var a bool
324302 if file .IsDir () {
325303 a , err = w .doAddDirectory (idx , s , name )
326304 } else {
327- a , _ , err = w .doAdd (idx , s , name )
305+ a , _ , err = w .doAddFile (idx , s , name )
328306 }
329307
330308 if err != nil {
@@ -337,12 +315,11 @@ func (w *Worktree) doAddDirectory(idx *index.Index, s Status, path string) (adde
337315 }
338316
339317 return
340-
341318}
342319
343- // AddGlob given a glob pattern adds all the matching files content and all his
344- // sub-directories recursively in the worktree to the index. If any of the
345- // file is already staged in the index no error is returned .
320+ // AddGlob adds all paths, matching pattern, to the index. If pattern matches a
321+ // directory path, all directory contents are added to the index recursively. No
322+ // error is returned if all matching paths are already staged in index .
346323func (w * Worktree ) AddGlob (pattern string ) error {
347324 files , err := util .Glob (w .Filesystem , pattern )
348325 if err != nil {
@@ -374,7 +351,7 @@ func (w *Worktree) AddGlob(pattern string) error {
374351 if fi .IsDir () {
375352 added , err = w .doAddDirectory (idx , s , file )
376353 } else {
377- added , _ , err = w .doAdd (idx , s , file )
354+ added , _ , err = w .doAddFile (idx , s , file )
378355 }
379356
380357 if err != nil {
@@ -393,9 +370,13 @@ func (w *Worktree) AddGlob(pattern string) error {
393370 return nil
394371}
395372
396- // doAdd create a new blob from path and update the index, added is true if
373+ // doAddFile create a new blob from path and update the index, added is true if
397374// the file added is different from the index.
398- func (w * Worktree ) doAdd (idx * index.Index , s Status , path string ) (added bool , h plumbing.Hash , err error ) {
375+ func (w * Worktree ) doAddFile (idx * index.Index , s Status , path string ) (added bool , h plumbing.Hash , err error ) {
376+ if s .File (path ).Worktree == Unmodified {
377+ return false , h , nil
378+ }
379+
399380 h , err = w .copyFileToStorage (path )
400381 if err != nil {
401382 if os .IsNotExist (err ) {
@@ -406,10 +387,6 @@ func (w *Worktree) doAdd(idx *index.Index, s Status, path string) (added bool, h
406387 return
407388 }
408389
409- if s .File (path ).Worktree == Unmodified {
410- return false , h , nil
411- }
412-
413390 if err := w .addOrUpdateFileToIndex (idx , path , h ); err != nil {
414391 return false , h , err
415392 }
@@ -486,10 +463,7 @@ func (w *Worktree) addOrUpdateFileToIndex(idx *index.Index, filename string, h p
486463}
487464
488465func (w * Worktree ) doAddFileToIndex (idx * index.Index , filename string , h plumbing.Hash ) error {
489- e := & index.Entry {Name : filename }
490- idx .Entries = append (idx .Entries , e )
491-
492- return w .doUpdateFileToIndex (e , filename , h )
466+ return w .doUpdateFileToIndex (idx .Add (filename ), filename , h )
493467}
494468
495469func (w * Worktree ) doUpdateFileToIndex (e * index.Entry , filename string , h plumbing.Hash ) error {
@@ -515,21 +489,79 @@ func (w *Worktree) doUpdateFileToIndex(e *index.Entry, filename string, h plumbi
515489
516490// Remove removes files from the working tree and from the index.
517491func (w * Worktree ) Remove (path string ) (plumbing.Hash , error ) {
492+ // TODO(mcuadros): remove plumbing.Hash from signature at v5.
518493 idx , err := w .r .Storer .Index ()
519494 if err != nil {
520495 return plumbing .ZeroHash , err
521496 }
522497
523- hash , err := w .deleteFromIndex (idx , path )
498+ var h plumbing.Hash
499+
500+ fi , err := w .Filesystem .Lstat (path )
501+ if err != nil || ! fi .IsDir () {
502+ h , err = w .doRemoveFile (idx , path )
503+ } else {
504+ _ , err = w .doRemoveDirectory (idx , path )
505+ }
524506 if err != nil {
525- return plumbing . ZeroHash , err
507+ return h , err
526508 }
527509
528- if err := w .deleteFromFilesystem (path ); err != nil {
529- return hash , err
510+ return h , w .r .Storer .SetIndex (idx )
511+ }
512+
513+ func (w * Worktree ) doRemoveDirectory (idx * index.Index , directory string ) (removed bool , err error ) {
514+ files , err := w .Filesystem .ReadDir (directory )
515+ if err != nil {
516+ return false , err
530517 }
531518
532- return hash , w .r .Storer .SetIndex (idx )
519+ for _ , file := range files {
520+ name := path .Join (directory , file .Name ())
521+
522+ var r bool
523+ if file .IsDir () {
524+ r , err = w .doRemoveDirectory (idx , name )
525+ } else {
526+ _ , err = w .doRemoveFile (idx , name )
527+ if err == index .ErrEntryNotFound {
528+ err = nil
529+ }
530+ }
531+
532+ if err != nil {
533+ return
534+ }
535+
536+ if ! removed && r {
537+ removed = true
538+ }
539+ }
540+
541+ err = w .removeEmptyDirectory (directory )
542+ return
543+ }
544+
545+ func (w * Worktree ) removeEmptyDirectory (path string ) error {
546+ files , err := w .Filesystem .ReadDir (path )
547+ if err != nil {
548+ return err
549+ }
550+
551+ if len (files ) != 0 {
552+ return nil
553+ }
554+
555+ return w .Filesystem .Remove (path )
556+ }
557+
558+ func (w * Worktree ) doRemoveFile (idx * index.Index , path string ) (plumbing.Hash , error ) {
559+ hash , err := w .deleteFromIndex (idx , path )
560+ if err != nil {
561+ return plumbing .ZeroHash , err
562+ }
563+
564+ return hash , w .deleteFromFilesystem (path )
533565}
534566
535567func (w * Worktree ) deleteFromIndex (idx * index.Index , path string ) (plumbing.Hash , error ) {
@@ -550,9 +582,43 @@ func (w *Worktree) deleteFromFilesystem(path string) error {
550582 return err
551583}
552584
585+ // RemoveGlob removes all paths, matching pattern, from the index. If pattern
586+ // matches a directory path, all directory contents are removed from the index
587+ // recursively.
588+ func (w * Worktree ) RemoveGlob (pattern string ) error {
589+ idx , err := w .r .Storer .Index ()
590+ if err != nil {
591+ return err
592+ }
593+
594+ entries , err := idx .Glob (pattern )
595+ if err != nil {
596+ return err
597+ }
598+
599+ for _ , e := range entries {
600+ file := filepath .FromSlash (e .Name )
601+ if _ , err := w .Filesystem .Lstat (file ); err != nil && ! os .IsNotExist (err ) {
602+ return err
603+ }
604+
605+ if _ , err := w .doRemoveFile (idx , file ); err != nil {
606+ return err
607+ }
608+
609+ dir , _ := filepath .Split (file )
610+ if err := w .removeEmptyDirectory (dir ); err != nil {
611+ return err
612+ }
613+ }
614+
615+ return w .r .Storer .SetIndex (idx )
616+ }
617+
553618// Move moves or rename a file in the worktree and the index, directories are
554619// not supported.
555620func (w * Worktree ) Move (from , to string ) (plumbing.Hash , error ) {
621+ // TODO(mcuadros): support directories and/or implement support for glob
556622 if _ , err := w .Filesystem .Lstat (from ); err != nil {
557623 return plumbing .ZeroHash , err
558624 }
0 commit comments