8 // uses the ls-files code
9 #include "builtin-ls-files.c"
14 // cached last access, to speed up searches (since we get a sorted list from git code)
15 struct DirStatus
*pLastAccessedChild
;
19 struct DirStatus
*next
;
20 struct DirStatus
*children
;
21 struct DirStatus
*parent
;
24 BOOL bExplicitlyIgnored
;
27 static struct DirStatus l_dirTree
;
30 static BOOL l_bNoRecurse
;
31 static int l_nMinStatusRelevantForDirs
;
32 static BOOL l_bSkipNormalDirs
;
33 static int l_nEmptyDirStatus
;
34 static BOOL l_bNoRecurseDir
;
36 static BOOL l_bFullPath
;
37 static char l_sFullPathBuf
[2048];
38 static LPSTR l_lpszFileName
;
40 static BOOL l_bDirStatus
;
41 static int l_nLastStatus
;
42 static int l_nEnumeratedCached
= 0;
45 static inline char GetStatusChar(int nStatus
)
49 case WGFS_Normal
: return 'N';
50 case WGFS_Modified
: return 'M';
51 case WGFS_Staged
: return 'S';
52 case WGFS_Added
: return 'A';
53 case WGFS_Conflicted
: return 'C';
54 case WGFS_Deleted
: return 'D';
56 case WGFS_Unversioned
: return 'U';
57 case WGFS_Ignored
: return 'I';
58 case WGFS_Unknown
: return '?';
59 case WGFS_Empty
: return 'E';
67 static BOOL
enum_ce_entry(struct cache_entry
*ce
, struct stat
*st
)
69 // is this of any use (ce->ce_flags & CE_VALID) ?
75 sFileName
= ce
->name
+ prefix_offset
;
79 strcpy(l_lpszFileName
, ce
->name
);
80 sFileName
= l_sFullPathBuf
;
83 const int nStage
= ce_stage(ce
);
85 int nStatus
= WGFS_Unknown
;
87 nStatus
= WGFS_Deleted
;
89 nStatus
= WGFS_Conflicted
;
90 else if ( ce_modified(ce
, st
, 0) )
91 nStatus
= WGFS_Modified
;
93 nStatus
= WGFS_Normal
;
94 l_nLastStatus
= nStatus
;
96 // output format: "F status sha1 filename"
99 fputc(GetStatusChar(nStatus
), stdout
);
101 fputsha1(ce
->sha1
, stdout
);
103 fputs(sFileName
, stdout
);
106 l_nEnumeratedCached
++;
111 // same as enum except it skips enumeration and just determines status (used for recursive folder status)
112 // returns TRUE if file was processed
113 static BOOL
process_ce_entry_status(struct cache_entry
*ce
, struct stat
*st
)
115 // is this of any use (ce->ce_flags & CE_VALID) ?
119 ef.sFileName = ce->name + offset;
123 strcpy(l_lpszFileName, ce->name);
124 ef.sFileName = l_sFullPathBuf;
127 const int nStage
= ce_stage(ce
);
129 UINT nStatus
= WGFS_Unknown
;
131 nStatus
= WGFS_Deleted
;
133 nStatus
= WGFS_Conflicted
;
134 else if ( ce_modified(ce
, st
, 0) )
135 nStatus
= WGFS_Modified
;
137 nStatus
= WGFS_Normal
;
138 l_nLastStatus
= nStatus
;
140 //ef.nStage = st ? ce_stage(ce) : 0;
142 //ef.sha1 = ce->sha1;
148 static BOOL
is_dir(const char *sProjectPath
, const char *sSubPath
);
149 static void update_dirs_unversioned(struct dir_entry
*ce
, int nPathNameOffset
);
151 static void enum_unversioned(struct dir_entry
**files
, int nr
, BOOL bIgnored
)
156 struct dir_entry
*ent
= files
[i
];
158 // make sure to skip dirs (unless an explicitly ignored dir)
159 if (!bIgnored
&& ent
->name
[ent
->len
-1] == '/')
162 if (!cache_name_is_other(ent
->name
, ent
->len
))
165 int len
= prefix_len
;
168 die("igit status: internal error - directory entry not superset of prefix");
170 if (pathspec
&& !pathspec_match(pathspec
, ps_matched
, ent
->name
, len
))
177 sFileName
= ent
->name
+ prefix_offset
;
181 strcpy(l_lpszFileName
, ent
->name
);
182 sFileName
= l_sFullPathBuf
;
187 // because we specified collect_all_ignored this may be a directory that was ignored
188 if ( !is_dir(".", ent
->name
) )
192 l_nLastStatus
= WGFS_Ignored
;
193 update_dirs_unversioned(ent
, 0);
196 fputs("F I 0000000000000000000000000000000000000000 ", stdout
);
202 const int nOrgEmptyDirStatus
= l_nEmptyDirStatus
;
203 l_nLastStatus
= l_nEmptyDirStatus
= WGFS_Ignored
;
204 update_dirs_unversioned(ent
, 0);
205 l_nEmptyDirStatus
= nOrgEmptyDirStatus
;
215 l_nLastStatus
= WGFS_Unversioned
;
216 update_dirs_unversioned(ent
, 0);
219 fputs("F U 0000000000000000000000000000000000000000 ", stdout
);
221 fputs(sFileName
, stdout
);
227 static inline BOOL
enum_dir(struct DirStatus
*dir
, LPCSTR lpszPathName
)
229 if (dir
->nStatus
== WGFS_Normal
&& l_bSkipNormalDirs
)
232 // output format: "D status pathname"
235 fputc(GetStatusChar(dir
->nStatus
), stdout
);
237 fputs(lpszPathName
, stdout
);
243 static BOOL
enum_dirs(struct DirStatus
*dir
, LPSTR sPathNameBuf
)
245 const int len
= strlen(dir
->lpszName
);
246 memcpy(sPathNameBuf
, dir
->lpszName
, len
);
250 if ( enum_dir(dir
, l_bFullPath
? l_sFullPathBuf
: l_sFullPathBuf
+prefix_offset
) )
253 if (!l_bNoRecurse
&& dir
->children
)
257 *sPathNameBuf
++ = '/';
264 if ( enum_dirs(dir
, sPathNameBuf
) )
275 static struct DirStatus
* GetSubDir(struct DirStatus
*dir
, LPCSTR lpszName
, int nNameLenInclTerminator
)
277 // check for cached access
278 if (dir
->pLastAccessedChild
279 && !strcmp(dir
->pLastAccessedChild
->lpszName
, lpszName
))
281 return dir
->pLastAccessedChild
;
285 struct DirStatus
*p
= dir
->children
;
286 struct DirStatus
*last
= NULL
;
289 if ( !strcmp(p
->lpszName
, lpszName
) )
290 return (dir
->pLastAccessedChild
= p
);
296 // dir not accessed before, create new entry
297 // TODO: do more efficient allocator (allocate larger pools, they can still be fire and forget and let our garbage collector clean up)
298 p
= dir
->pLastAccessedChild
= (struct DirStatus
*) malloc(sizeof(struct DirStatus
) + ((nNameLenInclTerminator
+3)&~3));
300 p
->pLastAccessedChild
= NULL
;
301 p
->lpszName
= (char*)p
+ sizeof(struct DirStatus
);
305 if (l_nEmptyDirStatus
!= WGFS_Ignored
)
307 p
->bExplicitlyIgnored
= dir
->bExplicitlyIgnored
;
308 p
->nStatus
= (p
->bExplicitlyIgnored
&& l_nEmptyDirStatus
< WGFS_Ignored
) ? WGFS_Ignored
: l_nEmptyDirStatus
;
312 p
->nStatus
= WGFS_Ignored
;
313 p
->bExplicitlyIgnored
= TRUE
;
323 memcpy((char*)p
->lpszName
, lpszName
, nNameLenInclTerminator
);
329 static inline BOOL
IsStatusRelevantForDirs(int nStatus
)
331 return nStatus
>= l_nMinStatusRelevantForDirs
&& nStatus
!= WGFS_Deleted
;
335 static void update_dirs_unversioned_rec(LPCSTR lpszFileName
, UINT nDirLen
, struct dir_entry
*ce
, struct DirStatus
*parentDir
)
337 const int nDirLen1
= nDirLen
+1;
339 memcpy(s
, lpszFileName
, nDirLen
);
342 struct DirStatus
*dir
= GetSubDir(parentDir
, s
, nDirLen1
);
343 //ASSERT(dir != NULL);
345 // TODO: if 'conflicted' status is added then need to check for that as highest prio
346 if (dir
->nStatus
>= WGFS_Modified
&& l_bNoRecurse
)
348 // no further processing needed
352 // process next subdir in lpszFileName
354 lpszFileName
+= nDirLen1
;
356 LPCSTR p
= strchr(lpszFileName
, '/');
359 // no more dirs in pathname (ie we are in the dir the file is located)
361 const int nFileStatus
= l_nLastStatus
;
363 if (nFileStatus
> dir
->nStatus
)
365 // update status on dir and all parents
368 if (nFileStatus
> dir
->nStatus
)
369 dir
->nStatus
= nFileStatus
;
371 while ( (dir
= dir
->parent
) );
374 else if (lpszFileName
!= p
) // quick check to make sure we're not left with a "/" filename
376 update_dirs_unversioned_rec(lpszFileName
, (UINT
)(p
-lpszFileName
), ce
, dir
);
380 static void update_dirs_unversioned(struct dir_entry
*ce
, int nPathNameOffset
)
382 // filename relative to enumerated path
383 LPCSTR lpszFileName
= ce
->name
+ nPathNameOffset
;
385 LPCSTR p
= strchr(lpszFileName
, '/');
386 if (p
<= lpszFileName
)
388 // file is not in sub-dir
390 const int nFileStatus
= l_nLastStatus
;
392 if (nFileStatus
> l_dirTree
.nStatus
)
393 l_dirTree
.nStatus
= nFileStatus
;
398 if (!l_bNoRecurseDir
)
400 update_dirs_unversioned_rec(lpszFileName
, (UINT
)(p
-lpszFileName
), ce
, &l_dirTree
);
405 static void update_dirs_rec(LPCSTR lpszFileName
, UINT nDirLen
, struct cache_entry
*ce
, BOOL bStatusCached
, struct DirStatus
*parentDir
)
407 const int nDirLen1
= nDirLen
+1;
409 memcpy(s
, lpszFileName
, nDirLen
);
412 struct DirStatus
*dir
= GetSubDir(parentDir
, s
, nDirLen1
);
413 //ASSERT(dir != NULL);
415 // TODO: if 'conflicted' status is added then need to check for that as highest prio
416 if (dir
->nStatus
>= WGFS_Modified
&& l_bNoRecurse
)
418 // no further processing needed
422 // process next subdir in lpszFileName
424 lpszFileName
+= nDirLen1
;
426 LPCSTR p
= strchr(lpszFileName
, '/');
429 // no more dirs in pathname (ie we are in the dir the file is located)
433 // file status not determined yet, do it now
435 int err
= lstat(ce
->name
, &st
);
436 if (!process_ce_entry_status(ce
, err
? NULL
: &st
) || !IsStatusRelevantForDirs(l_nLastStatus
))
439 const int nFileStatus
= l_nLastStatus
;
441 if (nFileStatus
> dir
->nStatus
)
443 // update status on dir and all parents
446 if (nFileStatus
> dir
->nStatus
)
447 dir
->nStatus
= nFileStatus
;
449 while ( (dir
= dir
->parent
) );
452 else if (lpszFileName
!= p
) // quick check to make sure we're not left with a "/" filename
454 update_dirs_rec(lpszFileName
, (UINT
)(p
-lpszFileName
), ce
, bStatusCached
, dir
);
458 static void update_dirs(struct cache_entry
*ce
, int nPathNameOffset
, BOOL bStatusCached
)
460 // filename relative to enumerated path
461 LPCSTR lpszFileName
= ce
->name
+ nPathNameOffset
;
463 LPCSTR p
= strchr(lpszFileName
, '/');
464 if (p
<= lpszFileName
)
466 // file is not in sub-dir
470 // file status not determined yet, do it now
472 int err
= lstat(ce
->name
, &st
);
473 if (!process_ce_entry_status(ce
, err
? NULL
: &st
) || !IsStatusRelevantForDirs(l_nLastStatus
))
476 const int nFileStatus
= l_nLastStatus
;
478 if (nFileStatus
> l_dirTree
.nStatus
)
479 l_dirTree
.nStatus
= nFileStatus
;
484 if (!l_bNoRecurseDir
)
486 update_dirs_rec(lpszFileName
, (UINT
)(p
-lpszFileName
), ce
, bStatusCached
, &l_dirTree
);
491 static inline BOOL
is_subpath(const char *sPath
, int nPathLen
, const char *sFile
)
493 return strchr(sFile
+ nPathLen
, '/') != NULL
;
496 static BOOL
is_dir(const char *sProjectPath
, const char *sSubPath
)
500 strcpy(s
, sProjectPath
);
509 // make sure it ends with a slash
513 // backslashify sub-path
522 int err
= lstat(s
, &st
);
524 return (!err
&& S_ISDIR(st
.st_mode
));
527 static inline BOOL
is_ce_name_eq(struct cache_entry
*ce1
, struct cache_entry
*ce2
)
529 const size_t len1
= ce1
->ce_flags
& CE_NAMEMASK
;
530 const size_t len2
= ce2
->ce_flags
& CE_NAMEMASK
;
532 return (len1
== len2
) ? !strcmp(ce1
->name
, ce2
->name
) : FALSE
;
536 BOOL
ig_enum_files(const char *pszProjectPath
, const char *pszSubPath
, const char *prefix
, unsigned int nFlags
)
538 // reset all local vars of builtin-ls-files.c to default
548 line_terminator
= '\n';
562 const BOOL bSubDir
= pszSubPath
&& is_dir(pszProjectPath
, pszSubPath
);
564 LPCSTR pszSubPathSpec
= pszSubPath
;
565 if (bSubDir
&& !(nFlags
& WGEFF_SingleFile
))
567 int len
= strlen(pszSubPath
);
568 char *s
= (char*)malloc(len
+3);
569 strcpy(s
, pszSubPath
);
575 //int exc_given = 0, require_work_tree = 0;
576 struct dir_struct _dir
;
578 memset(&_dir
, 0, sizeof(_dir
));
580 memset(&l_dirTree
, 0, sizeof(l_dirTree
));
581 l_dirTree
.nStatus
= WGFS_Normal
; // root dir is always at least WGFS_Normal even if empty
582 if (pszSubPath
&& !(nFlags
& WGEFF_EmptyAsNormal
))
583 l_dirTree
.nStatus
= WGFS_Empty
;
585 // NOTE: to force names to be relative to project root dir (no mater what current dir is) set prefix_offset to 0
587 prefix_offset
= strlen(prefix
);
588 git_config(git_default_config
, NULL
);
590 struct dir_struct
*dir
= &_dir
;
593 argv
[0] = pszSubPathSpec
;
596 if (/*require_work_tree &&*/ !is_inside_work_tree())
599 pathspec
= get_pathspec(prefix
, argv
);
601 // Verify that the pathspec matches the prefix
603 prefix
= verify_pathspec(prefix
);
605 // Treat unmatching pathspec elements as errors
606 if (pathspec
&& error_unmatch
)
609 for (num
= 0; pathspec
[num
]; num
++)
611 ps_matched
= xcalloc(1, num
);
614 // vars used for path recursion check
615 int pathspec_len
= 0;
616 if (pathspec
&& *pathspec
)
618 // calc length of pathspec plus 1 for a / (unless it already ends with a slash)
619 pathspec_len
= strlen(*pathspec
);
620 if ((*pathspec
)[pathspec_len
-1] == '*')
622 if ((*pathspec
)[pathspec_len
-1] != '/')
625 const char *refpath
= (pathspec
&& *pathspec
) ? *pathspec
: "";
631 l_bNoRecurseDir
= FALSE
;
633 BOOL single_dir
= (nFlags
& WGEFF_SingleFile
) && (!pszSubPath
|| bSubDir
);
634 // adjust other flags for best performance / correct results when WGEFF_SingleFile is set
635 if (single_dir
&& (nFlags
& WGEFF_NoRecurse
))
636 l_bNoRecurseDir
= TRUE
;
637 if (nFlags
& WGEFF_SingleFile
)
639 nFlags
|= WGEFF_NoRecurse
;
641 nFlags
&= ~(WGEFF_DirStatusAll
|WGEFF_DirStatusDelta
);
645 nFlags
= (nFlags
& ~WGEFF_DirStatusAll
) | WGEFF_DirStatusDelta
;
647 if ( !(nFlags
& WGEFF_EmptyAsNormal
) )
648 l_dirTree
.nStatus
= WGFS_Empty
;
651 BOOL no_recurse
= nFlags
& WGEFF_NoRecurse
;
652 l_bNoRecurse
= no_recurse
;
653 l_bFullPath
= nFlags
& WGEFF_FullPath
;
654 l_bDirStatus
= nFlags
& (WGEFF_DirStatusDelta
|WGEFF_DirStatusAll
);
656 // when all dirs should be enumerated we need IsStatusRelevantForDirs to report files of any status as relevant
657 // otherwise only above normal are considered, which is slightly more efficient
658 l_nMinStatusRelevantForDirs
= (nFlags
& WGEFF_DirStatusAll
) ? WGFS_Empty
: (WGFS_Normal
+1);
660 // initial status of dirs
661 l_nEmptyDirStatus
= (nFlags
& WGEFF_EmptyAsNormal
) ? WGFS_Normal
: WGFS_Empty
;
663 l_bSkipNormalDirs
= ((nFlags
& (WGEFF_DirStatusDelta
|WGEFF_DirStatusAll
)) == WGEFF_DirStatusDelta
);
666 l_lpszFileName
= NULL
;
669 strcpy(l_sFullPathBuf
, pszProjectPath
);
671 LPSTR q
= l_sFullPathBuf
;
678 // make sure it ends with a slash
684 // save pointer to where file paths, with project-relative names, can be concatenated
688 // shouldn't have any effect but set them to reflect what we want listed
698 //if (pathspec && *pathspec) OutputDebugString(*pathspec);OutputDebugString(" (1)\r\n");
699 //if (prefix) OutputDebugString(prefix);OutputDebugString(" (2)\r\n");
705 for (i
=0; i
<active_nr
; i
++)
707 struct cache_entry
*ce
= active_cache
[i
];
711 int dtype
= ce_to_dtype(ce
);
713 if (excluded(dir
, ce
->name
, &dtype
) != dir
->show_ignored
)
715 if (ce
->ce_flags
& CE_UPDATE
)
718 // skip file if not inside specified sub-path
719 // this test was originally done in enum_ce_entry but in order to avoid unecessery lstat calls it was moved
720 if (prefix_len
>= ce_namelen(ce
))
721 die("git ls-files: internal error - cache entry not superset of prefix");
722 if (pathspec
&& !pathspec_match(pathspec
, ps_matched
, ce
->name
, prefix_len
))
725 if (single_dir
|| (no_recurse
&& is_subpath(refpath
, pathspec_len
, ce
->name
)))
728 // this file would normally be skipped, but in order to determine correct dir status we need to process it
729 update_dirs(ce
, pathspec_len
, FALSE
);
734 err
= lstat(ce
->name
, &st
);
736 if ( enum_ce_entry(ce
, err
? NULL
: &st
) )
739 // normally (always?) conflicted/unmerged files will have 3 entries in a row (one in stage 1, one in 2 and one in 3)
740 // skip redundant entries here
745 for (j
=i
+1; j
<active_nr
; j
++)
747 struct cache_entry
*nextce
= active_cache
[j
];
749 if ( !is_ce_name_eq(ce
, nextce
) )
756 if (l_bDirStatus
&& IsStatusRelevantForDirs(l_nLastStatus
))
757 update_dirs(ce
, pathspec_len
, TRUE
);
760 BOOL bIgnoreInitialized
= FALSE
;
764 // check if root (pszSubPath) dir is ignored
766 if (!bIgnoreInitialized
)
768 setup_standard_excludes(dir
);
769 bIgnoreInitialized
= TRUE
;
773 strcpy(sDir
, pszSubPath
);
774 LPSTR p
= strrchr(sDir
, '/');
778 // check for matching ignore for each subdir level
779 p
= strchr(sDir
, '/');
785 if ( excluded(dir
, sDir
, &dtype
) )
787 l_dirTree
.nStatus
= WGFS_Ignored
;
788 l_dirTree
.bExplicitlyIgnored
= TRUE
;
794 p
= strchr(p
+1, '/');
805 // enumerate unversioned files
806 if ( !(nFlags
& WGEFF_SingleFile
) )
808 const char *path
= ".", *base
= "";
809 int baselen
= prefix_len
;
812 path
= base
= prefix
;
814 if (!bIgnoreInitialized
)
816 setup_standard_excludes(dir
);
817 bIgnoreInitialized
= TRUE
;
819 dir
->collect_ignored
= 1;
820 dir
->show_ignored
= 0;
821 dir
->show_other_directories
= 0;
822 dir
->hide_empty_directories
= 0;
823 dir
->collect_all_ignored
= 1;
824 dir
->no_recurse_readdir
= no_recurse
? 1 : 0;
825 read_directory(dir
, path
, base
, baselen
, pathspec
);
827 // if root dir is ignored, then all unversioned files under it are considered ignore
828 enum_unversioned(dir
->entries
, dir
->nr
, l_dirTree
.bExplicitlyIgnored
);
829 enum_unversioned(dir
->ignored
, dir
->ignored_nr
, TRUE
);
831 else if (!single_dir
&& !l_nEnumeratedCached
)
833 // get status of a single unversioned file
835 if (!bIgnoreInitialized
)
837 setup_standard_excludes(dir
);
838 bIgnoreInitialized
= TRUE
;
845 sFileName
= pszSubPath
+ prefix_offset
;
849 strcpy(l_lpszFileName
, pszSubPath
);
850 sFileName
= l_sFullPathBuf
;
854 // if root dir is ignored, then all unversioned files under it are considered ignore
855 if (!l_dirTree
.bExplicitlyIgnored
&& excluded(dir
, pszSubPath
, &dtype
))
856 fputs("F I 0000000000000000000000000000000000000000 ", stdout
);
858 fputs("F U 0000000000000000000000000000000000000000 ", stdout
);
859 fputs(sFileName
, stdout
);
867 LPCSTR lpszRootDir
="/";
870 lpszRootDir
= l_sFullPathBuf
;
873 strcpy(l_lpszFileName
, *pathspec
);
874 l_lpszFileName
+= pathspec_len
;
878 // remove trailng slash
879 l_lpszFileName
[-1] = 0;
881 else if (pathspec_len
)
883 lpszRootDir
= pszSubPath
;
885 strcpy(l_sFullPathBuf
, *pathspec
);
886 l_sFullPathBuf
[pathspec_len
-1] = '/';
887 l_sFullPathBuf
[pathspec_len
] = 0;
888 l_lpszFileName
= l_sFullPathBuf
;
894 l_lpszFileName
= l_sFullPathBuf
;
899 // enumerate single dir
900 l_bSkipNormalDirs
= FALSE
;
901 enum_dir(&l_dirTree
, lpszRootDir
);
903 else if (!enum_dir(&l_dirTree
, lpszRootDir
) && l_dirTree
.children
)
906 // re-add trailing slash
907 l_lpszFileName
[-1] = '/';
909 struct DirStatus
*p
= l_dirTree
.children
;
913 if ( enum_dirs(p
, l_lpszFileName
) )
916 while ( (p
= p
->next
) );
927 * This merges the file listing in the directory cache index
928 * with the actual working directory list, and shows different
929 * combinations of the two.
931 * Copyright (C) Linus Torvalds, 2005
940 static int show_deleted
;
941 static int show_cached
;
942 static int show_others
;
943 static int show_stage
;
944 static int show_unmerged
;
945 static int show_modified
;
946 static int show_killed
;
947 static int show_valid_bit
;
948 static int line_terminator
= '\n';
950 static int prefix_len
;
951 static int prefix_offset
;
952 static const char **pathspec
;
953 static int error_unmatch
;
954 static char *ps_matched
;
955 static const char *with_tree
;
957 static const char *tag_cached
= "";
958 static const char *tag_unmerged
= "";
959 static const char *tag_removed
= "";
960 static const char *tag_other
= "";
961 static const char *tag_killed
= "";
962 static const char *tag_modified
= "";
966 * Match a pathspec against a filename. The first "skiplen" characters
967 * are the common prefix
969 int pathspec_match(const char **spec
, char *ps_matched
,
970 const char *filename
, int skiplen
)
974 while ((m
= *spec
++) != NULL
) {
975 int matchlen
= strlen(m
+ skiplen
);
979 if (!strncmp(m
+ skiplen
, filename
+ skiplen
, matchlen
)) {
980 if (m
[skiplen
+ matchlen
- 1] == '/')
982 switch (filename
[skiplen
+ matchlen
]) {
987 if (!fnmatch(m
+ skiplen
, filename
+ skiplen
, 0))
1000 static void show_dir_entry(const char *tag
, struct dir_entry
*ent
)
1002 int len
= prefix_len
;
1003 int offset
= prefix_offset
;
1005 if (len
>= ent
->len
)
1006 die("git ls-files: internal error - directory entry not superset of prefix");
1008 if (pathspec
&& !pathspec_match(pathspec
, ps_matched
, ent
->name
, len
))
1012 write_name_quoted(ent
->name
+ offset
, stdout
, line_terminator
);
1015 static void show_other_files(struct dir_struct
*dir
)
1019 for (i
= 0; i
< dir
->nr
; i
++) {
1020 struct dir_entry
*ent
= dir
->entries
[i
];
1021 if (!cache_name_is_other(ent
->name
, ent
->len
))
1023 show_dir_entry(tag_other
, ent
);
1027 static void show_killed_files(struct dir_struct
*dir
)
1030 for (i
= 0; i
< dir
->nr
; i
++) {
1031 struct dir_entry
*ent
= dir
->entries
[i
];
1033 int pos
, len
, killed
= 0;
1035 for (cp
= ent
->name
; cp
- ent
->name
< ent
->len
; cp
= sp
+ 1) {
1036 sp
= strchr(cp
, '/');
1038 /* If ent->name is prefix of an entry in the
1039 * cache, it will be killed.
1041 pos
= cache_name_pos(ent
->name
, ent
->len
);
1043 die("bug in show-killed-files");
1045 while (pos
< active_nr
&&
1046 ce_stage(active_cache
[pos
]))
1047 pos
++; /* skip unmerged */
1048 if (active_nr
<= pos
)
1050 /* pos points at a name immediately after
1051 * ent->name in the cache. Does it expect
1052 * ent->name to be a directory?
1054 len
= ce_namelen(active_cache
[pos
]);
1055 if ((ent
->len
< len
) &&
1056 !strncmp(active_cache
[pos
]->name
,
1057 ent
->name
, ent
->len
) &&
1058 active_cache
[pos
]->name
[ent
->len
] == '/')
1062 if (0 <= cache_name_pos(ent
->name
, sp
- ent
->name
)) {
1063 /* If any of the leading directories in
1064 * ent->name is registered in the cache,
1065 * ent->name will be killed.
1072 show_dir_entry(tag_killed
, dir
->entries
[i
]);
1076 static void show_ce_entry(const char *tag
, struct cache_entry
*ce
)
1078 int len
= prefix_len
;
1079 int offset
= prefix_offset
;
1081 if (len
>= ce_namelen(ce
))
1082 die("git ls-files: internal error - cache entry not superset of prefix");
1084 if (pathspec
&& !pathspec_match(pathspec
, ps_matched
, ce
->name
, len
))
1087 if (tag
&& *tag
&& show_valid_bit
&&
1088 (ce
->ce_flags
& CE_VALID
)) {
1089 static char alttag
[4];
1090 memcpy(alttag
, tag
, 3);
1091 if (isalpha(tag
[0]))
1092 alttag
[0] = tolower(tag
[0]);
1093 else if (tag
[0] == '?')
1107 printf("%s%06o %s %d\t",
1110 abbrev
? find_unique_abbrev(ce
->sha1
,abbrev
)
1111 : sha1_to_hex(ce
->sha1
),
1114 write_name_quoted(ce
->name
+ offset
, stdout
, line_terminator
);
1117 static void show_files(struct dir_struct
*dir
, const char *prefix
)
1121 /* For cached/deleted files we don't need to even do the readdir */
1122 if (show_others
|| show_killed
) {
1123 const char *path
= ".", *base
= "";
1124 int baselen
= prefix_len
;
1127 path
= base
= prefix
;
1128 read_directory(dir
, path
, base
, baselen
, pathspec
);
1130 show_other_files(dir
);
1132 show_killed_files(dir
);
1134 if (show_cached
| show_stage
) {
1135 for (i
= 0; i
< active_nr
; i
++) {
1136 struct cache_entry
*ce
= active_cache
[i
];
1137 int dtype
= ce_to_dtype(ce
);
1138 if (excluded(dir
, ce
->name
, &dtype
) != dir
->show_ignored
)
1140 if (show_unmerged
&& !ce_stage(ce
))
1142 if (ce
->ce_flags
& CE_UPDATE
)
1144 show_ce_entry(ce_stage(ce
) ? tag_unmerged
: tag_cached
, ce
);
1147 if (show_deleted
| show_modified
) {
1148 for (i
= 0; i
< active_nr
; i
++) {
1149 struct cache_entry
*ce
= active_cache
[i
];
1152 int dtype
= ce_to_dtype(ce
);
1153 if (excluded(dir
, ce
->name
, &dtype
) != dir
->show_ignored
)
1155 if (ce
->ce_flags
& CE_UPDATE
)
1157 err
= lstat(ce
->name
, &st
);
1158 if (show_deleted
&& err
)
1159 show_ce_entry(tag_removed
, ce
);
1160 if (show_modified
&& ce_modified(ce
, &st
, 0))
1161 show_ce_entry(tag_modified
, ce
);
1167 * Prune the index to only contain stuff starting with "prefix"
1169 static void prune_cache(const char *prefix
)
1171 int pos
= cache_name_pos(prefix
, prefix_len
);
1172 unsigned int first
, last
;
1176 memmove(active_cache
, active_cache
+ pos
,
1177 (active_nr
- pos
) * sizeof(struct cache_entry
*));
1181 while (last
> first
) {
1182 int next
= (last
+ first
) >> 1;
1183 struct cache_entry
*ce
= active_cache
[next
];
1184 if (!strncmp(ce
->name
, prefix
, prefix_len
)) {
1193 static const char *verify_pathspec(const char *prefix
)
1195 const char **p
, *n
, *prev
;
1200 for (p
= pathspec
; (n
= *p
) != NULL
; p
++) {
1202 for (i
= 0; i
< max
; i
++) {
1204 if (prev
&& prev
[i
] != c
)
1206 if (!c
|| c
== '*' || c
== '?')
1219 if (prefix_offset
> max
|| memcmp(prev
, prefix
, prefix_offset
))
1220 die("git ls-files: cannot generate relative filenames containing '..'");
1223 return max
? xmemdupz(prev
, max
) : NULL
;
1227 * Read the tree specified with --with-tree option
1228 * (typically, HEAD) into stage #1 and then
1229 * squash them down to stage #0. This is used for
1230 * --error-unmatch to list and check the path patterns
1231 * that were given from the command line. We are not
1232 * going to write this index out.
1234 void overlay_tree_on_cache(const char *tree_name
, const char *prefix
)
1237 unsigned char sha1
[20];
1239 struct cache_entry
*last_stage0
= NULL
;
1242 if (get_sha1(tree_name
, sha1
))
1243 die("tree-ish %s not found.", tree_name
);
1244 tree
= parse_tree_indirect(sha1
);
1246 die("bad tree-ish %s", tree_name
);
1248 /* Hoist the unmerged entries up to stage #3 to make room */
1249 for (i
= 0; i
< active_nr
; i
++) {
1250 struct cache_entry
*ce
= active_cache
[i
];
1253 ce
->ce_flags
|= CE_STAGEMASK
;
1257 static const char *(matchbuf
[2]);
1258 matchbuf
[0] = prefix
;
1263 if (read_tree(tree
, 1, match
))
1264 die("unable to read tree entries %s", tree_name
);
1266 for (i
= 0; i
< active_nr
; i
++) {
1267 struct cache_entry
*ce
= active_cache
[i
];
1268 switch (ce_stage(ce
)) {
1276 * If there is stage #0 entry for this, we do not
1277 * need to show it. We use CE_UPDATE bit to mark
1281 !strcmp(last_stage0
->name
, ce
->name
))
1282 ce
->ce_flags
|= CE_UPDATE
;
1287 int report_path_error(const char *ps_matched
, const char **pathspec
, int prefix_offset
)
1290 * Make sure all pathspec matched; otherwise it is an error.
1292 int num
, errors
= 0;
1293 for (num
= 0; pathspec
[num
]; num
++) {
1294 int other
, found_dup
;
1296 if (ps_matched
[num
])
1299 * The caller might have fed identical pathspec
1300 * twice. Do not barf on such a mistake.
1302 for (found_dup
= other
= 0;
1303 !found_dup
&& pathspec
[other
];
1305 if (other
== num
|| !ps_matched
[other
])
1307 if (!strcmp(pathspec
[other
], pathspec
[num
]))
1309 * Ok, we have a match already.
1316 error("pathspec '%s' did not match any file(s) known to git.",
1317 pathspec
[num
] + prefix_offset
);
1323 static const char ls_files_usage
[] =
1324 "git ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
1325 "[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
1326 "[ --exclude-per-directory=<filename> ] [--exclude-standard] "
1327 "[--full-name] [--abbrev] [--] [<file>]*";
1329 int cmd_ls_files(int argc
, const char **argv
, const char *prefix
)
1332 int exc_given
= 0, require_work_tree
= 0;
1333 struct dir_struct dir
;
1335 memset(&dir
, 0, sizeof(dir
));
1337 prefix_offset
= strlen(prefix
);
1338 git_config(git_default_config
, NULL
);
1340 for (i
= 1; i
< argc
; i
++) {
1341 const char *arg
= argv
[i
];
1343 if (!strcmp(arg
, "--")) {
1347 if (!strcmp(arg
, "-z")) {
1348 line_terminator
= 0;
1351 if (!strcmp(arg
, "-t") || !strcmp(arg
, "-v")) {
1353 tag_unmerged
= "M ";
1355 tag_modified
= "C ";
1362 if (!strcmp(arg
, "-c") || !strcmp(arg
, "--cached")) {
1366 if (!strcmp(arg
, "-d") || !strcmp(arg
, "--deleted")) {
1370 if (!strcmp(arg
, "-m") || !strcmp(arg
, "--modified")) {
1372 require_work_tree
= 1;
1375 if (!strcmp(arg
, "-o") || !strcmp(arg
, "--others")) {
1377 require_work_tree
= 1;
1380 if (!strcmp(arg
, "-i") || !strcmp(arg
, "--ignored")) {
1381 dir
.show_ignored
= 1;
1382 require_work_tree
= 1;
1385 if (!strcmp(arg
, "-s") || !strcmp(arg
, "--stage")) {
1389 if (!strcmp(arg
, "-k") || !strcmp(arg
, "--killed")) {
1391 require_work_tree
= 1;
1394 if (!strcmp(arg
, "--directory")) {
1395 dir
.show_other_directories
= 1;
1398 if (!strcmp(arg
, "--no-empty-directory")) {
1399 dir
.hide_empty_directories
= 1;
1402 if (!strcmp(arg
, "-u") || !strcmp(arg
, "--unmerged")) {
1403 /* There's no point in showing unmerged unless
1404 * you also show the stage information.
1410 if (!strcmp(arg
, "-x") && i
+1 < argc
) {
1412 add_exclude(argv
[++i
], "", 0, &dir
.exclude_list
[EXC_CMDL
]);
1415 if (!prefixcmp(arg
, "--exclude=")) {
1417 add_exclude(arg
+10, "", 0, &dir
.exclude_list
[EXC_CMDL
]);
1420 if (!strcmp(arg
, "-X") && i
+1 < argc
) {
1422 add_excludes_from_file(&dir
, argv
[++i
]);
1425 if (!prefixcmp(arg
, "--exclude-from=")) {
1427 add_excludes_from_file(&dir
, arg
+15);
1430 if (!prefixcmp(arg
, "--exclude-per-directory=")) {
1432 dir
.exclude_per_dir
= arg
+ 24;
1435 if (!strcmp(arg
, "--exclude-standard")) {
1437 setup_standard_excludes(&dir
);
1440 if (!strcmp(arg
, "--full-name")) {
1444 if (!strcmp(arg
, "--error-unmatch")) {
1448 if (!prefixcmp(arg
, "--with-tree=")) {
1449 with_tree
= arg
+ 12;
1452 if (!prefixcmp(arg
, "--abbrev=")) {
1453 abbrev
= strtoul(arg
+9, NULL
, 10);
1454 if (abbrev
&& abbrev
< MINIMUM_ABBREV
)
1455 abbrev
= MINIMUM_ABBREV
;
1456 else if (abbrev
> 40)
1460 if (!strcmp(arg
, "--abbrev")) {
1461 abbrev
= DEFAULT_ABBREV
;
1465 usage(ls_files_usage
);
1469 if (require_work_tree
&& !is_inside_work_tree())
1472 pathspec
= get_pathspec(prefix
, argv
+ i
);
1474 /* Verify that the pathspec matches the prefix */
1476 prefix
= verify_pathspec(prefix
);
1478 /* Treat unmatching pathspec elements as errors */
1479 if (pathspec
&& error_unmatch
) {
1481 for (num
= 0; pathspec
[num
]; num
++)
1483 ps_matched
= xcalloc(1, num
);
1486 if (dir
.show_ignored
&& !exc_given
) {
1487 fprintf(stderr
, "%s: --ignored needs some exclude pattern\n",
1492 /* With no flags, we default to showing the cached files */
1493 if (!(show_stage
| show_deleted
| show_others
| show_unmerged
|
1494 show_killed
| show_modified
))
1499 prune_cache(prefix
);
1502 * Basic sanity check; show-stages and show-unmerged
1503 * would not make any sense with this option.
1505 if (show_stage
|| show_unmerged
)
1506 die("ls-files --with-tree is incompatible with -s or -u");
1507 overlay_tree_on_cache(with_tree
, prefix
);
1509 show_files(&dir
, prefix
);
1513 bad
= report_path_error(ps_matched
, pathspec
, prefix_offset
);
1515 fprintf(stderr
, "Did you forget to 'git add'?\n");