fixed issues with directory ignore detection
[git/mingw/4msysgit/wingit-dll.git] / igit-enumfiles.c
blob6cd4e7a3dbeac025fa0644526dcb6e726b40388d
1 // igit-enumfiles.c
2 //
4 #include <windows.h>
5 #include "igit.h"
8 // uses the ls-files code
9 #include "builtin-ls-files.c"
12 struct DirStatus
14 // cached last access, to speed up searches (since we get a sorted list from git code)
15 struct DirStatus *pLastAccessedChild;
17 LPCSTR lpszName;
19 struct DirStatus *next;
20 struct DirStatus *children;
21 struct DirStatus *parent;
23 int nStatus;
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)
47 switch (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';
62 return '?';
67 static BOOL enum_ce_entry(struct cache_entry *ce, struct stat *st)
69 // is this of any use (ce->ce_flags & CE_VALID) ?
71 LPCSTR sFileName;
73 if (!l_bFullPath)
75 sFileName = ce->name + prefix_offset;
77 else
79 strcpy(l_lpszFileName, ce->name);
80 sFileName = l_sFullPathBuf;
83 const int nStage = ce_stage(ce);
85 int nStatus = WGFS_Unknown;
86 if (!st)
87 nStatus = WGFS_Deleted;
88 else if (nStage)
89 nStatus = WGFS_Conflicted;
90 else if ( ce_modified(ce, st, 0) )
91 nStatus = WGFS_Modified;
92 else
93 nStatus = WGFS_Normal;
94 l_nLastStatus = nStatus;
96 // output format: "F status sha1 filename"
98 fputs("F ", stdout);
99 fputc(GetStatusChar(nStatus), stdout);
100 fputc(' ', stdout);
101 fputsha1(ce->sha1, stdout);
102 fputc(' ', stdout);
103 fputs(sFileName, stdout);
104 fputc(0, stdout);
106 l_nEnumeratedCached++;
108 return FALSE;
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) ?
117 /*if (!l_bFullPath)
119 ef.sFileName = ce->name + offset;
121 else
123 strcpy(l_lpszFileName, ce->name);
124 ef.sFileName = l_sFullPathBuf;
127 const int nStage = ce_stage(ce);
129 UINT nStatus = WGFS_Unknown;
130 if (!st)
131 nStatus = WGFS_Deleted;
132 else if (nStage)
133 nStatus = WGFS_Conflicted;
134 else if ( ce_modified(ce, st, 0) )
135 nStatus = WGFS_Modified;
136 else
137 nStatus = WGFS_Normal;
138 l_nLastStatus = nStatus;
140 //ef.nStage = st ? ce_stage(ce) : 0;
141 //ef.nFlags = 0;
142 //ef.sha1 = ce->sha1;
144 return TRUE;
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)
153 int i;
154 for (i=0; i<nr; i++)
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] == '/')
160 continue;
162 if (!cache_name_is_other(ent->name, ent->len))
163 continue;
165 int len = prefix_len;
167 if (len >= ent->len)
168 die("igit status: internal error - directory entry not superset of prefix");
170 if (pathspec && !pathspec_match(pathspec, ps_matched, ent->name, len))
171 continue;
173 LPCSTR sFileName;
175 if (!l_bFullPath)
177 sFileName = ent->name + prefix_offset;
179 else
181 strcpy(l_lpszFileName, ent->name);
182 sFileName = l_sFullPathBuf;
185 if (bIgnored)
187 // because we specified collect_all_ignored this may be a directory that was ignored
188 if ( !is_dir(".", ent->name) )
190 if (l_bDirStatus)
192 l_nLastStatus = WGFS_Ignored;
193 update_dirs_unversioned(ent, 0);
196 fputs("F I 0000000000000000000000000000000000000000 ", stdout);
198 else
200 if (l_bDirStatus)
202 const int nOrgEmptyDirStatus = l_nEmptyDirStatus;
203 l_nLastStatus = l_nEmptyDirStatus = WGFS_Ignored;
204 update_dirs_unversioned(ent, 0);
205 l_nEmptyDirStatus = nOrgEmptyDirStatus;
208 continue;
211 else
213 if (l_bDirStatus)
215 l_nLastStatus = WGFS_Unversioned;
216 update_dirs_unversioned(ent, 0);
219 fputs("F U 0000000000000000000000000000000000000000 ", stdout);
221 fputs(sFileName, stdout);
222 fputc(0, stdout);
227 static inline BOOL enum_dir(struct DirStatus *dir, LPCSTR lpszPathName)
229 if (dir->nStatus == WGFS_Normal && l_bSkipNormalDirs)
230 return FALSE;
232 // output format: "D status pathname"
234 fputs("D ", stdout);
235 fputc(GetStatusChar(dir->nStatus), stdout);
236 fputc(' ', stdout);
237 fputs(lpszPathName, stdout);
238 fputc(0, stdout);
240 return FALSE;
243 static BOOL enum_dirs(struct DirStatus *dir, LPSTR sPathNameBuf)
245 const int len = strlen(dir->lpszName);
246 memcpy(sPathNameBuf, dir->lpszName, len);
247 sPathNameBuf += len;
248 *sPathNameBuf = 0;
250 if ( enum_dir(dir, l_bFullPath ? l_sFullPathBuf : l_sFullPathBuf+prefix_offset) )
251 return TRUE;
253 if (!l_bNoRecurse && dir->children)
255 // recurse
257 *sPathNameBuf++ = '/';
258 *sPathNameBuf = 0;
260 dir = dir->children;
262 while (dir)
264 if ( enum_dirs(dir, sPathNameBuf) )
265 return TRUE;
267 dir = dir->next;
271 return FALSE;
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;
284 // search children
285 struct DirStatus *p = dir->children;
286 struct DirStatus *last = NULL;
287 while (p)
289 if ( !strcmp(p->lpszName, lpszName) )
290 return (dir->pLastAccessedChild = p);
292 last = p;
293 p = p->next;
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);
302 p->next = NULL;
303 p->children = NULL;
304 p->parent = dir;
305 if (l_nEmptyDirStatus != WGFS_Ignored)
307 p->bExplicitlyIgnored = dir->bExplicitlyIgnored;
308 p->nStatus = (p->bExplicitlyIgnored && l_nEmptyDirStatus < WGFS_Ignored) ? WGFS_Ignored : l_nEmptyDirStatus;
310 else
312 p->nStatus = WGFS_Ignored;
313 p->bExplicitlyIgnored = TRUE;
316 // append to list
317 if (dir->children)
318 last->next = p;
319 else
320 dir->children = p;
322 // copy string
323 memcpy((char*)p->lpszName, lpszName, nNameLenInclTerminator);
325 return p;
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;
338 char s[nDirLen1];
339 memcpy(s, lpszFileName, nDirLen);
340 s[nDirLen] = 0;
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
349 return;
352 // process next subdir in lpszFileName
354 lpszFileName += nDirLen1;
356 LPCSTR p = strchr(lpszFileName, '/');
357 if (!p)
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;
395 return;
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;
408 char s[nDirLen1];
409 memcpy(s, lpszFileName, nDirLen);
410 s[nDirLen] = 0;
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
419 return;
422 // process next subdir in lpszFileName
424 lpszFileName += nDirLen1;
426 LPCSTR p = strchr(lpszFileName, '/');
427 if (!p)
429 // no more dirs in pathname (ie we are in the dir the file is located)
431 if (!bStatusCached)
433 // file status not determined yet, do it now
434 struct stat st;
435 int err = lstat(ce->name, &st);
436 if (!process_ce_entry_status(ce, err ? NULL : &st) || !IsStatusRelevantForDirs(l_nLastStatus))
437 return;
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
468 if (!bStatusCached)
470 // file status not determined yet, do it now
471 struct stat st;
472 int err = lstat(ce->name, &st);
473 if (!process_ce_entry_status(ce, err ? NULL : &st) || !IsStatusRelevantForDirs(l_nLastStatus))
474 return;
476 const int nFileStatus = l_nLastStatus;
478 if (nFileStatus > l_dirTree.nStatus)
479 l_dirTree.nStatus = nFileStatus;
481 return;
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)
498 char s[2048];
500 strcpy(s, sProjectPath);
501 // backslashify
502 LPSTR q = s;
503 while (*q)
505 if (*q == '/')
506 *q = '\\';
507 q++;
509 // make sure it ends with a slash
510 if (q[-1] != '\\')
511 *q++ = '\\';
512 strcpy(q, sSubPath);
513 // backslashify sub-path
514 while (*q)
516 if (*q == '/')
517 *q = '\\';
518 q++;
521 struct stat st;
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
539 abbrev = 0;
540 show_deleted = 0;
541 show_cached = 0;
542 show_others = 0;
543 show_stage = 0;
544 show_unmerged = 0;
545 show_modified = 0;
546 show_killed = 0;
547 show_valid_bit = 0;
548 line_terminator = '\n';
549 prefix_len = 0;
550 prefix_offset = 0;
551 pathspec = 0;
552 error_unmatch = 0;
553 ps_matched = 0;
554 with_tree = 0;
555 tag_cached = "";
556 tag_unmerged = "";
557 tag_removed = "";
558 tag_other = "";
559 tag_killed = "";
560 tag_modified = "";
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);
570 strcpy(s+len, "/*");
571 pszSubPathSpec = s;
574 int i;
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
586 if (prefix)
587 prefix_offset = strlen(prefix);
588 git_config(git_default_config, NULL);
590 struct dir_struct *dir = &_dir;
592 const char *argv[2];
593 argv[0] = pszSubPathSpec;
594 argv[1] = NULL;
596 if (/*require_work_tree &&*/ !is_inside_work_tree())
597 setup_work_tree();
599 pathspec = get_pathspec(prefix, argv);
601 // Verify that the pathspec matches the prefix
602 if (pathspec)
603 prefix = verify_pathspec(prefix);
605 // Treat unmatching pathspec elements as errors
606 if (pathspec && error_unmatch)
608 int num;
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] == '*')
621 pathspec_len--;
622 if ((*pathspec)[pathspec_len-1] != '/')
623 pathspec_len++;
625 const char *refpath = (pathspec && *pathspec) ? *pathspec : "";
628 // configure
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;
640 if (!single_dir)
641 nFlags &= ~(WGEFF_DirStatusAll|WGEFF_DirStatusDelta);
643 if (single_dir)
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);
665 *l_sFullPathBuf = 0;
666 l_lpszFileName = NULL;
667 if (l_bFullPath)
669 strcpy(l_sFullPathBuf, pszProjectPath);
670 // slashify
671 LPSTR q = l_sFullPathBuf;
672 while (*q)
674 if (*q == '\\')
675 *q = '/';
676 q++;
678 // make sure it ends with a slash
679 if (q[-1] != '/')
681 *q++ = '/';
682 *q = 0;
684 // save pointer to where file paths, with project-relative names, can be concatenated
685 l_lpszFileName = q;
688 // shouldn't have any effect but set them to reflect what we want listed
689 show_cached = 1;
690 show_modified = 1;
691 show_deleted = 1;
692 show_unmerged = 1;
694 read_cache();
695 if (prefix)
696 prune_cache(prefix);
698 //if (pathspec && *pathspec) OutputDebugString(*pathspec);OutputDebugString(" (1)\r\n");
699 //if (prefix) OutputDebugString(prefix);OutputDebugString(" (2)\r\n");
702 // enum files
705 for (i=0; i<active_nr; i++)
707 struct cache_entry *ce = active_cache[i];
708 struct stat st;
709 int err;
711 int dtype = ce_to_dtype(ce);
713 if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
714 continue;
715 if (ce->ce_flags & CE_UPDATE)
716 continue;
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))
723 continue;
725 if (single_dir || (no_recurse && is_subpath(refpath, pathspec_len, ce->name)))
727 if (l_bDirStatus)
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);
731 continue;
734 err = lstat(ce->name, &st);
736 if ( enum_ce_entry(ce, err ? NULL : &st) )
737 return TRUE;
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
741 if ( ce_stage(ce) )
743 int j;
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) )
750 break;
752 i = j;
756 if (l_bDirStatus && IsStatusRelevantForDirs(l_nLastStatus))
757 update_dirs(ce, pathspec_len, TRUE);
760 BOOL bIgnoreInitialized = FALSE;
762 if (pszSubPath)
764 // check if root (pszSubPath) dir is ignored
766 if (!bIgnoreInitialized)
768 setup_standard_excludes(dir);
769 bIgnoreInitialized = TRUE;
772 char sDir[MAX_PATH];
773 strcpy(sDir, pszSubPath);
774 LPSTR p = strrchr(sDir, '/');
775 if (p) *p = 0;
777 int dtype = DT_DIR;
778 // check for matching ignore for each subdir level
779 p = strchr(sDir, '/');
780 for (;;)
782 if (p)
783 *p = 0;
785 if ( excluded(dir, sDir, &dtype) )
787 l_dirTree.nStatus = WGFS_Ignored;
788 l_dirTree.bExplicitlyIgnored = TRUE;
791 if (p)
793 *p = '/';
794 p = strchr(p+1, '/');
795 if (!p)
796 break;
798 else
800 break;
805 // enumerate unversioned files
806 if ( !(nFlags & WGEFF_SingleFile) )
808 const char *path = ".", *base = "";
809 int baselen = prefix_len;
811 if (baselen)
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;
841 LPCSTR sFileName;
843 if (!l_bFullPath)
845 sFileName = pszSubPath + prefix_offset;
847 else
849 strcpy(l_lpszFileName, pszSubPath);
850 sFileName = l_sFullPathBuf;
853 int dtype = DT_REG;
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);
857 else
858 fputs("F U 0000000000000000000000000000000000000000 ", stdout);
859 fputs(sFileName, stdout);
860 fputc(0, stdout);
863 if (l_bDirStatus)
865 // enumerate dirs
867 LPCSTR lpszRootDir="/";
868 if (l_bFullPath)
870 lpszRootDir = l_sFullPathBuf;
871 if (pathspec_len)
873 strcpy(l_lpszFileName, *pathspec);
874 l_lpszFileName += pathspec_len;
877 *l_lpszFileName = 0;
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;
890 else
892 lpszRootDir = ".";
894 l_lpszFileName = l_sFullPathBuf;
897 if (single_dir)
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)
905 if (l_bFullPath)
906 // re-add trailing slash
907 l_lpszFileName[-1] = '/';
909 struct DirStatus *p = l_dirTree.children;
913 if ( enum_dirs(p, l_lpszFileName) )
914 break;
916 while ( (p = p->next) );
920 return TRUE;
924 #if 0
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
933 #include "cache.h"
934 #include "quote.h"
935 #include "dir.h"
936 #include "builtin.h"
937 #include "tree.h"
939 static int abbrev;
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)
972 const char *m;
974 while ((m = *spec++) != NULL) {
975 int matchlen = strlen(m + skiplen);
977 if (!matchlen)
978 goto matched;
979 if (!strncmp(m + skiplen, filename + skiplen, matchlen)) {
980 if (m[skiplen + matchlen - 1] == '/')
981 goto matched;
982 switch (filename[skiplen + matchlen]) {
983 case '/': case '\0':
984 goto matched;
987 if (!fnmatch(m + skiplen, filename + skiplen, 0))
988 goto matched;
989 if (ps_matched)
990 ps_matched++;
991 continue;
992 matched:
993 if (ps_matched)
994 *ps_matched = 1;
995 return 1;
997 return 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))
1009 return;
1011 fputs(tag, stdout);
1012 write_name_quoted(ent->name + offset, stdout, line_terminator);
1015 static void show_other_files(struct dir_struct *dir)
1017 int i;
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))
1022 continue;
1023 show_dir_entry(tag_other, ent);
1027 static void show_killed_files(struct dir_struct *dir)
1029 int i;
1030 for (i = 0; i < dir->nr; i++) {
1031 struct dir_entry *ent = dir->entries[i];
1032 char *cp, *sp;
1033 int pos, len, killed = 0;
1035 for (cp = ent->name; cp - ent->name < ent->len; cp = sp + 1) {
1036 sp = strchr(cp, '/');
1037 if (!sp) {
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);
1042 if (0 <= pos)
1043 die("bug in show-killed-files");
1044 pos = -pos - 1;
1045 while (pos < active_nr &&
1046 ce_stage(active_cache[pos]))
1047 pos++; /* skip unmerged */
1048 if (active_nr <= pos)
1049 break;
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] == '/')
1059 killed = 1;
1060 break;
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.
1067 killed = 1;
1068 break;
1071 if (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))
1085 return;
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] == '?')
1094 alttag[0] = '!';
1095 else {
1096 alttag[0] = 'v';
1097 alttag[1] = tag[0];
1098 alttag[2] = ' ';
1099 alttag[3] = 0;
1101 tag = alttag;
1104 if (!show_stage) {
1105 fputs(tag, stdout);
1106 } else {
1107 printf("%s%06o %s %d\t",
1108 tag,
1109 ce->ce_mode,
1110 abbrev ? find_unique_abbrev(ce->sha1,abbrev)
1111 : sha1_to_hex(ce->sha1),
1112 ce_stage(ce));
1114 write_name_quoted(ce->name + offset, stdout, line_terminator);
1117 static void show_files(struct dir_struct *dir, const char *prefix)
1119 int i;
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;
1126 if (baselen)
1127 path = base = prefix;
1128 read_directory(dir, path, base, baselen, pathspec);
1129 if (show_others)
1130 show_other_files(dir);
1131 if (show_killed)
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)
1139 continue;
1140 if (show_unmerged && !ce_stage(ce))
1141 continue;
1142 if (ce->ce_flags & CE_UPDATE)
1143 continue;
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];
1150 struct stat st;
1151 int err;
1152 int dtype = ce_to_dtype(ce);
1153 if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
1154 continue;
1155 if (ce->ce_flags & CE_UPDATE)
1156 continue;
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;
1174 if (pos < 0)
1175 pos = -pos-1;
1176 memmove(active_cache, active_cache + pos,
1177 (active_nr - pos) * sizeof(struct cache_entry *));
1178 active_nr -= pos;
1179 first = 0;
1180 last = active_nr;
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)) {
1185 first = next+1;
1186 continue;
1188 last = next;
1190 active_nr = last;
1193 static const char *verify_pathspec(const char *prefix)
1195 const char **p, *n, *prev;
1196 unsigned long max;
1198 prev = NULL;
1199 max = PATH_MAX;
1200 for (p = pathspec; (n = *p) != NULL; p++) {
1201 int i, len = 0;
1202 for (i = 0; i < max; i++) {
1203 char c = n[i];
1204 if (prev && prev[i] != c)
1205 break;
1206 if (!c || c == '*' || c == '?')
1207 break;
1208 if (c == '/')
1209 len = i+1;
1211 prev = n;
1212 if (len < max) {
1213 max = len;
1214 if (!max)
1215 break;
1219 if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
1220 die("git ls-files: cannot generate relative filenames containing '..'");
1222 prefix_len = max;
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)
1236 struct tree *tree;
1237 unsigned char sha1[20];
1238 const char **match;
1239 struct cache_entry *last_stage0 = NULL;
1240 int i;
1242 if (get_sha1(tree_name, sha1))
1243 die("tree-ish %s not found.", tree_name);
1244 tree = parse_tree_indirect(sha1);
1245 if (!tree)
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];
1251 if (!ce_stage(ce))
1252 continue;
1253 ce->ce_flags |= CE_STAGEMASK;
1256 if (prefix) {
1257 static const char *(matchbuf[2]);
1258 matchbuf[0] = prefix;
1259 matchbuf[1] = NULL;
1260 match = matchbuf;
1261 } else
1262 match = NULL;
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)) {
1269 case 0:
1270 last_stage0 = ce;
1271 /* fallthru */
1272 default:
1273 continue;
1274 case 1:
1276 * If there is stage #0 entry for this, we do not
1277 * need to show it. We use CE_UPDATE bit to mark
1278 * such an entry.
1280 if (last_stage0 &&
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])
1297 continue;
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];
1304 other++) {
1305 if (other == num || !ps_matched[other])
1306 continue;
1307 if (!strcmp(pathspec[other], pathspec[num]))
1309 * Ok, we have a match already.
1311 found_dup = 1;
1313 if (found_dup)
1314 continue;
1316 error("pathspec '%s' did not match any file(s) known to git.",
1317 pathspec[num] + prefix_offset);
1318 errors++;
1320 return errors;
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)
1331 int i;
1332 int exc_given = 0, require_work_tree = 0;
1333 struct dir_struct dir;
1335 memset(&dir, 0, sizeof(dir));
1336 if (prefix)
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, "--")) {
1344 i++;
1345 break;
1347 if (!strcmp(arg, "-z")) {
1348 line_terminator = 0;
1349 continue;
1351 if (!strcmp(arg, "-t") || !strcmp(arg, "-v")) {
1352 tag_cached = "H ";
1353 tag_unmerged = "M ";
1354 tag_removed = "R ";
1355 tag_modified = "C ";
1356 tag_other = "? ";
1357 tag_killed = "K ";
1358 if (arg[1] == 'v')
1359 show_valid_bit = 1;
1360 continue;
1362 if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) {
1363 show_cached = 1;
1364 continue;
1366 if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) {
1367 show_deleted = 1;
1368 continue;
1370 if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) {
1371 show_modified = 1;
1372 require_work_tree = 1;
1373 continue;
1375 if (!strcmp(arg, "-o") || !strcmp(arg, "--others")) {
1376 show_others = 1;
1377 require_work_tree = 1;
1378 continue;
1380 if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) {
1381 dir.show_ignored = 1;
1382 require_work_tree = 1;
1383 continue;
1385 if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) {
1386 show_stage = 1;
1387 continue;
1389 if (!strcmp(arg, "-k") || !strcmp(arg, "--killed")) {
1390 show_killed = 1;
1391 require_work_tree = 1;
1392 continue;
1394 if (!strcmp(arg, "--directory")) {
1395 dir.show_other_directories = 1;
1396 continue;
1398 if (!strcmp(arg, "--no-empty-directory")) {
1399 dir.hide_empty_directories = 1;
1400 continue;
1402 if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) {
1403 /* There's no point in showing unmerged unless
1404 * you also show the stage information.
1406 show_stage = 1;
1407 show_unmerged = 1;
1408 continue;
1410 if (!strcmp(arg, "-x") && i+1 < argc) {
1411 exc_given = 1;
1412 add_exclude(argv[++i], "", 0, &dir.exclude_list[EXC_CMDL]);
1413 continue;
1415 if (!prefixcmp(arg, "--exclude=")) {
1416 exc_given = 1;
1417 add_exclude(arg+10, "", 0, &dir.exclude_list[EXC_CMDL]);
1418 continue;
1420 if (!strcmp(arg, "-X") && i+1 < argc) {
1421 exc_given = 1;
1422 add_excludes_from_file(&dir, argv[++i]);
1423 continue;
1425 if (!prefixcmp(arg, "--exclude-from=")) {
1426 exc_given = 1;
1427 add_excludes_from_file(&dir, arg+15);
1428 continue;
1430 if (!prefixcmp(arg, "--exclude-per-directory=")) {
1431 exc_given = 1;
1432 dir.exclude_per_dir = arg + 24;
1433 continue;
1435 if (!strcmp(arg, "--exclude-standard")) {
1436 exc_given = 1;
1437 setup_standard_excludes(&dir);
1438 continue;
1440 if (!strcmp(arg, "--full-name")) {
1441 prefix_offset = 0;
1442 continue;
1444 if (!strcmp(arg, "--error-unmatch")) {
1445 error_unmatch = 1;
1446 continue;
1448 if (!prefixcmp(arg, "--with-tree=")) {
1449 with_tree = arg + 12;
1450 continue;
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)
1457 abbrev = 40;
1458 continue;
1460 if (!strcmp(arg, "--abbrev")) {
1461 abbrev = DEFAULT_ABBREV;
1462 continue;
1464 if (*arg == '-')
1465 usage(ls_files_usage);
1466 break;
1469 if (require_work_tree && !is_inside_work_tree())
1470 setup_work_tree();
1472 pathspec = get_pathspec(prefix, argv + i);
1474 /* Verify that the pathspec matches the prefix */
1475 if (pathspec)
1476 prefix = verify_pathspec(prefix);
1478 /* Treat unmatching pathspec elements as errors */
1479 if (pathspec && error_unmatch) {
1480 int num;
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",
1488 argv[0]);
1489 exit(1);
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))
1495 show_cached = 1;
1497 read_cache();
1498 if (prefix)
1499 prune_cache(prefix);
1500 if (with_tree) {
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);
1511 if (ps_matched) {
1512 int bad;
1513 bad = report_path_error(ps_matched, pathspec, prefix_offset);
1514 if (bad)
1515 fprintf(stderr, "Did you forget to 'git add'?\n");
1517 return bad ? 1 : 0;
1520 return 0;
1523 #endif