12 #include "cache-tree.h"
13 #include "unpack-trees.h"
14 #include "reflog-walk.h"
17 // uses the ls-files code
18 #include "builtin-ls-files.c"
21 // custom cache entry flags (just to make sure that no git functions get confused)
22 #define CE_IG_ADDED 0x2000000
23 #define CE_IG_DELETED 0x4000000
24 #define CE_IG_STAGED 0x8000000
29 // cached last access, to speed up searches (since we get a sorted list from git code)
30 struct DirStatus
*pLastAccessedChild
;
34 struct DirStatus
*next
;
35 struct DirStatus
*children
;
36 struct DirStatus
*parent
;
39 BOOL bExplicitlyIgnored
;
42 static struct DirStatus l_dirTree
;
45 static BOOL l_bNoRecurse
;
46 static int l_nMinStatusRelevantForDirs
;
47 static BOOL l_bSkipNormalDirs
;
48 static int l_nEmptyDirStatus
;
49 static BOOL l_bNoRecurseDir
;
51 static BOOL l_bFullPath
;
52 static char l_sFullPathBuf
[2048];
53 static LPSTR l_lpszFileName
;
55 static BOOL l_bDirStatus
;
56 static int l_nLastStatus
;
57 static int l_nEnumeratedCached
= 0;
59 static BOOL l_bHasHistory
= FALSE
;
62 static inline char GetStatusChar(int nStatus
)
66 case WGFS_Normal
: return 'N';
67 case WGFS_Modified
: return 'M';
68 case WGFS_Staged
: return 'S';
69 case WGFS_Added
: return 'A';
70 case WGFS_Conflicted
: return 'C';
71 case WGFS_Deleted
: return 'D';
73 case WGFS_Unversioned
: return 'U';
74 case WGFS_Ignored
: return 'I';
75 case WGFS_Unknown
: return '?';
76 case WGFS_Empty
: return 'E';
84 static BOOL
enum_ce_entry(struct cache_entry
*ce
, struct stat
*st
)
86 // is this of any use (ce->ce_flags & CE_VALID) ?
92 sFileName
= ce
->name
+ prefix_offset
;
96 strcpy(l_lpszFileName
, ce
->name
);
97 sFileName
= l_sFullPathBuf
;
100 const int nStage
= ce_stage(ce
);
102 int nStatus
= WGFS_Unknown
;
103 if (!st
|| (ce
->ce_flags
& CE_IG_DELETED
))
104 nStatus
= WGFS_Deleted
;
105 else if (ce
->ce_flags
& CE_IG_ADDED
)
106 nStatus
= WGFS_Added
;
108 nStatus
= WGFS_Conflicted
;
109 else if ( ce_modified(ce
, st
, 0) )
110 nStatus
= WGFS_Modified
;
111 else if (ce
->ce_flags
& CE_IG_STAGED
)
112 nStatus
= WGFS_Staged
;
113 else if (!l_bHasHistory
)
114 nStatus
= WGFS_Added
;
116 nStatus
= WGFS_Normal
;
117 l_nLastStatus
= nStatus
;
119 // output format: "F status sha1 filename"
122 fputc(GetStatusChar(nStatus
), stdout
);
124 fputsha1(ce
->sha1
, stdout
);
126 fputs(sFileName
, stdout
);
129 l_nEnumeratedCached
++;
134 // same as enum except it skips enumeration and just determines status (used for recursive folder status)
135 // returns TRUE if file was processed
136 static BOOL
process_ce_entry_status(struct cache_entry
*ce
, struct stat
*st
)
138 // is this of any use (ce->ce_flags & CE_VALID) ?
142 ef.sFileName = ce->name + offset;
146 strcpy(l_lpszFileName, ce->name);
147 ef.sFileName = l_sFullPathBuf;
150 const int nStage
= ce_stage(ce
);
152 UINT nStatus
= WGFS_Unknown
;
153 if (!st
|| (ce
->ce_flags
& CE_IG_DELETED
))
154 nStatus
= WGFS_Deleted
;
155 else if (ce
->ce_flags
& CE_IG_ADDED
)
156 nStatus
= WGFS_Added
;
158 nStatus
= WGFS_Conflicted
;
159 else if ( ce_modified(ce
, st
, 0) )
160 nStatus
= WGFS_Modified
;
161 else if (ce
->ce_flags
& CE_IG_STAGED
)
162 nStatus
= WGFS_Staged
;
163 else if (!l_bHasHistory
)
164 nStatus
= WGFS_Added
;
166 nStatus
= WGFS_Normal
;
167 l_nLastStatus
= nStatus
;
169 //ef.nStage = st ? ce_stage(ce) : 0;
171 //ef.sha1 = ce->sha1;
177 static void update_dirs_unversioned(struct dir_entry
*ce
, int nPathNameOffset
);
179 static void enum_unversioned(struct dir_entry
**files
, int nr
, BOOL bIgnored
)
184 struct dir_entry
*ent
= files
[i
];
186 if (ent
->name
[ent
->len
-1] != '/' && !cache_name_is_other(ent
->name
, ent
->len
))
189 int len
= prefix_len
;
192 die("igit status: internal error - directory entry not superset of prefix");
194 if (pathspec
&& !pathspec_match(pathspec
, ps_matched
, ent
->name
, len
))
201 sFileName
= ent
->name
+ prefix_offset
;
205 strcpy(l_lpszFileName
, ent
->name
);
206 sFileName
= l_sFullPathBuf
;
211 // because we specified collect_all_ignored this may be a directory that was ignored
212 if (ent
->name
[ent
->len
-1] != '/')
216 l_nLastStatus
= WGFS_Ignored
;
217 update_dirs_unversioned(ent
, len
);
220 fputs("F I 0000000000000000000000000000000000000000 ", stdout
);
226 const int nOrgEmptyDirStatus
= l_nEmptyDirStatus
;
227 l_nLastStatus
= l_nEmptyDirStatus
= WGFS_Ignored
;
228 update_dirs_unversioned(ent
, len
);
229 l_nEmptyDirStatus
= nOrgEmptyDirStatus
;
237 if (ent
->name
[ent
->len
-1] != '/')
241 l_nLastStatus
= WGFS_Unversioned
;
242 update_dirs_unversioned(ent
, len
);
245 fputs("F U 0000000000000000000000000000000000000000 ", stdout
);
251 l_nLastStatus
= l_nEmptyDirStatus
;
252 update_dirs_unversioned(ent
, len
);
258 fputs(sFileName
, stdout
);
264 static inline BOOL
enum_dir(struct DirStatus
*dir
, LPCSTR lpszPathName
)
266 if (dir
->nStatus
== WGFS_Normal
&& l_bSkipNormalDirs
)
269 // output format: "D status pathname"
272 fputc(GetStatusChar(dir
->nStatus
), stdout
);
274 fputs(lpszPathName
, stdout
);
280 static BOOL
enum_dirs(struct DirStatus
*dir
, LPSTR sPathNameBuf
)
282 const int len
= strlen(dir
->lpszName
);
283 memcpy(sPathNameBuf
, dir
->lpszName
, len
);
287 if ( enum_dir(dir
, l_bFullPath
? l_sFullPathBuf
: l_sFullPathBuf
+prefix_offset
) )
290 if (!l_bNoRecurse
&& dir
->children
)
294 *sPathNameBuf
++ = '/';
301 if ( enum_dirs(dir
, sPathNameBuf
) )
312 static struct DirStatus
* GetSubDir(struct DirStatus
*dir
, LPCSTR lpszName
, int nNameLenInclTerminator
)
314 // check for cached access
315 if (dir
->pLastAccessedChild
316 && !strcmp(dir
->pLastAccessedChild
->lpszName
, lpszName
))
318 return dir
->pLastAccessedChild
;
322 struct DirStatus
*p
= dir
->children
;
323 struct DirStatus
*last
= NULL
;
326 if ( !strcmp(p
->lpszName
, lpszName
) )
327 return (dir
->pLastAccessedChild
= p
);
333 // dir not accessed before, create new entry
334 // TODO: do more efficient allocator (allocate larger pools, they can still be fire and forget and let our garbage collector clean up)
335 p
= dir
->pLastAccessedChild
= (struct DirStatus
*) malloc(sizeof(struct DirStatus
) + ((nNameLenInclTerminator
+3)&~3));
337 p
->pLastAccessedChild
= NULL
;
338 p
->lpszName
= (char*)p
+ sizeof(struct DirStatus
);
342 if (l_nEmptyDirStatus
!= WGFS_Ignored
)
344 p
->bExplicitlyIgnored
= dir
->bExplicitlyIgnored
;
345 p
->nStatus
= (p
->bExplicitlyIgnored
&& l_nEmptyDirStatus
< WGFS_Ignored
) ? WGFS_Ignored
: l_nEmptyDirStatus
;
349 p
->nStatus
= WGFS_Ignored
;
350 p
->bExplicitlyIgnored
= TRUE
;
360 memcpy((char*)p
->lpszName
, lpszName
, nNameLenInclTerminator
);
366 static inline BOOL
IsStatusRelevantForDirs(int nStatus
)
368 return nStatus
>= l_nMinStatusRelevantForDirs
&& nStatus
!= WGFS_Deleted
;
372 static void update_dirs_unversioned_rec(LPCSTR lpszFileName
, UINT nDirLen
, struct dir_entry
*ce
, struct DirStatus
*parentDir
)
374 const int nDirLen1
= nDirLen
+1;
376 memcpy(s
, lpszFileName
, nDirLen
);
379 struct DirStatus
*dir
= GetSubDir(parentDir
, s
, nDirLen1
);
380 //ASSERT(dir != NULL);
382 // TODO: if 'conflicted' status is added then need to check for that as highest prio
383 if (dir
->nStatus
>= WGFS_Modified
&& l_bNoRecurse
)
385 // no further processing needed
389 // process next subdir in lpszFileName
391 lpszFileName
+= nDirLen1
;
393 LPCSTR p
= strchr(lpszFileName
, '/');
396 // no more dirs in pathname (ie we are in the dir the file is located)
398 if (l_nEmptyDirStatus
== WGFS_Unknown
)
399 // only want dirst enumerated without recursive status
402 const int nFileStatus
= l_nLastStatus
;
404 if (nFileStatus
> dir
->nStatus
)
406 // update status on dir and all parents
409 if (nFileStatus
> dir
->nStatus
)
410 dir
->nStatus
= nFileStatus
;
412 while ( (dir
= dir
->parent
) );
415 else if (lpszFileName
!= p
) // quick check to make sure we're not left with a "/" filename
417 update_dirs_unversioned_rec(lpszFileName
, (UINT
)(p
-lpszFileName
), ce
, dir
);
421 static void update_dirs_unversioned(struct dir_entry
*ce
, int nPathNameOffset
)
423 // filename relative to enumerated path
424 LPCSTR lpszFileName
= ce
->name
+ nPathNameOffset
;
426 LPCSTR p
= strchr(lpszFileName
, '/');
427 if (p
<= lpszFileName
)
429 // file is not in sub-dir
431 const int nFileStatus
= l_nLastStatus
;
433 if (nFileStatus
> l_dirTree
.nStatus
)
434 l_dirTree
.nStatus
= nFileStatus
;
439 if (!l_bNoRecurseDir
)
441 update_dirs_unversioned_rec(lpszFileName
, (UINT
)(p
-lpszFileName
), ce
, &l_dirTree
);
446 static void update_dirs_rec(LPCSTR lpszFileName
, UINT nDirLen
, struct cache_entry
*ce
, BOOL bStatusCached
, struct DirStatus
*parentDir
)
448 const int nDirLen1
= nDirLen
+1;
450 memcpy(s
, lpszFileName
, nDirLen
);
453 struct DirStatus
*dir
= GetSubDir(parentDir
, s
, nDirLen1
);
454 //ASSERT(dir != NULL);
456 // TODO: if 'conflicted' status is added then need to check for that as highest prio
457 if (dir
->nStatus
>= WGFS_Modified
&& l_bNoRecurse
)
459 // no further processing needed
463 // process next subdir in lpszFileName
465 lpszFileName
+= nDirLen1
;
467 LPCSTR p
= strchr(lpszFileName
, '/');
470 // no more dirs in pathname (ie we are in the dir the file is located)
472 if (l_nEmptyDirStatus
== WGFS_Unknown
)
473 // only want dirst enumerated without recursive status
478 // file status not determined yet, do it now
480 int err
= lstat(ce
->name
, &st
);
481 if (!process_ce_entry_status(ce
, err
? NULL
: &st
) || !IsStatusRelevantForDirs(l_nLastStatus
))
484 const int nFileStatus
= l_nLastStatus
;
486 if (nFileStatus
> dir
->nStatus
)
488 // update status on dir and all parents
491 if (nFileStatus
> dir
->nStatus
)
492 dir
->nStatus
= nFileStatus
;
494 while ( (dir
= dir
->parent
) );
497 else if (lpszFileName
!= p
) // quick check to make sure we're not left with a "/" filename
499 update_dirs_rec(lpszFileName
, (UINT
)(p
-lpszFileName
), ce
, bStatusCached
, dir
);
503 static void update_dirs(struct cache_entry
*ce
, int nPathNameOffset
, BOOL bStatusCached
)
505 // filename relative to enumerated path
506 LPCSTR lpszFileName
= ce
->name
+ nPathNameOffset
;
508 LPCSTR p
= strchr(lpszFileName
, '/');
509 if (p
<= lpszFileName
)
511 // file is not in sub-dir
515 // file status not determined yet, do it now
517 int err
= lstat(ce
->name
, &st
);
518 if (!process_ce_entry_status(ce
, err
? NULL
: &st
) || !IsStatusRelevantForDirs(l_nLastStatus
))
521 const int nFileStatus
= l_nLastStatus
;
523 if (nFileStatus
> l_dirTree
.nStatus
)
524 l_dirTree
.nStatus
= nFileStatus
;
529 if (!l_bNoRecurseDir
)
531 update_dirs_rec(lpszFileName
, (UINT
)(p
-lpszFileName
), ce
, bStatusCached
, &l_dirTree
);
536 static inline BOOL
is_subpath(const char *sPath
, int nPathLen
, const char *sFile
)
538 return strchr(sFile
+ nPathLen
, '/') != NULL
;
541 static BOOL
is_dir(const char *sProjectPath
, const char *sSubPath
)
545 strcpy(s
, sProjectPath
);
554 // make sure it ends with a slash
558 // backslashify sub-path
567 int err
= lstat(s
, &st
);
569 return (!err
&& S_ISDIR(st
.st_mode
));
572 static inline BOOL
is_ce_name_eq(struct cache_entry
*ce1
, struct cache_entry
*ce2
)
574 const size_t len1
= ce1
->ce_flags
& CE_NAMEMASK
;
575 const size_t len2
= ce2
->ce_flags
& CE_NAMEMASK
;
577 return (len1
== len2
) ? !strcmp(ce1
->name
, ce2
->name
) : FALSE
;
581 struct oneway_unpack_data
{
582 struct rev_info
*revs
;
583 char symcache
[PATH_MAX
];
586 // modified version of function in diff-lib.c
587 static void do_oneway_diff(struct unpack_trees_options
*o
, struct cache_entry
*idx
, struct cache_entry
*tree
)
591 // file has no previous commit, newly added
592 idx
->ce_flags
|= CE_IG_ADDED
;
596 // file only in previous commit, deleted
597 idx
->ce_flags
|= CE_IG_DELETED
;
599 else if (!(idx
->ce_flags
& CE_INTENT_TO_ADD
)
600 && hashcmp(tree
->sha1
, idx
->sha1
) && !is_null_sha1(idx
->sha1
))
602 // file modified and in both indices, staged
603 idx
->ce_flags
|= CE_IG_STAGED
;
607 // function taken from diff-lib.c
608 static inline void skip_same_name(struct cache_entry
*ce
, struct unpack_trees_options
*o
)
610 int len
= ce_namelen(ce
);
611 const struct index_state
*index
= o
->src_index
;
613 while (o
->pos
< index
->cache_nr
) {
614 struct cache_entry
*next
= index
->cache
[o
->pos
];
615 if (len
!= ce_namelen(next
))
617 if (memcmp(ce
->name
, next
->name
, len
))
623 // function taken from diff-lib.c
624 static int oneway_diff(struct cache_entry
**src
, struct unpack_trees_options
*o
)
626 struct cache_entry
*idx
= src
[0];
627 struct cache_entry
*tree
= src
[1];
628 struct oneway_unpack_data
*cbdata
= o
->unpack_data
;
629 struct rev_info
*revs
= cbdata
->revs
;
631 if (idx
&& ce_stage(idx
))
632 skip_same_name(idx
, o
);
635 * Unpack-trees generates a DF/conflict entry if
636 * there was a directory in the index and a tree
637 * in the tree. From a diff standpoint, that's a
638 * delete of the tree and a create of the file.
640 if (tree
== o
->df_conflict_entry
)
643 if (ce_path_match(idx
? idx
: tree
, revs
->prune_data
))
644 do_oneway_diff(o
, idx
, tree
);
650 * This turns all merge entries into "stage 3". That guarantees that
651 * when we read in the new tree (into "stage 1"), we won't lose sight
652 * of the fact that we had unmerged entries.
654 static void mark_merge_entries(void)
657 for (i
= 0; i
< active_nr
; i
++) {
658 struct cache_entry
*ce
= active_cache
[i
];
661 ce
->ce_flags
|= CE_STAGEMASK
;
665 static void preprocess_index(struct rev_info
*revs
)
667 // compare current index with index from last commit to detect staged and newly added files
670 // based on run_diff_index()
675 const char *tree_name
;
676 struct unpack_trees_options opts
;
678 struct oneway_unpack_data unpack_cb
;
680 mark_merge_entries();
682 ent
= revs
->pending
.objects
[0].item
;
683 tree_name
= revs
->pending
.objects
[0].name
;
684 tree
= parse_tree_indirect(ent
->sha1
);
689 unpack_cb
.revs
= revs
;
690 unpack_cb
.symcache
[0] = '\0';
691 memset(&opts
, 0, sizeof(opts
));
695 opts
.fn
= oneway_diff
;
696 opts
.unpack_data
= &unpack_cb
;
697 opts
.src_index
= &the_index
;
698 opts
.dst_index
= NULL
;
700 init_tree_desc(&t
, tree
->buffer
, tree
->size
);
702 if ( unpack_trees(1, &t
, &opts
) )
708 static struct object
*get_reference(struct rev_info
*revs
, const char *name
, const unsigned char *sha1
, unsigned int flags
)
710 struct object
*object
;
712 object
= parse_object(sha1
);
714 return NULL
;//die("bad object %s", name);
715 object
->flags
|= flags
;
719 static int add_pending_object_with_mode(struct rev_info
*revs
, struct object
*obj
, const char *name
, unsigned mode
)
721 if (revs
->no_walk
&& (obj
->flags
& UNINTERESTING
))
722 return 1;//die("object ranges do not make sense when not walking revisions");
723 if (revs
->reflog_info
&& obj
->type
== OBJ_COMMIT
724 && add_reflog_for_walk(revs
->reflog_info
, (struct commit
*)obj
, name
))
726 add_object_array_with_mode(obj
, name
, &revs
->pending
, mode
);
730 static int setup_revisions_lite(struct rev_info
*revs
, const char *def
)
732 if (revs
->def
== NULL
)
734 if (revs
->def
&& !revs
->pending
.nr
) {
735 unsigned char sha1
[20];
736 struct object
*object
;
738 if (get_sha1_with_mode(revs
->def
, sha1
, &mode
))
739 return 1;//die("bad default revision '%s'", revs->def);
740 object
= get_reference(revs
, revs
->def
, sha1
, 0);
743 if ( add_pending_object_with_mode(revs
, object
, revs
->def
, mode
) )
747 /* Did the user ask for any diff output? Run the diff! */
748 if (revs
->diffopt
.output_format
& ~DIFF_FORMAT_NO_OUTPUT
)
751 /* Pickaxe, diff-filter and rename following need diffs */
752 if (revs
->diffopt
.pickaxe
||
753 revs
->diffopt
.filter
||
754 DIFF_OPT_TST(&revs
->diffopt
, FOLLOW_RENAMES
))
757 if (revs
->topo_order
)
760 if (revs
->prune_data
) {
761 diff_tree_setup_paths(revs
->prune_data
, &revs
->pruning
);
762 /* Can't prune commits with rename following: the paths change.. */
763 if (!DIFF_OPT_TST(&revs
->diffopt
, FOLLOW_RENAMES
))
765 if (!revs
->full_diff
)
766 diff_tree_setup_paths(revs
->prune_data
, &revs
->diffopt
);
768 if (revs
->combine_merges
) {
769 revs
->ignore_merges
= 0;
770 if (revs
->dense_combined_merges
&& !revs
->diffopt
.output_format
)
771 revs
->diffopt
.output_format
= DIFF_FORMAT_PATCH
;
773 revs
->diffopt
.abbrev
= revs
->abbrev
;
774 if (diff_setup_done(&revs
->diffopt
) < 0)
775 return 4;//die("diff_setup_done failed");
777 compile_grep_patterns(&revs
->grep_filter
);
779 /*if (revs->reverse && revs->reflog_info)
780 die("cannot combine --reverse with --walk-reflogs");
781 if (revs->rewrite_parents && revs->children.name)
782 die("cannot combine --parents and --children");*/
785 * Limitations on the graph functionality
787 /*if (revs->reverse && revs->graph)
788 die("cannot combine --reverse with --graph");
790 if (revs->reflog_info && revs->graph)
791 die("cannot combine --walk-reflogs with --graph");*/
798 BOOL
ig_enum_files(const char *pszProjectPath
, const char *pszSubPath
, const char *prefix
, unsigned int nFlags
)
800 // reset all local vars of builtin-ls-files.c to default
810 line_terminator
= '\n';
824 const BOOL bSubDir
= pszSubPath
&& is_dir(pszProjectPath
, pszSubPath
);
826 LPCSTR pszSubPathSpec
= pszSubPath
;
827 if (bSubDir
&& !(nFlags
& WGEFF_SingleFile
))
829 int len
= strlen(pszSubPath
);
830 char *s
= (char*)malloc(len
+3);
831 strcpy(s
, pszSubPath
);
837 //int exc_given = 0, require_work_tree = 0;
838 struct dir_struct _dir
;
840 memset(&_dir
, 0, sizeof(_dir
));
842 memset(&l_dirTree
, 0, sizeof(l_dirTree
));
843 l_dirTree
.nStatus
= WGFS_Normal
; // root dir is always at least WGFS_Normal even if empty
844 if (pszSubPath
&& !(nFlags
& WGEFF_EmptyAsNormal
))
845 l_dirTree
.nStatus
= WGFS_Empty
;
847 // NOTE: to force names to be relative to project root dir (no mater what current dir is) set prefix_offset to 0
849 prefix_offset
= strlen(prefix
);
850 git_config(git_default_config
, NULL
);
852 struct dir_struct
*dir
= &_dir
;
855 argv
[0] = pszSubPathSpec
;
858 if (/*require_work_tree &&*/ !is_inside_work_tree())
861 pathspec
= get_pathspec(prefix
, argv
);
863 // Verify that the pathspec matches the prefix
865 prefix
= verify_pathspec(prefix
);
867 // Treat unmatching pathspec elements as errors
868 if (pathspec
&& error_unmatch
)
871 for (num
= 0; pathspec
[num
]; num
++)
873 ps_matched
= xcalloc(1, num
);
876 // vars used for path recursion check
877 int pathspec_len
= 0;
878 if (pathspec
&& *pathspec
)
880 // calc length of pathspec plus 1 for a / (unless it already ends with a slash)
881 pathspec_len
= strlen(*pathspec
);
882 if ((*pathspec
)[pathspec_len
-1] == '*')
884 if ((*pathspec
)[pathspec_len
-1] != '/')
887 const char *refpath
= (pathspec
&& *pathspec
) ? *pathspec
: "";
893 l_bNoRecurseDir
= FALSE
;
895 BOOL single_dir
= (nFlags
& WGEFF_SingleFile
) && (!pszSubPath
|| bSubDir
);
896 // adjust other flags for best performance / correct results when WGEFF_SingleFile is set
897 if (single_dir
&& (nFlags
& WGEFF_NoRecurse
))
898 l_bNoRecurseDir
= TRUE
;
899 if (nFlags
& WGEFF_SingleFile
)
901 nFlags
|= WGEFF_NoRecurse
;
903 nFlags
&= ~(WGEFF_DirStatusAll
|WGEFF_DirStatusDelta
);
907 nFlags
= (nFlags
& ~WGEFF_DirStatusAll
) | WGEFF_DirStatusDelta
;
909 if ( !(nFlags
& WGEFF_EmptyAsNormal
) )
910 l_dirTree
.nStatus
= WGFS_Empty
;
913 BOOL no_recurse
= nFlags
& WGEFF_NoRecurse
;
914 l_bNoRecurse
= no_recurse
;
915 l_bFullPath
= nFlags
& WGEFF_FullPath
;
916 l_bDirStatus
= nFlags
& (WGEFF_DirStatusDelta
|WGEFF_DirStatusAll
);
918 // when all dirs should be enumerated we need IsStatusRelevantForDirs to report files of any status as relevant
919 // otherwise only above normal are considered, which is slightly more efficient
920 l_nMinStatusRelevantForDirs
= (nFlags
& WGEFF_DirStatusAll
) ? WGFS_Empty
: (WGFS_Normal
+1);
922 // initial status of dirs
923 l_nEmptyDirStatus
= (nFlags
& WGEFF_EmptyAsNormal
) ? WGFS_Normal
: WGFS_Empty
;
925 l_bSkipNormalDirs
= ((nFlags
& (WGEFF_DirStatusDelta
|WGEFF_DirStatusAll
)) == WGEFF_DirStatusDelta
);
927 if (!(nFlags
& WGEFF_SingleFile
) && !l_bDirStatus
)
929 // no recursive dir status requested, list all dirs as unknown
931 l_nEmptyDirStatus
= l_nMinStatusRelevantForDirs
= WGFS_Unknown
;
932 l_bSkipNormalDirs
= FALSE
;
933 l_dirTree
.nStatus
= WGFS_Unknown
;
937 l_lpszFileName
= NULL
;
940 strcpy(l_sFullPathBuf
, pszProjectPath
);
942 LPSTR q
= l_sFullPathBuf
;
949 // make sure it ends with a slash
955 // save pointer to where file paths, with project-relative names, can be concatenated
959 // shouldn't have any effect but set them to reflect what we want listed
966 init_revisions(&rev
, prefix
);
967 rev
.ignore_merges
= 0;
970 l_bHasHistory
= !setup_revisions_lite(&rev
, "HEAD");
974 preprocess_index(&rev
);
978 //if (pathspec && *pathspec) OutputDebugString(*pathspec);OutputDebugString(" (1)\r\n");
979 //if (prefix) OutputDebugString(prefix);OutputDebugString(" (2)\r\n");
985 for (i
=0; i
<active_nr
; i
++)
987 struct cache_entry
*ce
= active_cache
[i
];
991 int dtype
= ce_to_dtype(ce
);
993 if (excluded(dir
, ce
->name
, &dtype
) != dir
->show_ignored
)
995 if (ce
->ce_flags
& CE_UPDATE
)
998 // skip file if not inside specified sub-path
999 // this test was originally done in enum_ce_entry but in order to avoid unecessery lstat calls it was moved
1000 if (prefix_len
>= ce_namelen(ce
))
1001 die("git ls-files: internal error - cache entry not superset of prefix");
1002 if (pathspec
&& !pathspec_match(pathspec
, ps_matched
, ce
->name
, prefix_len
))
1005 if (single_dir
|| (no_recurse
&& is_subpath(refpath
, pathspec_len
, ce
->name
)))
1008 // this file would normally be skipped, but in order to determine correct dir status we need to process it
1009 update_dirs(ce
, pathspec_len
, FALSE
);
1014 err
= lstat(ce
->name
, &st
);
1016 if ( enum_ce_entry(ce
, err
? NULL
: &st
) )
1019 // normally (always?) conflicted/unmerged files will have 3 entries in a row (one in stage 1, one in 2 and one in 3)
1020 // skip redundant entries here
1025 for (j
=i
+1; j
<active_nr
; j
++)
1027 struct cache_entry
*nextce
= active_cache
[j
];
1029 if ( !is_ce_name_eq(ce
, nextce
) )
1036 if (l_bDirStatus
&& IsStatusRelevantForDirs(l_nLastStatus
))
1037 update_dirs(ce
, pathspec_len
, TRUE
);
1040 BOOL bIgnoreInitialized
= FALSE
;
1044 // check if root (pszSubPath) dir is ignored
1046 if (!bIgnoreInitialized
)
1048 setup_standard_excludes(dir
);
1049 bIgnoreInitialized
= TRUE
;
1052 char sDir
[MAX_PATH
];
1053 strcpy(sDir
, pszSubPath
);
1054 LPSTR p
= strrchr(sDir
, '/');
1058 // check for matching ignore for each subdir level
1059 p
= strchr(sDir
, '/');
1065 if ( excluded(dir
, sDir
, &dtype
) )
1067 l_dirTree
.nStatus
= WGFS_Ignored
;
1068 l_dirTree
.bExplicitlyIgnored
= TRUE
;
1074 p
= strchr(p
+1, '/');
1085 // enumerate unversioned files
1086 if ( !(nFlags
& WGEFF_SingleFile
) )
1088 const char *path
= ".", *base
= "";
1089 int baselen
= prefix_len
;
1092 path
= base
= prefix
;
1094 if (!bIgnoreInitialized
)
1096 setup_standard_excludes(dir
);
1097 bIgnoreInitialized
= TRUE
;
1099 dir
->collect_ignored
= 1;
1100 dir
->show_ignored
= 0;
1101 dir
->show_other_directories
= 0;
1102 dir
->hide_empty_directories
= 0;
1103 dir
->collect_all_ignored
= 1;
1104 dir
->collect_directories
= 1;
1105 dir
->no_recurse_readdir
= no_recurse
? 1 : 0;
1106 read_directory(dir
, path
, base
, baselen
, pathspec
);
1108 // if root dir is ignored, then all unversioned files under it are considered ignore
1109 enum_unversioned(dir
->entries
, dir
->nr
, l_dirTree
.bExplicitlyIgnored
);
1110 enum_unversioned(dir
->ignored
, dir
->ignored_nr
, TRUE
);
1112 else if (!single_dir
&& !l_nEnumeratedCached
)
1114 // get status of a single unversioned file
1116 if (!bIgnoreInitialized
)
1118 setup_standard_excludes(dir
);
1119 bIgnoreInitialized
= TRUE
;
1126 sFileName
= pszSubPath
+ prefix_offset
;
1130 strcpy(l_lpszFileName
, pszSubPath
);
1131 sFileName
= l_sFullPathBuf
;
1135 // if root dir is ignored, then all unversioned files under it are considered ignore
1136 if (!l_dirTree
.bExplicitlyIgnored
&& excluded(dir
, pszSubPath
, &dtype
))
1137 fputs("F I 0000000000000000000000000000000000000000 ", stdout
);
1139 fputs("F U 0000000000000000000000000000000000000000 ", stdout
);
1140 fputs(sFileName
, stdout
);
1148 LPCSTR lpszRootDir
="/";
1151 lpszRootDir
= l_sFullPathBuf
;
1154 strcpy(l_lpszFileName
, *pathspec
);
1155 l_lpszFileName
+= pathspec_len
;
1158 *l_lpszFileName
= 0;
1159 // remove trailng slash
1160 l_lpszFileName
[-1] = 0;
1162 else if (pathspec_len
)
1166 strcpy(l_sFullPathBuf
, *pathspec
);
1167 l_sFullPathBuf
[pathspec_len
-1] = '/';
1168 l_sFullPathBuf
[pathspec_len
] = 0;
1169 l_lpszFileName
= l_sFullPathBuf
;
1175 l_lpszFileName
= l_sFullPathBuf
;
1180 // enumerate single dir
1181 l_bSkipNormalDirs
= FALSE
;
1182 enum_dir(&l_dirTree
, lpszRootDir
);
1184 else if (!enum_dir(&l_dirTree
, lpszRootDir
) && l_dirTree
.children
)
1187 // re-add trailing slash
1188 l_lpszFileName
[-1] = '/';
1190 struct DirStatus
*p
= l_dirTree
.children
;
1194 if ( enum_dirs(p
, l_lpszFileName
) )
1197 while ( (p
= p
->next
) );
1208 * This merges the file listing in the directory cache index
1209 * with the actual working directory list, and shows different
1210 * combinations of the two.
1212 * Copyright (C) Linus Torvalds, 2005
1217 #include "builtin.h"
1221 static int show_deleted
;
1222 static int show_cached
;
1223 static int show_others
;
1224 static int show_stage
;
1225 static int show_unmerged
;
1226 static int show_modified
;
1227 static int show_killed
;
1228 static int show_valid_bit
;
1229 static int line_terminator
= '\n';
1231 static int prefix_len
;
1232 static int prefix_offset
;
1233 static const char **pathspec
;
1234 static int error_unmatch
;
1235 static char *ps_matched
;
1236 static const char *with_tree
;
1238 static const char *tag_cached
= "";
1239 static const char *tag_unmerged
= "";
1240 static const char *tag_removed
= "";
1241 static const char *tag_other
= "";
1242 static const char *tag_killed
= "";
1243 static const char *tag_modified
= "";
1247 * Match a pathspec against a filename. The first "skiplen" characters
1248 * are the common prefix
1250 int pathspec_match(const char **spec
, char *ps_matched
,
1251 const char *filename
, int skiplen
)
1255 while ((m
= *spec
++) != NULL
) {
1256 int matchlen
= strlen(m
+ skiplen
);
1260 if (!strncmp(m
+ skiplen
, filename
+ skiplen
, matchlen
)) {
1261 if (m
[skiplen
+ matchlen
- 1] == '/')
1263 switch (filename
[skiplen
+ matchlen
]) {
1264 case '/': case '\0':
1268 if (!fnmatch(m
+ skiplen
, filename
+ skiplen
, 0))
1281 static void show_dir_entry(const char *tag
, struct dir_entry
*ent
)
1283 int len
= prefix_len
;
1284 int offset
= prefix_offset
;
1286 if (len
>= ent
->len
)
1287 die("git ls-files: internal error - directory entry not superset of prefix");
1289 if (pathspec
&& !pathspec_match(pathspec
, ps_matched
, ent
->name
, len
))
1293 write_name_quoted(ent
->name
+ offset
, stdout
, line_terminator
);
1296 static void show_other_files(struct dir_struct
*dir
)
1300 for (i
= 0; i
< dir
->nr
; i
++) {
1301 struct dir_entry
*ent
= dir
->entries
[i
];
1302 if (!cache_name_is_other(ent
->name
, ent
->len
))
1304 show_dir_entry(tag_other
, ent
);
1308 static void show_killed_files(struct dir_struct
*dir
)
1311 for (i
= 0; i
< dir
->nr
; i
++) {
1312 struct dir_entry
*ent
= dir
->entries
[i
];
1314 int pos
, len
, killed
= 0;
1316 for (cp
= ent
->name
; cp
- ent
->name
< ent
->len
; cp
= sp
+ 1) {
1317 sp
= strchr(cp
, '/');
1319 /* If ent->name is prefix of an entry in the
1320 * cache, it will be killed.
1322 pos
= cache_name_pos(ent
->name
, ent
->len
);
1324 die("bug in show-killed-files");
1326 while (pos
< active_nr
&&
1327 ce_stage(active_cache
[pos
]))
1328 pos
++; /* skip unmerged */
1329 if (active_nr
<= pos
)
1331 /* pos points at a name immediately after
1332 * ent->name in the cache. Does it expect
1333 * ent->name to be a directory?
1335 len
= ce_namelen(active_cache
[pos
]);
1336 if ((ent
->len
< len
) &&
1337 !strncmp(active_cache
[pos
]->name
,
1338 ent
->name
, ent
->len
) &&
1339 active_cache
[pos
]->name
[ent
->len
] == '/')
1343 if (0 <= cache_name_pos(ent
->name
, sp
- ent
->name
)) {
1344 /* If any of the leading directories in
1345 * ent->name is registered in the cache,
1346 * ent->name will be killed.
1353 show_dir_entry(tag_killed
, dir
->entries
[i
]);
1357 static void show_ce_entry(const char *tag
, struct cache_entry
*ce
)
1359 int len
= prefix_len
;
1360 int offset
= prefix_offset
;
1362 if (len
>= ce_namelen(ce
))
1363 die("git ls-files: internal error - cache entry not superset of prefix");
1365 if (pathspec
&& !pathspec_match(pathspec
, ps_matched
, ce
->name
, len
))
1368 if (tag
&& *tag
&& show_valid_bit
&&
1369 (ce
->ce_flags
& CE_VALID
)) {
1370 static char alttag
[4];
1371 memcpy(alttag
, tag
, 3);
1372 if (isalpha(tag
[0]))
1373 alttag
[0] = tolower(tag
[0]);
1374 else if (tag
[0] == '?')
1388 printf("%s%06o %s %d\t",
1391 abbrev
? find_unique_abbrev(ce
->sha1
,abbrev
)
1392 : sha1_to_hex(ce
->sha1
),
1395 write_name_quoted(ce
->name
+ offset
, stdout
, line_terminator
);
1398 static void show_files(struct dir_struct
*dir
, const char *prefix
)
1402 /* For cached/deleted files we don't need to even do the readdir */
1403 if (show_others
|| show_killed
) {
1404 const char *path
= ".", *base
= "";
1405 int baselen
= prefix_len
;
1408 path
= base
= prefix
;
1409 read_directory(dir
, path
, base
, baselen
, pathspec
);
1411 show_other_files(dir
);
1413 show_killed_files(dir
);
1415 if (show_cached
| show_stage
) {
1416 for (i
= 0; i
< active_nr
; i
++) {
1417 struct cache_entry
*ce
= active_cache
[i
];
1418 int dtype
= ce_to_dtype(ce
);
1419 if (excluded(dir
, ce
->name
, &dtype
) != dir
->show_ignored
)
1421 if (show_unmerged
&& !ce_stage(ce
))
1423 if (ce
->ce_flags
& CE_UPDATE
)
1425 show_ce_entry(ce_stage(ce
) ? tag_unmerged
: tag_cached
, ce
);
1428 if (show_deleted
| show_modified
) {
1429 for (i
= 0; i
< active_nr
; i
++) {
1430 struct cache_entry
*ce
= active_cache
[i
];
1433 int dtype
= ce_to_dtype(ce
);
1434 if (excluded(dir
, ce
->name
, &dtype
) != dir
->show_ignored
)
1436 if (ce
->ce_flags
& CE_UPDATE
)
1438 err
= lstat(ce
->name
, &st
);
1439 if (show_deleted
&& err
)
1440 show_ce_entry(tag_removed
, ce
);
1441 if (show_modified
&& ce_modified(ce
, &st
, 0))
1442 show_ce_entry(tag_modified
, ce
);
1448 * Prune the index to only contain stuff starting with "prefix"
1450 static void prune_cache(const char *prefix
)
1452 int pos
= cache_name_pos(prefix
, prefix_len
);
1453 unsigned int first
, last
;
1457 memmove(active_cache
, active_cache
+ pos
,
1458 (active_nr
- pos
) * sizeof(struct cache_entry
*));
1462 while (last
> first
) {
1463 int next
= (last
+ first
) >> 1;
1464 struct cache_entry
*ce
= active_cache
[next
];
1465 if (!strncmp(ce
->name
, prefix
, prefix_len
)) {
1474 static const char *verify_pathspec(const char *prefix
)
1476 const char **p
, *n
, *prev
;
1481 for (p
= pathspec
; (n
= *p
) != NULL
; p
++) {
1483 for (i
= 0; i
< max
; i
++) {
1485 if (prev
&& prev
[i
] != c
)
1487 if (!c
|| c
== '*' || c
== '?')
1500 if (prefix_offset
> max
|| memcmp(prev
, prefix
, prefix_offset
))
1501 die("git ls-files: cannot generate relative filenames containing '..'");
1504 return max
? xmemdupz(prev
, max
) : NULL
;
1508 * Read the tree specified with --with-tree option
1509 * (typically, HEAD) into stage #1 and then
1510 * squash them down to stage #0. This is used for
1511 * --error-unmatch to list and check the path patterns
1512 * that were given from the command line. We are not
1513 * going to write this index out.
1515 void overlay_tree_on_cache(const char *tree_name
, const char *prefix
)
1518 unsigned char sha1
[20];
1520 struct cache_entry
*last_stage0
= NULL
;
1523 if (get_sha1(tree_name
, sha1
))
1524 die("tree-ish %s not found.", tree_name
);
1525 tree
= parse_tree_indirect(sha1
);
1527 die("bad tree-ish %s", tree_name
);
1529 /* Hoist the unmerged entries up to stage #3 to make room */
1530 for (i
= 0; i
< active_nr
; i
++) {
1531 struct cache_entry
*ce
= active_cache
[i
];
1534 ce
->ce_flags
|= CE_STAGEMASK
;
1538 static const char *(matchbuf
[2]);
1539 matchbuf
[0] = prefix
;
1544 if (read_tree(tree
, 1, match
))
1545 die("unable to read tree entries %s", tree_name
);
1547 for (i
= 0; i
< active_nr
; i
++) {
1548 struct cache_entry
*ce
= active_cache
[i
];
1549 switch (ce_stage(ce
)) {
1557 * If there is stage #0 entry for this, we do not
1558 * need to show it. We use CE_UPDATE bit to mark
1562 !strcmp(last_stage0
->name
, ce
->name
))
1563 ce
->ce_flags
|= CE_UPDATE
;
1568 int report_path_error(const char *ps_matched
, const char **pathspec
, int prefix_offset
)
1571 * Make sure all pathspec matched; otherwise it is an error.
1573 int num
, errors
= 0;
1574 for (num
= 0; pathspec
[num
]; num
++) {
1575 int other
, found_dup
;
1577 if (ps_matched
[num
])
1580 * The caller might have fed identical pathspec
1581 * twice. Do not barf on such a mistake.
1583 for (found_dup
= other
= 0;
1584 !found_dup
&& pathspec
[other
];
1586 if (other
== num
|| !ps_matched
[other
])
1588 if (!strcmp(pathspec
[other
], pathspec
[num
]))
1590 * Ok, we have a match already.
1597 error("pathspec '%s' did not match any file(s) known to git.",
1598 pathspec
[num
] + prefix_offset
);
1604 static const char ls_files_usage
[] =
1605 "git ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
1606 "[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
1607 "[ --exclude-per-directory=<filename> ] [--exclude-standard] "
1608 "[--full-name] [--abbrev] [--] [<file>]*";
1610 int cmd_ls_files(int argc
, const char **argv
, const char *prefix
)
1613 int exc_given
= 0, require_work_tree
= 0;
1614 struct dir_struct dir
;
1616 memset(&dir
, 0, sizeof(dir
));
1618 prefix_offset
= strlen(prefix
);
1619 git_config(git_default_config
, NULL
);
1621 for (i
= 1; i
< argc
; i
++) {
1622 const char *arg
= argv
[i
];
1624 if (!strcmp(arg
, "--")) {
1628 if (!strcmp(arg
, "-z")) {
1629 line_terminator
= 0;
1632 if (!strcmp(arg
, "-t") || !strcmp(arg
, "-v")) {
1634 tag_unmerged
= "M ";
1636 tag_modified
= "C ";
1643 if (!strcmp(arg
, "-c") || !strcmp(arg
, "--cached")) {
1647 if (!strcmp(arg
, "-d") || !strcmp(arg
, "--deleted")) {
1651 if (!strcmp(arg
, "-m") || !strcmp(arg
, "--modified")) {
1653 require_work_tree
= 1;
1656 if (!strcmp(arg
, "-o") || !strcmp(arg
, "--others")) {
1658 require_work_tree
= 1;
1661 if (!strcmp(arg
, "-i") || !strcmp(arg
, "--ignored")) {
1662 dir
.show_ignored
= 1;
1663 require_work_tree
= 1;
1666 if (!strcmp(arg
, "-s") || !strcmp(arg
, "--stage")) {
1670 if (!strcmp(arg
, "-k") || !strcmp(arg
, "--killed")) {
1672 require_work_tree
= 1;
1675 if (!strcmp(arg
, "--directory")) {
1676 dir
.show_other_directories
= 1;
1679 if (!strcmp(arg
, "--no-empty-directory")) {
1680 dir
.hide_empty_directories
= 1;
1683 if (!strcmp(arg
, "-u") || !strcmp(arg
, "--unmerged")) {
1684 /* There's no point in showing unmerged unless
1685 * you also show the stage information.
1691 if (!strcmp(arg
, "-x") && i
+1 < argc
) {
1693 add_exclude(argv
[++i
], "", 0, &dir
.exclude_list
[EXC_CMDL
]);
1696 if (!prefixcmp(arg
, "--exclude=")) {
1698 add_exclude(arg
+10, "", 0, &dir
.exclude_list
[EXC_CMDL
]);
1701 if (!strcmp(arg
, "-X") && i
+1 < argc
) {
1703 add_excludes_from_file(&dir
, argv
[++i
]);
1706 if (!prefixcmp(arg
, "--exclude-from=")) {
1708 add_excludes_from_file(&dir
, arg
+15);
1711 if (!prefixcmp(arg
, "--exclude-per-directory=")) {
1713 dir
.exclude_per_dir
= arg
+ 24;
1716 if (!strcmp(arg
, "--exclude-standard")) {
1718 setup_standard_excludes(&dir
);
1721 if (!strcmp(arg
, "--full-name")) {
1725 if (!strcmp(arg
, "--error-unmatch")) {
1729 if (!prefixcmp(arg
, "--with-tree=")) {
1730 with_tree
= arg
+ 12;
1733 if (!prefixcmp(arg
, "--abbrev=")) {
1734 abbrev
= strtoul(arg
+9, NULL
, 10);
1735 if (abbrev
&& abbrev
< MINIMUM_ABBREV
)
1736 abbrev
= MINIMUM_ABBREV
;
1737 else if (abbrev
> 40)
1741 if (!strcmp(arg
, "--abbrev")) {
1742 abbrev
= DEFAULT_ABBREV
;
1746 usage(ls_files_usage
);
1750 if (require_work_tree
&& !is_inside_work_tree())
1753 pathspec
= get_pathspec(prefix
, argv
+ i
);
1755 /* Verify that the pathspec matches the prefix */
1757 prefix
= verify_pathspec(prefix
);
1759 /* Treat unmatching pathspec elements as errors */
1760 if (pathspec
&& error_unmatch
) {
1762 for (num
= 0; pathspec
[num
]; num
++)
1764 ps_matched
= xcalloc(1, num
);
1767 if (dir
.show_ignored
&& !exc_given
) {
1768 fprintf(stderr
, "%s: --ignored needs some exclude pattern\n",
1773 /* With no flags, we default to showing the cached files */
1774 if (!(show_stage
| show_deleted
| show_others
| show_unmerged
|
1775 show_killed
| show_modified
))
1780 prune_cache(prefix
);
1783 * Basic sanity check; show-stages and show-unmerged
1784 * would not make any sense with this option.
1786 if (show_stage
|| show_unmerged
)
1787 die("ls-files --with-tree is incompatible with -s or -u");
1788 overlay_tree_on_cache(with_tree
, prefix
);
1790 show_files(&dir
, prefix
);
1794 bad
= report_path_error(ps_matched
, pathspec
, prefix_offset
);
1796 fprintf(stderr
, "Did you forget to 'git add'?\n");