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
;
47 struct cache_entry
*ce
;
48 struct EntryRef
*next
;
51 static struct EntryRef
*l_delQueue
= NULL
;
54 static BOOL l_bNoRecurse
;
55 static int l_nMinStatusRelevantForDirs
;
56 static BOOL l_bSkipNormalDirs
;
57 static int l_nEmptyDirStatus
;
58 static BOOL l_bNoRecurseDir
;
60 static BOOL l_bFullPath
;
61 static char l_sFullPathBuf
[2048];
62 static LPSTR l_lpszFileName
;
64 static BOOL l_bDirStatus
;
65 static int l_nLastStatus
;
66 static int l_nEnumeratedCached
= 0;
68 static BOOL l_bHasHistory
= FALSE
;
71 static inline char GetStatusChar(int nStatus
)
75 case WGFS_Normal
: return 'N';
76 case WGFS_Modified
: return 'M';
77 case WGFS_Staged
: return 'S';
78 case WGFS_Added
: return 'A';
79 case WGFS_Conflicted
: return 'C';
80 case WGFS_Deleted
: return 'D';
82 case WGFS_Unversioned
: return 'U';
83 case WGFS_Ignored
: return 'I';
84 case WGFS_Unknown
: return '?';
85 case WGFS_Empty
: return 'E';
92 static inline void queue_deleted(struct cache_entry
*ce
)
94 struct EntryRef
*p
= (struct EntryRef
*) malloc( sizeof(struct EntryRef
) );
103 static BOOL
enum_ce_entry(struct cache_entry
*ce
, struct stat
*st
)
105 // is this of any use (ce->ce_flags & CE_VALID) ?
111 sFileName
= ce
->name
+ prefix_offset
;
115 strcpy(l_lpszFileName
, ce
->name
);
116 sFileName
= l_sFullPathBuf
;
119 const int nStage
= ce_stage(ce
);
121 int nStatus
= WGFS_Unknown
;
122 if (!st
|| (ce
->ce_flags
& CE_IG_DELETED
))
123 nStatus
= WGFS_Deleted
;
125 nStatus
= WGFS_Conflicted
;
126 else if (ce
->ce_flags
& CE_IG_ADDED
)
127 nStatus
= WGFS_Added
;
128 else if ( ce_modified(ce
, st
, 0) )
129 nStatus
= WGFS_Modified
;
130 else if (ce
->ce_flags
& CE_IG_STAGED
)
131 nStatus
= WGFS_Staged
;
132 else if (!l_bHasHistory
)
133 nStatus
= WGFS_Added
;
135 nStatus
= WGFS_Normal
;
136 l_nLastStatus
= nStatus
;
138 // output format: "F status sha1 filename"
141 fputc(GetStatusChar(nStatus
), stdout
);
143 fputsha1(ce
->sha1
, stdout
);
145 fputs(sFileName
, stdout
);
148 l_nEnumeratedCached
++;
153 // same as enum except it skips enumeration and just determines status (used for recursive folder status)
154 // returns TRUE if file was processed
155 static BOOL
process_ce_entry_status(struct cache_entry
*ce
, struct stat
*st
)
157 // is this of any use (ce->ce_flags & CE_VALID) ?
161 ef.sFileName = ce->name + offset;
165 strcpy(l_lpszFileName, ce->name);
166 ef.sFileName = l_sFullPathBuf;
169 const int nStage
= ce_stage(ce
);
171 UINT nStatus
= WGFS_Unknown
;
172 if (!st
|| (ce
->ce_flags
& CE_IG_DELETED
))
173 nStatus
= WGFS_Deleted
;
175 nStatus
= WGFS_Conflicted
;
176 else if (ce
->ce_flags
& CE_IG_ADDED
)
177 nStatus
= WGFS_Added
;
178 else if ( ce_modified(ce
, st
, 0) )
179 nStatus
= WGFS_Modified
;
180 else if (ce
->ce_flags
& CE_IG_STAGED
)
181 nStatus
= WGFS_Staged
;
182 else if (!l_bHasHistory
)
183 nStatus
= WGFS_Added
;
185 nStatus
= WGFS_Normal
;
186 l_nLastStatus
= nStatus
;
188 //ef.nStage = st ? ce_stage(ce) : 0;
190 //ef.sha1 = ce->sha1;
196 static void update_dirs_unversioned(struct dir_entry
*ce
, int nPathNameOffset
);
198 static void enum_unversioned(struct dir_entry
**files
, int nr
, BOOL bIgnored
)
203 struct dir_entry
*ent
= files
[i
];
205 if (ent
->name
[ent
->len
-1] != '/' && !cache_name_is_other(ent
->name
, ent
->len
))
208 int len
= prefix_len
;
211 die("igit status: internal error - directory entry not superset of prefix");
213 if (pathspec
&& !pathspec_match(pathspec
, ps_matched
, ent
->name
, len
))
220 sFileName
= ent
->name
+ prefix_offset
;
224 strcpy(l_lpszFileName
, ent
->name
);
225 sFileName
= l_sFullPathBuf
;
230 // because we specified collect_all_ignored this may be a directory that was ignored
231 if (ent
->name
[ent
->len
-1] != '/')
235 l_nLastStatus
= WGFS_Ignored
;
236 update_dirs_unversioned(ent
, len
);
239 fputs("F I 0000000000000000000000000000000000000000 ", stdout
);
245 const int nOrgEmptyDirStatus
= l_nEmptyDirStatus
;
246 l_nLastStatus
= l_nEmptyDirStatus
= WGFS_Ignored
;
247 update_dirs_unversioned(ent
, len
);
248 l_nEmptyDirStatus
= nOrgEmptyDirStatus
;
256 if (ent
->name
[ent
->len
-1] != '/')
260 l_nLastStatus
= WGFS_Unversioned
;
261 update_dirs_unversioned(ent
, len
);
264 fputs("F U 0000000000000000000000000000000000000000 ", stdout
);
270 l_nLastStatus
= l_nEmptyDirStatus
;
271 update_dirs_unversioned(ent
, len
);
277 fputs(sFileName
, stdout
);
283 static inline BOOL
enum_dir(struct DirStatus
*dir
, LPCSTR lpszPathName
)
285 if (dir
->nStatus
== WGFS_Normal
&& l_bSkipNormalDirs
)
288 // output format: "D status pathname"
291 fputc(GetStatusChar(dir
->nStatus
), stdout
);
293 fputs(lpszPathName
, stdout
);
299 static BOOL
enum_dirs(struct DirStatus
*dir
, LPSTR sPathNameBuf
)
301 const int len
= strlen(dir
->lpszName
);
302 memcpy(sPathNameBuf
, dir
->lpszName
, len
);
306 if ( enum_dir(dir
, l_bFullPath
? l_sFullPathBuf
: l_sFullPathBuf
+prefix_offset
) )
309 if (!l_bNoRecurse
&& dir
->children
)
313 *sPathNameBuf
++ = '/';
320 if ( enum_dirs(dir
, sPathNameBuf
) )
331 static struct DirStatus
* GetSubDir(struct DirStatus
*dir
, LPCSTR lpszName
, int nNameLenInclTerminator
)
333 // check for cached access
334 if (dir
->pLastAccessedChild
335 && !strcmp(dir
->pLastAccessedChild
->lpszName
, lpszName
))
337 return dir
->pLastAccessedChild
;
341 struct DirStatus
*p
= dir
->children
;
342 struct DirStatus
*last
= NULL
;
345 if ( !strcmp(p
->lpszName
, lpszName
) )
346 return (dir
->pLastAccessedChild
= p
);
352 // dir not accessed before, create new entry
353 // TODO: do more efficient allocator (allocate larger pools, they can still be fire and forget and let our garbage collector clean up)
354 p
= dir
->pLastAccessedChild
= (struct DirStatus
*) malloc(sizeof(struct DirStatus
) + ((nNameLenInclTerminator
+3)&~3));
356 p
->pLastAccessedChild
= NULL
;
357 p
->lpszName
= (char*)p
+ sizeof(struct DirStatus
);
361 if (l_nEmptyDirStatus
!= WGFS_Ignored
)
363 p
->bExplicitlyIgnored
= dir
->bExplicitlyIgnored
;
364 p
->nStatus
= (p
->bExplicitlyIgnored
&& l_nEmptyDirStatus
< WGFS_Ignored
) ? WGFS_Ignored
: l_nEmptyDirStatus
;
368 p
->nStatus
= WGFS_Ignored
;
369 p
->bExplicitlyIgnored
= TRUE
;
379 memcpy((char*)p
->lpszName
, lpszName
, nNameLenInclTerminator
);
385 static inline BOOL
IsStatusRelevantForDirs(int nStatus
)
387 return nStatus
>= l_nMinStatusRelevantForDirs
&& nStatus
!= WGFS_Deleted
;
391 static void update_dirs_unversioned_rec(LPCSTR lpszFileName
, UINT nDirLen
, struct dir_entry
*ce
, struct DirStatus
*parentDir
)
393 const int nDirLen1
= nDirLen
+1;
395 memcpy(s
, lpszFileName
, nDirLen
);
398 struct DirStatus
*dir
= GetSubDir(parentDir
, s
, nDirLen1
);
399 //ASSERT(dir != NULL);
401 if (dir
->nStatus
>= WGFS_Conflicted
&& l_bNoRecurse
)
403 // no further processing needed
407 // process next subdir in lpszFileName
409 lpszFileName
+= nDirLen1
;
411 LPCSTR p
= strchr(lpszFileName
, '/');
414 // no more dirs in pathname (ie we are in the dir the file is located)
416 if (l_nEmptyDirStatus
== WGFS_Unknown
)
417 // only want dirs enumerated without recursive status
420 const int nFileStatus
= l_nLastStatus
;
422 if (nFileStatus
> dir
->nStatus
)
424 // update status on dir and all parents
427 if (nFileStatus
> dir
->nStatus
)
428 dir
->nStatus
= nFileStatus
;
430 while ( (dir
= dir
->parent
) );
433 else if (lpszFileName
!= p
) // quick check to make sure we're not left with a "/" filename
435 update_dirs_unversioned_rec(lpszFileName
, (UINT
)(p
-lpszFileName
), ce
, dir
);
439 static void update_dirs_unversioned(struct dir_entry
*ce
, int nPathNameOffset
)
441 // filename relative to enumerated path
442 LPCSTR lpszFileName
= ce
->name
+ nPathNameOffset
;
444 LPCSTR p
= strchr(lpszFileName
, '/');
445 if (p
<= lpszFileName
)
447 // file is not in sub-dir
449 if (l_nEmptyDirStatus
== WGFS_Unknown
)
450 // only want dirst enumerated without recursive status
453 const int nFileStatus
= l_nLastStatus
;
455 if (nFileStatus
> l_dirTree
.nStatus
)
456 l_dirTree
.nStatus
= nFileStatus
;
461 if (!l_bNoRecurseDir
)
463 update_dirs_unversioned_rec(lpszFileName
, (UINT
)(p
-lpszFileName
), ce
, &l_dirTree
);
468 static void update_dirs_rec(LPCSTR lpszFileName
, UINT nDirLen
, struct cache_entry
*ce
, BOOL bStatusCached
, struct DirStatus
*parentDir
)
470 const int nDirLen1
= nDirLen
+1;
472 memcpy(s
, lpszFileName
, nDirLen
);
475 struct DirStatus
*dir
= GetSubDir(parentDir
, s
, nDirLen1
);
476 //ASSERT(dir != NULL);
478 if (dir
->nStatus
>= WGFS_Conflicted
&& l_bNoRecurse
)
480 // no further processing needed
484 // process next subdir in lpszFileName
486 lpszFileName
+= nDirLen1
;
488 LPCSTR p
= strchr(lpszFileName
, '/');
491 // no more dirs in pathname (ie we are in the dir the file is located)
493 if (l_nEmptyDirStatus
== WGFS_Unknown
)
494 // only want dirst enumerated without recursive status
499 // file status not determined yet, do it now
501 int err
= lstat(ce
->name
, &st
);
502 if (!process_ce_entry_status(ce
, err
? NULL
: &st
) || !IsStatusRelevantForDirs(l_nLastStatus
))
505 const int nFileStatus
= l_nLastStatus
;
507 if (nFileStatus
> dir
->nStatus
)
509 // update status on dir and all parents
512 if (nFileStatus
> dir
->nStatus
)
513 dir
->nStatus
= nFileStatus
;
515 while ( (dir
= dir
->parent
) );
518 else if (lpszFileName
!= p
) // quick check to make sure we're not left with a "/" filename
520 update_dirs_rec(lpszFileName
, (UINT
)(p
-lpszFileName
), ce
, bStatusCached
, dir
);
524 static void update_dirs(struct cache_entry
*ce
, int nPathNameOffset
, BOOL bStatusCached
)
526 // filename relative to enumerated path
527 LPCSTR lpszFileName
= ce
->name
+ nPathNameOffset
;
529 LPCSTR p
= strchr(lpszFileName
, '/');
530 if (p
<= lpszFileName
)
532 // file is not in sub-dir
534 if (l_nEmptyDirStatus
== WGFS_Unknown
)
535 // only want dirs enumerated without recursive status
540 // file status not determined yet, do it now
542 int err
= lstat(ce
->name
, &st
);
543 if (!process_ce_entry_status(ce
, err
? NULL
: &st
) || !IsStatusRelevantForDirs(l_nLastStatus
))
546 const int nFileStatus
= l_nLastStatus
;
548 if (nFileStatus
> l_dirTree
.nStatus
)
549 l_dirTree
.nStatus
= nFileStatus
;
554 if (!l_bNoRecurseDir
)
556 update_dirs_rec(lpszFileName
, (UINT
)(p
-lpszFileName
), ce
, bStatusCached
, &l_dirTree
);
561 static inline BOOL
is_subpath(const char *sPath
, int nPathLen
, const char *sFile
)
563 return strchr(sFile
+ nPathLen
, '/') != NULL
;
566 static BOOL
is_dir(const char *sProjectPath
, const char *sSubPath
)
570 strcpy(s
, sProjectPath
);
579 // make sure it ends with a slash
583 // backslashify sub-path
592 int err
= lstat(s
, &st
);
594 return (!err
&& S_ISDIR(st
.st_mode
));
597 static inline BOOL
is_ce_name_eq(struct cache_entry
*ce1
, struct cache_entry
*ce2
)
599 const size_t len1
= ce1
->ce_flags
& CE_NAMEMASK
;
600 const size_t len2
= ce2
->ce_flags
& CE_NAMEMASK
;
602 return (len1
== len2
) ? !strcmp(ce1
->name
, ce2
->name
) : FALSE
;
606 struct oneway_unpack_data
{
607 struct rev_info
*revs
;
608 char symcache
[PATH_MAX
];
611 // modified version of function in diff-lib.c
612 static void do_oneway_diff(struct unpack_trees_options
*o
, struct cache_entry
*idx
, struct cache_entry
*tree
)
618 // file has no previous commit, newly added
619 idx
->ce_flags
|= CE_IG_ADDED
;
624 // file only in previous commit, deleted
625 tree
->ce_flags
|= CE_IG_DELETED
;
628 else if (!(idx
->ce_flags
& CE_INTENT_TO_ADD
)
629 && hashcmp(tree
->sha1
, idx
->sha1
) && !is_null_sha1(idx
->sha1
))
631 // file modified and in both indices, staged
632 idx
->ce_flags
|= CE_IG_STAGED
;
636 // function taken from diff-lib.c
637 static inline void skip_same_name(struct cache_entry
*ce
, struct unpack_trees_options
*o
)
639 int len
= ce_namelen(ce
);
640 const struct index_state
*index
= o
->src_index
;
642 while (o
->pos
< index
->cache_nr
) {
643 struct cache_entry
*next
= index
->cache
[o
->pos
];
644 if (len
!= ce_namelen(next
))
646 if (memcmp(ce
->name
, next
->name
, len
))
652 // function taken from diff-lib.c
653 static int oneway_diff(struct cache_entry
**src
, struct unpack_trees_options
*o
)
655 struct cache_entry
*idx
= src
[0];
656 struct cache_entry
*tree
= src
[1];
657 struct oneway_unpack_data
*cbdata
= o
->unpack_data
;
658 struct rev_info
*revs
= cbdata
->revs
;
660 if (idx
&& ce_stage(idx
))
661 skip_same_name(idx
, o
);
664 * Unpack-trees generates a DF/conflict entry if
665 * there was a directory in the index and a tree
666 * in the tree. From a diff standpoint, that's a
667 * delete of the tree and a create of the file.
669 if (tree
== o
->df_conflict_entry
)
672 if (ce_path_match(idx
? idx
: tree
, revs
->prune_data
))
673 do_oneway_diff(o
, idx
, tree
);
679 * This turns all merge entries into "stage 3". That guarantees that
680 * when we read in the new tree (into "stage 1"), we won't lose sight
681 * of the fact that we had unmerged entries.
683 static void mark_merge_entries(void)
686 for (i
= 0; i
< active_nr
; i
++) {
687 struct cache_entry
*ce
= active_cache
[i
];
690 ce
->ce_flags
|= CE_STAGEMASK
;
694 static void preprocess_index(struct rev_info
*revs
)
696 // compare current index with index from last commit to detect staged and newly added files
699 // based on run_diff_index()
704 const char *tree_name
;
705 struct unpack_trees_options opts
;
707 struct oneway_unpack_data unpack_cb
;
709 mark_merge_entries();
711 ent
= revs
->pending
.objects
[0].item
;
712 tree_name
= revs
->pending
.objects
[0].name
;
713 tree
= parse_tree_indirect(ent
->sha1
);
718 unpack_cb
.revs
= revs
;
719 unpack_cb
.symcache
[0] = '\0';
720 memset(&opts
, 0, sizeof(opts
));
724 opts
.fn
= oneway_diff
;
725 opts
.unpack_data
= &unpack_cb
;
726 opts
.src_index
= &the_index
;
727 opts
.dst_index
= NULL
;
729 init_tree_desc(&t
, tree
->buffer
, tree
->size
);
731 if ( unpack_trees(1, &t
, &opts
) )
735 // add deleted files to index (easier for enumeration functions to process)
738 struct EntryRef
*p
= l_delQueue
;
742 // only add file for enumeration if they still exist
744 if ( lstat(p
->ce
->name
, &st
) )
746 struct cache_entry
*ce
= make_cache_entry(p
->ce
->ce_mode
, null_sha1
, p
->ce
->name
, 0, 0);
748 add_index_entry(&the_index
, ce
, ADD_CACHE_OK_TO_ADD
|ADD_CACHE_SKIP_DFCHECK
|ADD_CACHE_NEW_ONLY
);
749 ce
->ce_flags
&= ~CE_ADDED
;
750 ce
->ce_flags
|= CE_IG_DELETED
;
753 struct EntryRef
*q
= p
;
764 static struct object
*get_reference(struct rev_info
*revs
, const char *name
, const unsigned char *sha1
, unsigned int flags
)
766 struct object
*object
;
768 object
= parse_object(sha1
);
770 return NULL
;//die("bad object %s", name);
771 object
->flags
|= flags
;
775 static int add_pending_object_with_mode(struct rev_info
*revs
, struct object
*obj
, const char *name
, unsigned mode
)
777 if (revs
->no_walk
&& (obj
->flags
& UNINTERESTING
))
778 return 1;//die("object ranges do not make sense when not walking revisions");
779 if (revs
->reflog_info
&& obj
->type
== OBJ_COMMIT
780 && add_reflog_for_walk(revs
->reflog_info
, (struct commit
*)obj
, name
))
782 add_object_array_with_mode(obj
, name
, &revs
->pending
, mode
);
786 static int setup_revisions_lite(struct rev_info
*revs
, const char *def
)
788 if (revs
->def
== NULL
)
790 if (revs
->def
&& !revs
->pending
.nr
) {
791 unsigned char sha1
[20];
792 struct object
*object
;
794 if (get_sha1_with_mode(revs
->def
, sha1
, &mode
))
795 return 1;//die("bad default revision '%s'", revs->def);
796 object
= get_reference(revs
, revs
->def
, sha1
, 0);
799 if ( add_pending_object_with_mode(revs
, object
, revs
->def
, mode
) )
803 /* Did the user ask for any diff output? Run the diff! */
804 if (revs
->diffopt
.output_format
& ~DIFF_FORMAT_NO_OUTPUT
)
807 /* Pickaxe, diff-filter and rename following need diffs */
808 if (revs
->diffopt
.pickaxe
||
809 revs
->diffopt
.filter
||
810 DIFF_OPT_TST(&revs
->diffopt
, FOLLOW_RENAMES
))
813 if (revs
->topo_order
)
816 if (revs
->prune_data
) {
817 diff_tree_setup_paths(revs
->prune_data
, &revs
->pruning
);
818 /* Can't prune commits with rename following: the paths change.. */
819 if (!DIFF_OPT_TST(&revs
->diffopt
, FOLLOW_RENAMES
))
821 if (!revs
->full_diff
)
822 diff_tree_setup_paths(revs
->prune_data
, &revs
->diffopt
);
824 if (revs
->combine_merges
) {
825 revs
->ignore_merges
= 0;
826 if (revs
->dense_combined_merges
&& !revs
->diffopt
.output_format
)
827 revs
->diffopt
.output_format
= DIFF_FORMAT_PATCH
;
829 revs
->diffopt
.abbrev
= revs
->abbrev
;
830 if (diff_setup_done(&revs
->diffopt
) < 0)
831 return 4;//die("diff_setup_done failed");
833 compile_grep_patterns(&revs
->grep_filter
);
835 /*if (revs->reverse && revs->reflog_info)
836 die("cannot combine --reverse with --walk-reflogs");
837 if (revs->rewrite_parents && revs->children.name)
838 die("cannot combine --parents and --children");*/
841 * Limitations on the graph functionality
843 /*if (revs->reverse && revs->graph)
844 die("cannot combine --reverse with --graph");
846 if (revs->reflog_info && revs->graph)
847 die("cannot combine --walk-reflogs with --graph");*/
854 BOOL
ig_enum_files(const char *pszProjectPath
, const char *pszSubPath
, const char *prefix
, unsigned int nFlags
)
856 // reset all local vars of builtin-ls-files.c to default
866 line_terminator
= '\n';
880 const BOOL bSubDir
= pszSubPath
&& is_dir(pszProjectPath
, pszSubPath
);
882 LPCSTR pszSubPathSpec
= pszSubPath
;
883 if (bSubDir
&& !(nFlags
& WGEFF_SingleFile
))
885 int len
= strlen(pszSubPath
);
886 char *s
= (char*)malloc(len
+3);
887 strcpy(s
, pszSubPath
);
893 //int exc_given = 0, require_work_tree = 0;
894 struct dir_struct _dir
;
896 memset(&_dir
, 0, sizeof(_dir
));
898 memset(&l_dirTree
, 0, sizeof(l_dirTree
));
899 l_dirTree
.nStatus
= WGFS_Normal
; // root dir is always at least WGFS_Normal even if empty
900 if (pszSubPath
&& !(nFlags
& WGEFF_EmptyAsNormal
))
901 l_dirTree
.nStatus
= WGFS_Empty
;
903 // NOTE: to force names to be relative to project root dir (no mater what current dir is) set prefix_offset to 0
905 prefix_offset
= strlen(prefix
);
906 git_config(git_default_config
, NULL
);
908 struct dir_struct
*dir
= &_dir
;
911 argv
[0] = pszSubPathSpec
;
914 if (/*require_work_tree &&*/ !is_inside_work_tree())
917 pathspec
= get_pathspec(prefix
, argv
);
919 // Verify that the pathspec matches the prefix
921 prefix
= verify_pathspec(prefix
);
923 // Treat unmatching pathspec elements as errors
924 if (pathspec
&& error_unmatch
)
927 for (num
= 0; pathspec
[num
]; num
++)
929 ps_matched
= xcalloc(1, num
);
932 // vars used for path recursion check
933 int pathspec_len
= 0;
934 if (pathspec
&& *pathspec
)
936 // calc length of pathspec plus 1 for a / (unless it already ends with a slash)
937 pathspec_len
= strlen(*pathspec
);
938 if ((*pathspec
)[pathspec_len
-1] == '*')
940 if ((*pathspec
)[pathspec_len
-1] != '/')
943 const char *refpath
= (pathspec
&& *pathspec
) ? *pathspec
: "";
949 l_bNoRecurseDir
= FALSE
;
951 BOOL single_dir
= (nFlags
& WGEFF_SingleFile
) && (!pszSubPath
|| bSubDir
);
952 // adjust other flags for best performance / correct results when WGEFF_SingleFile is set
953 if (single_dir
&& (nFlags
& WGEFF_NoRecurse
))
954 l_bNoRecurseDir
= TRUE
;
955 if (nFlags
& WGEFF_SingleFile
)
957 nFlags
|= WGEFF_NoRecurse
;
959 nFlags
&= ~(WGEFF_DirStatusAll
|WGEFF_DirStatusDelta
);
963 nFlags
= (nFlags
& ~WGEFF_DirStatusAll
) | WGEFF_DirStatusDelta
;
965 if ( !(nFlags
& WGEFF_EmptyAsNormal
) )
966 l_dirTree
.nStatus
= WGFS_Empty
;
969 BOOL no_recurse
= nFlags
& WGEFF_NoRecurse
;
970 l_bNoRecurse
= no_recurse
;
971 l_bFullPath
= nFlags
& WGEFF_FullPath
;
972 l_bDirStatus
= nFlags
& (WGEFF_DirStatusDelta
|WGEFF_DirStatusAll
);
974 // when all dirs should be enumerated we need IsStatusRelevantForDirs to report files of any status as relevant
975 // otherwise only above normal are considered, which is slightly more efficient
976 l_nMinStatusRelevantForDirs
= (nFlags
& WGEFF_DirStatusAll
) ? WGFS_Empty
: (WGFS_Normal
+1);
978 // initial status of dirs
979 l_nEmptyDirStatus
= (nFlags
& WGEFF_EmptyAsNormal
) ? WGFS_Normal
: WGFS_Empty
;
981 l_bSkipNormalDirs
= ((nFlags
& (WGEFF_DirStatusDelta
|WGEFF_DirStatusAll
)) == WGEFF_DirStatusDelta
);
983 if (!(nFlags
& WGEFF_SingleFile
) && !l_bDirStatus
)
985 // no recursive dir status requested, list all dirs as unknown
987 l_nEmptyDirStatus
= l_nMinStatusRelevantForDirs
= WGFS_Unknown
;
988 l_bSkipNormalDirs
= FALSE
;
989 l_dirTree
.nStatus
= WGFS_Unknown
;
993 l_lpszFileName
= NULL
;
996 strcpy(l_sFullPathBuf
, pszProjectPath
);
998 LPSTR q
= l_sFullPathBuf
;
1005 // make sure it ends with a slash
1011 // save pointer to where file paths, with project-relative names, can be concatenated
1015 // shouldn't have any effect but set them to reflect what we want listed
1021 struct rev_info rev
;
1022 init_revisions(&rev
, prefix
);
1023 rev
.ignore_merges
= 0;
1026 l_bHasHistory
= !setup_revisions_lite(&rev
, "HEAD");
1030 preprocess_index(&rev
);
1032 prune_cache(prefix
);
1034 //if (pathspec && *pathspec) OutputDebugString(*pathspec);OutputDebugString(" (1)\r\n");
1035 //if (prefix) OutputDebugString(prefix);OutputDebugString(" (2)\r\n");
1041 for (i
=0; i
<active_nr
; i
++)
1043 struct cache_entry
*ce
= active_cache
[i
];
1047 int dtype
= ce_to_dtype(ce
);
1049 if (excluded(dir
, ce
->name
, &dtype
) != dir
->show_ignored
)
1051 if (ce
->ce_flags
& CE_UPDATE
)
1054 // skip file if not inside specified sub-path
1055 // this test was originally done in enum_ce_entry but in order to avoid unecessery lstat calls it was moved
1056 if (prefix_len
>= ce_namelen(ce
))
1057 die("git ls-files: internal error - cache entry not superset of prefix");
1058 if (pathspec
&& !pathspec_match(pathspec
, ps_matched
, ce
->name
, prefix_len
))
1061 if (single_dir
|| (no_recurse
&& is_subpath(refpath
, pathspec_len
, ce
->name
)))
1064 // this file would normally be skipped, but in order to determine correct dir status we need to process it
1065 update_dirs(ce
, pathspec_len
, FALSE
);
1070 err
= (ce
->ce_flags
& CE_IG_DELETED
) ? 1 : lstat(ce
->name
, &st
);
1072 if ( enum_ce_entry(ce
, err
? NULL
: &st
) )
1075 // normally (always?) conflicted/unmerged files will have 3 entries in a row (one in stage 1, one in 2 and one in 3)
1076 // skip redundant entries here
1081 for (j
=i
+1; j
<active_nr
; j
++)
1083 struct cache_entry
*nextce
= active_cache
[j
];
1085 if ( !is_ce_name_eq(ce
, nextce
) )
1092 if (l_bDirStatus
&& IsStatusRelevantForDirs(l_nLastStatus
))
1093 update_dirs(ce
, pathspec_len
, TRUE
);
1096 BOOL bIgnoreInitialized
= FALSE
;
1100 // check if root (pszSubPath) dir is ignored
1102 if (!bIgnoreInitialized
)
1104 setup_standard_excludes(dir
);
1105 bIgnoreInitialized
= TRUE
;
1108 char sDir
[MAX_PATH
];
1109 strcpy(sDir
, pszSubPath
);
1110 LPSTR p
= strrchr(sDir
, '/');
1114 // check for matching ignore for each subdir level
1115 p
= strchr(sDir
, '/');
1121 if ( excluded(dir
, sDir
, &dtype
) )
1123 l_dirTree
.nStatus
= WGFS_Ignored
;
1124 l_dirTree
.bExplicitlyIgnored
= TRUE
;
1130 p
= strchr(p
+1, '/');
1141 // enumerate unversioned files
1142 if ( !(nFlags
& WGEFF_SingleFile
) )
1144 const char *path
= ".", *base
= "";
1145 int baselen
= prefix_len
;
1148 path
= base
= prefix
;
1150 if (!bIgnoreInitialized
)
1152 setup_standard_excludes(dir
);
1153 bIgnoreInitialized
= TRUE
;
1155 dir
->collect_ignored
= 1;
1156 dir
->show_ignored
= 0;
1157 dir
->show_other_directories
= 0;
1158 dir
->hide_empty_directories
= 0;
1159 dir
->collect_all_ignored
= 1;
1160 dir
->collect_directories
= 1;
1161 dir
->no_recurse_readdir
= no_recurse
? 1 : 0;
1162 read_directory(dir
, path
, base
, baselen
, pathspec
);
1164 // if root dir is ignored, then all unversioned files under it are considered ignore
1165 enum_unversioned(dir
->entries
, dir
->nr
, l_dirTree
.bExplicitlyIgnored
);
1166 enum_unversioned(dir
->ignored
, dir
->ignored_nr
, TRUE
);
1168 else if (!single_dir
&& !l_nEnumeratedCached
)
1170 // get status of a single unversioned file
1172 if (!bIgnoreInitialized
)
1174 setup_standard_excludes(dir
);
1175 bIgnoreInitialized
= TRUE
;
1182 sFileName
= pszSubPath
+ prefix_offset
;
1186 strcpy(l_lpszFileName
, pszSubPath
);
1187 sFileName
= l_sFullPathBuf
;
1191 // if root dir is ignored, then all unversioned files under it are considered ignore
1192 if (!l_dirTree
.bExplicitlyIgnored
&& excluded(dir
, pszSubPath
, &dtype
))
1193 fputs("F I 0000000000000000000000000000000000000000 ", stdout
);
1195 fputs("F U 0000000000000000000000000000000000000000 ", stdout
);
1196 fputs(sFileName
, stdout
);
1204 LPCSTR lpszRootDir
="/";
1207 lpszRootDir
= l_sFullPathBuf
;
1210 strcpy(l_lpszFileName
, *pathspec
);
1211 l_lpszFileName
+= pathspec_len
;
1214 *l_lpszFileName
= 0;
1215 // remove trailng slash
1216 l_lpszFileName
[-1] = 0;
1218 else if (pathspec_len
)
1222 strcpy(l_sFullPathBuf
, *pathspec
);
1223 l_sFullPathBuf
[pathspec_len
-1] = '/';
1224 l_sFullPathBuf
[pathspec_len
] = 0;
1225 l_lpszFileName
= l_sFullPathBuf
;
1231 l_lpszFileName
= l_sFullPathBuf
;
1236 // enumerate single dir
1237 l_bSkipNormalDirs
= FALSE
;
1238 enum_dir(&l_dirTree
, lpszRootDir
);
1240 else if (!enum_dir(&l_dirTree
, lpszRootDir
) && l_dirTree
.children
)
1243 // re-add trailing slash
1244 l_lpszFileName
[-1] = '/';
1246 struct DirStatus
*p
= l_dirTree
.children
;
1250 if ( enum_dirs(p
, l_lpszFileName
) )
1253 while ( (p
= p
->next
) );
1264 * This merges the file listing in the directory cache index
1265 * with the actual working directory list, and shows different
1266 * combinations of the two.
1268 * Copyright (C) Linus Torvalds, 2005
1273 #include "builtin.h"
1277 static int show_deleted
;
1278 static int show_cached
;
1279 static int show_others
;
1280 static int show_stage
;
1281 static int show_unmerged
;
1282 static int show_modified
;
1283 static int show_killed
;
1284 static int show_valid_bit
;
1285 static int line_terminator
= '\n';
1287 static int prefix_len
;
1288 static int prefix_offset
;
1289 static const char **pathspec
;
1290 static int error_unmatch
;
1291 static char *ps_matched
;
1292 static const char *with_tree
;
1294 static const char *tag_cached
= "";
1295 static const char *tag_unmerged
= "";
1296 static const char *tag_removed
= "";
1297 static const char *tag_other
= "";
1298 static const char *tag_killed
= "";
1299 static const char *tag_modified
= "";
1303 * Match a pathspec against a filename. The first "skiplen" characters
1304 * are the common prefix
1306 int pathspec_match(const char **spec
, char *ps_matched
,
1307 const char *filename
, int skiplen
)
1311 while ((m
= *spec
++) != NULL
) {
1312 int matchlen
= strlen(m
+ skiplen
);
1316 if (!strncmp(m
+ skiplen
, filename
+ skiplen
, matchlen
)) {
1317 if (m
[skiplen
+ matchlen
- 1] == '/')
1319 switch (filename
[skiplen
+ matchlen
]) {
1320 case '/': case '\0':
1324 if (!fnmatch(m
+ skiplen
, filename
+ skiplen
, 0))
1337 static void show_dir_entry(const char *tag
, struct dir_entry
*ent
)
1339 int len
= prefix_len
;
1340 int offset
= prefix_offset
;
1342 if (len
>= ent
->len
)
1343 die("git ls-files: internal error - directory entry not superset of prefix");
1345 if (pathspec
&& !pathspec_match(pathspec
, ps_matched
, ent
->name
, len
))
1349 write_name_quoted(ent
->name
+ offset
, stdout
, line_terminator
);
1352 static void show_other_files(struct dir_struct
*dir
)
1356 for (i
= 0; i
< dir
->nr
; i
++) {
1357 struct dir_entry
*ent
= dir
->entries
[i
];
1358 if (!cache_name_is_other(ent
->name
, ent
->len
))
1360 show_dir_entry(tag_other
, ent
);
1364 static void show_killed_files(struct dir_struct
*dir
)
1367 for (i
= 0; i
< dir
->nr
; i
++) {
1368 struct dir_entry
*ent
= dir
->entries
[i
];
1370 int pos
, len
, killed
= 0;
1372 for (cp
= ent
->name
; cp
- ent
->name
< ent
->len
; cp
= sp
+ 1) {
1373 sp
= strchr(cp
, '/');
1375 /* If ent->name is prefix of an entry in the
1376 * cache, it will be killed.
1378 pos
= cache_name_pos(ent
->name
, ent
->len
);
1380 die("bug in show-killed-files");
1382 while (pos
< active_nr
&&
1383 ce_stage(active_cache
[pos
]))
1384 pos
++; /* skip unmerged */
1385 if (active_nr
<= pos
)
1387 /* pos points at a name immediately after
1388 * ent->name in the cache. Does it expect
1389 * ent->name to be a directory?
1391 len
= ce_namelen(active_cache
[pos
]);
1392 if ((ent
->len
< len
) &&
1393 !strncmp(active_cache
[pos
]->name
,
1394 ent
->name
, ent
->len
) &&
1395 active_cache
[pos
]->name
[ent
->len
] == '/')
1399 if (0 <= cache_name_pos(ent
->name
, sp
- ent
->name
)) {
1400 /* If any of the leading directories in
1401 * ent->name is registered in the cache,
1402 * ent->name will be killed.
1409 show_dir_entry(tag_killed
, dir
->entries
[i
]);
1413 static void show_ce_entry(const char *tag
, struct cache_entry
*ce
)
1415 int len
= prefix_len
;
1416 int offset
= prefix_offset
;
1418 if (len
>= ce_namelen(ce
))
1419 die("git ls-files: internal error - cache entry not superset of prefix");
1421 if (pathspec
&& !pathspec_match(pathspec
, ps_matched
, ce
->name
, len
))
1424 if (tag
&& *tag
&& show_valid_bit
&&
1425 (ce
->ce_flags
& CE_VALID
)) {
1426 static char alttag
[4];
1427 memcpy(alttag
, tag
, 3);
1428 if (isalpha(tag
[0]))
1429 alttag
[0] = tolower(tag
[0]);
1430 else if (tag
[0] == '?')
1444 printf("%s%06o %s %d\t",
1447 abbrev
? find_unique_abbrev(ce
->sha1
,abbrev
)
1448 : sha1_to_hex(ce
->sha1
),
1451 write_name_quoted(ce
->name
+ offset
, stdout
, line_terminator
);
1454 static void show_files(struct dir_struct
*dir
, const char *prefix
)
1458 /* For cached/deleted files we don't need to even do the readdir */
1459 if (show_others
|| show_killed
) {
1460 const char *path
= ".", *base
= "";
1461 int baselen
= prefix_len
;
1464 path
= base
= prefix
;
1465 read_directory(dir
, path
, base
, baselen
, pathspec
);
1467 show_other_files(dir
);
1469 show_killed_files(dir
);
1471 if (show_cached
| show_stage
) {
1472 for (i
= 0; i
< active_nr
; i
++) {
1473 struct cache_entry
*ce
= active_cache
[i
];
1474 int dtype
= ce_to_dtype(ce
);
1475 if (excluded(dir
, ce
->name
, &dtype
) != dir
->show_ignored
)
1477 if (show_unmerged
&& !ce_stage(ce
))
1479 if (ce
->ce_flags
& CE_UPDATE
)
1481 show_ce_entry(ce_stage(ce
) ? tag_unmerged
: tag_cached
, ce
);
1484 if (show_deleted
| show_modified
) {
1485 for (i
= 0; i
< active_nr
; i
++) {
1486 struct cache_entry
*ce
= active_cache
[i
];
1489 int dtype
= ce_to_dtype(ce
);
1490 if (excluded(dir
, ce
->name
, &dtype
) != dir
->show_ignored
)
1492 if (ce
->ce_flags
& CE_UPDATE
)
1494 err
= lstat(ce
->name
, &st
);
1495 if (show_deleted
&& err
)
1496 show_ce_entry(tag_removed
, ce
);
1497 if (show_modified
&& ce_modified(ce
, &st
, 0))
1498 show_ce_entry(tag_modified
, ce
);
1504 * Prune the index to only contain stuff starting with "prefix"
1506 static void prune_cache(const char *prefix
)
1508 int pos
= cache_name_pos(prefix
, prefix_len
);
1509 unsigned int first
, last
;
1513 memmove(active_cache
, active_cache
+ pos
,
1514 (active_nr
- pos
) * sizeof(struct cache_entry
*));
1518 while (last
> first
) {
1519 int next
= (last
+ first
) >> 1;
1520 struct cache_entry
*ce
= active_cache
[next
];
1521 if (!strncmp(ce
->name
, prefix
, prefix_len
)) {
1530 static const char *verify_pathspec(const char *prefix
)
1532 const char **p
, *n
, *prev
;
1537 for (p
= pathspec
; (n
= *p
) != NULL
; p
++) {
1539 for (i
= 0; i
< max
; i
++) {
1541 if (prev
&& prev
[i
] != c
)
1543 if (!c
|| c
== '*' || c
== '?')
1556 if (prefix_offset
> max
|| memcmp(prev
, prefix
, prefix_offset
))
1557 die("git ls-files: cannot generate relative filenames containing '..'");
1560 return max
? xmemdupz(prev
, max
) : NULL
;
1564 * Read the tree specified with --with-tree option
1565 * (typically, HEAD) into stage #1 and then
1566 * squash them down to stage #0. This is used for
1567 * --error-unmatch to list and check the path patterns
1568 * that were given from the command line. We are not
1569 * going to write this index out.
1571 void overlay_tree_on_cache(const char *tree_name
, const char *prefix
)
1574 unsigned char sha1
[20];
1576 struct cache_entry
*last_stage0
= NULL
;
1579 if (get_sha1(tree_name
, sha1
))
1580 die("tree-ish %s not found.", tree_name
);
1581 tree
= parse_tree_indirect(sha1
);
1583 die("bad tree-ish %s", tree_name
);
1585 /* Hoist the unmerged entries up to stage #3 to make room */
1586 for (i
= 0; i
< active_nr
; i
++) {
1587 struct cache_entry
*ce
= active_cache
[i
];
1590 ce
->ce_flags
|= CE_STAGEMASK
;
1594 static const char *(matchbuf
[2]);
1595 matchbuf
[0] = prefix
;
1600 if (read_tree(tree
, 1, match
))
1601 die("unable to read tree entries %s", tree_name
);
1603 for (i
= 0; i
< active_nr
; i
++) {
1604 struct cache_entry
*ce
= active_cache
[i
];
1605 switch (ce_stage(ce
)) {
1613 * If there is stage #0 entry for this, we do not
1614 * need to show it. We use CE_UPDATE bit to mark
1618 !strcmp(last_stage0
->name
, ce
->name
))
1619 ce
->ce_flags
|= CE_UPDATE
;
1624 int report_path_error(const char *ps_matched
, const char **pathspec
, int prefix_offset
)
1627 * Make sure all pathspec matched; otherwise it is an error.
1629 int num
, errors
= 0;
1630 for (num
= 0; pathspec
[num
]; num
++) {
1631 int other
, found_dup
;
1633 if (ps_matched
[num
])
1636 * The caller might have fed identical pathspec
1637 * twice. Do not barf on such a mistake.
1639 for (found_dup
= other
= 0;
1640 !found_dup
&& pathspec
[other
];
1642 if (other
== num
|| !ps_matched
[other
])
1644 if (!strcmp(pathspec
[other
], pathspec
[num
]))
1646 * Ok, we have a match already.
1653 error("pathspec '%s' did not match any file(s) known to git.",
1654 pathspec
[num
] + prefix_offset
);
1660 static const char ls_files_usage
[] =
1661 "git ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
1662 "[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
1663 "[ --exclude-per-directory=<filename> ] [--exclude-standard] "
1664 "[--full-name] [--abbrev] [--] [<file>]*";
1666 int cmd_ls_files(int argc
, const char **argv
, const char *prefix
)
1669 int exc_given
= 0, require_work_tree
= 0;
1670 struct dir_struct dir
;
1672 memset(&dir
, 0, sizeof(dir
));
1674 prefix_offset
= strlen(prefix
);
1675 git_config(git_default_config
, NULL
);
1677 for (i
= 1; i
< argc
; i
++) {
1678 const char *arg
= argv
[i
];
1680 if (!strcmp(arg
, "--")) {
1684 if (!strcmp(arg
, "-z")) {
1685 line_terminator
= 0;
1688 if (!strcmp(arg
, "-t") || !strcmp(arg
, "-v")) {
1690 tag_unmerged
= "M ";
1692 tag_modified
= "C ";
1699 if (!strcmp(arg
, "-c") || !strcmp(arg
, "--cached")) {
1703 if (!strcmp(arg
, "-d") || !strcmp(arg
, "--deleted")) {
1707 if (!strcmp(arg
, "-m") || !strcmp(arg
, "--modified")) {
1709 require_work_tree
= 1;
1712 if (!strcmp(arg
, "-o") || !strcmp(arg
, "--others")) {
1714 require_work_tree
= 1;
1717 if (!strcmp(arg
, "-i") || !strcmp(arg
, "--ignored")) {
1718 dir
.show_ignored
= 1;
1719 require_work_tree
= 1;
1722 if (!strcmp(arg
, "-s") || !strcmp(arg
, "--stage")) {
1726 if (!strcmp(arg
, "-k") || !strcmp(arg
, "--killed")) {
1728 require_work_tree
= 1;
1731 if (!strcmp(arg
, "--directory")) {
1732 dir
.show_other_directories
= 1;
1735 if (!strcmp(arg
, "--no-empty-directory")) {
1736 dir
.hide_empty_directories
= 1;
1739 if (!strcmp(arg
, "-u") || !strcmp(arg
, "--unmerged")) {
1740 /* There's no point in showing unmerged unless
1741 * you also show the stage information.
1747 if (!strcmp(arg
, "-x") && i
+1 < argc
) {
1749 add_exclude(argv
[++i
], "", 0, &dir
.exclude_list
[EXC_CMDL
]);
1752 if (!prefixcmp(arg
, "--exclude=")) {
1754 add_exclude(arg
+10, "", 0, &dir
.exclude_list
[EXC_CMDL
]);
1757 if (!strcmp(arg
, "-X") && i
+1 < argc
) {
1759 add_excludes_from_file(&dir
, argv
[++i
]);
1762 if (!prefixcmp(arg
, "--exclude-from=")) {
1764 add_excludes_from_file(&dir
, arg
+15);
1767 if (!prefixcmp(arg
, "--exclude-per-directory=")) {
1769 dir
.exclude_per_dir
= arg
+ 24;
1772 if (!strcmp(arg
, "--exclude-standard")) {
1774 setup_standard_excludes(&dir
);
1777 if (!strcmp(arg
, "--full-name")) {
1781 if (!strcmp(arg
, "--error-unmatch")) {
1785 if (!prefixcmp(arg
, "--with-tree=")) {
1786 with_tree
= arg
+ 12;
1789 if (!prefixcmp(arg
, "--abbrev=")) {
1790 abbrev
= strtoul(arg
+9, NULL
, 10);
1791 if (abbrev
&& abbrev
< MINIMUM_ABBREV
)
1792 abbrev
= MINIMUM_ABBREV
;
1793 else if (abbrev
> 40)
1797 if (!strcmp(arg
, "--abbrev")) {
1798 abbrev
= DEFAULT_ABBREV
;
1802 usage(ls_files_usage
);
1806 if (require_work_tree
&& !is_inside_work_tree())
1809 pathspec
= get_pathspec(prefix
, argv
+ i
);
1811 /* Verify that the pathspec matches the prefix */
1813 prefix
= verify_pathspec(prefix
);
1815 /* Treat unmatching pathspec elements as errors */
1816 if (pathspec
&& error_unmatch
) {
1818 for (num
= 0; pathspec
[num
]; num
++)
1820 ps_matched
= xcalloc(1, num
);
1823 if (dir
.show_ignored
&& !exc_given
) {
1824 fprintf(stderr
, "%s: --ignored needs some exclude pattern\n",
1829 /* With no flags, we default to showing the cached files */
1830 if (!(show_stage
| show_deleted
| show_others
| show_unmerged
|
1831 show_killed
| show_modified
))
1836 prune_cache(prefix
);
1839 * Basic sanity check; show-stages and show-unmerged
1840 * would not make any sense with this option.
1842 if (show_stage
|| show_unmerged
)
1843 die("ls-files --with-tree is incompatible with -s or -u");
1844 overlay_tree_on_cache(with_tree
, prefix
);
1846 show_files(&dir
, prefix
);
1850 bad
= report_path_error(ps_matched
, pathspec
, prefix_offset
);
1852 fprintf(stderr
, "Did you forget to 'git add'?\n");