initial commit (same functionality as wingit)
[git/mingw/4msysgit/wingit-dll.git] / igit-enumfiles.c
blob6e942d96c48fc33234458bfb1a80eded5154fc4a
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;
26 static struct DirStatus l_dirTree;
29 static BOOL l_bNoRecurse;
30 static int l_nMinStatusRelevantForDirs;
31 static BOOL l_bSkipNormalDirs;
32 static int l_nEmptyDirStatus;
33 static BOOL l_bNoRecurseDir;
35 static BOOL l_bFullPath;
36 static char l_sFullPathBuf[2048];
37 static LPSTR l_lpszFileName;
39 static BOOL l_bDirStatus;
40 static int l_nLastStatus;
43 static inline char GetStatusChar(int nStatus)
45 switch (nStatus)
47 case WGFS_Normal: return 'N';
48 case WGFS_Modified: return 'M';
49 //case WGFS_Staged: return 'S';
50 //case WGFS_Added: return 'A';
51 case WGFS_Conflicted: return 'C';
52 case WGFS_Deleted: return 'D';
54 case WGFS_Unknown: return '?';
55 case WGFS_Empty: return 'E';
56 //case WGFS_Unversioned: return 'U';
59 return '?';
64 static BOOL enum_ce_entry(struct cache_entry *ce, struct stat *st)
66 // is this of any use (ce->ce_flags & CE_VALID) ?
68 LPCSTR sFileName;
70 if (!l_bFullPath)
72 sFileName = ce->name + prefix_offset;
74 else
76 strcpy(l_lpszFileName, ce->name);
77 sFileName = l_sFullPathBuf;
80 const int nStage = ce_stage(ce);
82 int nStatus = WGFS_Unknown;
83 if (!st)
84 nStatus = WGFS_Deleted;
85 else if (nStage)
86 nStatus = WGFS_Conflicted;
87 else if ( ce_modified(ce, st, 0) )
88 nStatus = WGFS_Modified;
89 else
90 nStatus = WGFS_Normal;
91 l_nLastStatus = nStatus;
93 // output format: "F status sha1 filename"
95 fputs("F ", stdout);
96 fputc(GetStatusChar(nStatus), stdout);
97 fputc(' ', stdout);
98 fputsha1(ce->sha1, stdout);
99 fputc(' ', stdout);
100 fputs(sFileName, stdout);
101 fputc(0, stdout);
103 return FALSE;
106 // same as enum except it skips enumeration and just determines status (used for recursive folder status)
107 // returns TRUE if file was processed
108 static BOOL process_ce_entry_status(struct cache_entry *ce, struct stat *st)
110 // is this of any use (ce->ce_flags & CE_VALID) ?
112 /*if (!l_bFullPath)
114 ef.sFileName = ce->name + offset;
116 else
118 strcpy(l_lpszFileName, ce->name);
119 ef.sFileName = l_sFullPathBuf;
122 const int nStage = ce_stage(ce);
124 UINT nStatus = WGFS_Unknown;
125 if (!st)
126 nStatus = WGFS_Deleted;
127 else if (nStage)
128 nStatus = WGFS_Conflicted;
129 else if ( ce_modified(ce, st, 0) )
130 nStatus = WGFS_Modified;
131 else
132 nStatus = WGFS_Normal;
133 l_nLastStatus = nStatus;
135 //ef.nStage = st ? ce_stage(ce) : 0;
136 //ef.nFlags = 0;
137 //ef.sha1 = ce->sha1;
139 return TRUE;
143 static inline BOOL enum_dir(struct DirStatus *dir, LPCSTR lpszPathName)
145 if (dir->nStatus == WGFS_Normal && l_bSkipNormalDirs)
146 return FALSE;
148 // output format: "D status pathname"
150 fputs("D ", stdout);
151 fputc(GetStatusChar(dir->nStatus), stdout);
152 fputc(' ', stdout);
153 fputs(lpszPathName, stdout);
154 fputc(0, stdout);
156 return FALSE;
159 static BOOL enum_dirs(struct DirStatus *dir, LPSTR sPathNameBuf)
161 const int len = strlen(dir->lpszName);
162 memcpy(sPathNameBuf, dir->lpszName, len);
163 sPathNameBuf += len;
164 *sPathNameBuf = 0;
166 if ( enum_dir(dir, l_bFullPath ? l_sFullPathBuf : l_sFullPathBuf+prefix_offset) )
167 return TRUE;
169 if (!l_bNoRecurse && dir->children)
171 // recurse
173 *sPathNameBuf++ = '/';
174 *sPathNameBuf = 0;
176 dir = dir->children;
178 while (dir)
180 if ( enum_dirs(dir, sPathNameBuf) )
181 return TRUE;
183 dir = dir->next;
187 return FALSE;
191 static struct DirStatus* GetSubDir(struct DirStatus *dir, LPCSTR lpszName, int nNameLenInclTerminator)
193 // check for cached access
194 if (dir->pLastAccessedChild
195 && !strcmp(dir->pLastAccessedChild->lpszName, lpszName))
197 return dir->pLastAccessedChild;
200 // search children
201 struct DirStatus *p = dir->children;
202 struct DirStatus *last = NULL;
203 while (p)
205 if ( !strcmp(p->lpszName, lpszName) )
206 return (dir->pLastAccessedChild = p);
208 last = p;
209 p = p->next;
212 // dir not accessed before, create new entry
213 // TODO: do more efficient allocator (allocate larger pools, they can still be fire and forget and let our garbage collector clean up)
214 p = dir->pLastAccessedChild = (struct DirStatus*) malloc(sizeof(struct DirStatus) + ((nNameLenInclTerminator+3)&~3));
216 p->pLastAccessedChild = NULL;
217 p->lpszName = (char*)p + sizeof(struct DirStatus);
218 p->next = NULL;
219 p->children = NULL;
220 p->parent = dir;
221 p->nStatus = l_nEmptyDirStatus;
223 // append to list
224 if (dir->children)
225 last->next = p;
226 else
227 dir->children = p;
229 // copy string
230 memcpy((char*)p->lpszName, lpszName, nNameLenInclTerminator);
232 return p;
236 static inline BOOL IsStatusRelevantForDirs(int nStatus)
238 return nStatus >= l_nMinStatusRelevantForDirs && nStatus != WGFS_Deleted;
242 static void update_dirs_rec(LPCSTR lpszFileName, UINT nDirLen, struct cache_entry *ce, BOOL bStatusCached, struct DirStatus *parentDir)
244 const int nDirLen1 = nDirLen+1;
245 char s[nDirLen1];
246 memcpy(s, lpszFileName, nDirLen);
247 s[nDirLen] = 0;
249 struct DirStatus *dir = GetSubDir(parentDir, s, nDirLen1);
250 //ASSERT(dir != NULL);
252 // TODO: if 'conflicted' status is added then need to check for that as highest prio
253 if (dir->nStatus >= WGFS_Modified && l_bNoRecurse)
255 // no further processing needed
256 return;
259 // process next subdir in lpszFileName
261 lpszFileName += nDirLen1;
263 LPCSTR p = strchr(lpszFileName, '/');
264 if (!p)
266 // no more dirs in pathname (ie we are in the dir the file is located)
268 if (!bStatusCached)
270 // file status not determined yet, do it now
271 struct stat st;
272 int err = lstat(ce->name, &st);
273 if (!process_ce_entry_status(ce, err ? NULL : &st) || !IsStatusRelevantForDirs(l_nLastStatus))
274 return;
276 const int nFileStatus = l_nLastStatus;
278 if (nFileStatus > dir->nStatus)
280 // update status on dir and all parents
283 if (nFileStatus > dir->nStatus)
284 dir->nStatus = nFileStatus;
286 while ( (dir = dir->parent) );
289 else if (lpszFileName != p) // quick check to make sure we're not left with a "/" filename
291 update_dirs_rec(lpszFileName, (UINT)(p-lpszFileName), ce, bStatusCached, dir);
295 static void update_dirs(struct cache_entry *ce, int nPathNameOffset, BOOL bStatusCached)
297 // filename relative to enumerated path
298 LPCSTR lpszFileName = ce->name + nPathNameOffset;
300 LPCSTR p = strchr(lpszFileName, '/');
301 if (p <= lpszFileName)
303 // file is not in sub-dir
305 if (!bStatusCached)
307 // file status not determined yet, do it now
308 struct stat st;
309 int err = lstat(ce->name, &st);
310 if (!process_ce_entry_status(ce, err ? NULL : &st) || !IsStatusRelevantForDirs(l_nLastStatus))
311 return;
313 const int nFileStatus = l_nLastStatus;
315 if (nFileStatus > l_dirTree.nStatus)
316 l_dirTree.nStatus = nFileStatus;
318 return;
321 if (!l_bNoRecurseDir)
323 update_dirs_rec(lpszFileName, (UINT)(p-lpszFileName), ce, bStatusCached, &l_dirTree);
328 static inline BOOL is_subpath(const char *sPath, int nPathLen, const char *sFile)
330 return strchr(sFile + nPathLen, '/') != NULL;
333 static BOOL is_dir(const char *sProjectPath, const char *sSubPath)
335 char s[2048];
337 strcpy(s, sProjectPath);
338 // backslashify
339 LPSTR q = s;
340 while (*q)
342 if (*q == '/')
343 *q = '\\';
344 q++;
346 // make sure it ends with a slash
347 if (q[-1] != '\\')
348 *q++ = '\\';
349 strcpy(q, sSubPath);
350 // backslashify sub-path
351 while (*q)
353 if (*q == '/')
354 *q = '\\';
355 q++;
358 struct stat st;
359 int err = lstat(s, &st);
361 return (!err && S_ISDIR(st.st_mode));
364 static inline BOOL is_ce_name_eq(struct cache_entry *ce1, struct cache_entry *ce2)
366 const size_t len1 = ce1->ce_flags & CE_NAMEMASK;
367 const size_t len2 = ce2->ce_flags & CE_NAMEMASK;
369 return (len1 == len2) ? !strcmp(ce1->name, ce2->name) : FALSE;
373 BOOL ig_enum_files(const char *pszProjectPath, const char *pszSubPath, const char *prefix, unsigned int nFlags)
375 // reset all local vars of builtin-ls-files.c to default
376 abbrev = 0;
377 show_deleted = 0;
378 show_cached = 0;
379 show_others = 0;
380 show_stage = 0;
381 show_unmerged = 0;
382 show_modified = 0;
383 show_killed = 0;
384 show_valid_bit = 0;
385 line_terminator = '\n';
386 prefix_len = 0;
387 prefix_offset = 0;
388 pathspec = 0;
389 error_unmatch = 0;
390 ps_matched = 0;
391 with_tree = 0;
392 tag_cached = "";
393 tag_unmerged = "";
394 tag_removed = "";
395 tag_other = "";
396 tag_killed = "";
397 tag_modified = "";
399 int i;
400 //int exc_given = 0, require_work_tree = 0;
401 struct dir_struct _dir;
403 memset(&_dir, 0, sizeof(_dir));
405 memset(&l_dirTree, 0, sizeof(l_dirTree));
406 l_dirTree.nStatus = WGFS_Normal; // root dir is always at least WGFS_Normal even if empty
407 if (pszSubPath && !(nFlags & WGEFF_EmptyAsNormal))
408 l_dirTree.nStatus = WGFS_Empty;
410 // NOTE: to force names to be relative to project root dir (no mater what current dir is) set prefix_offset to 0
411 if (prefix)
412 prefix_offset = strlen(prefix);
413 git_config(git_default_config, NULL);
415 struct dir_struct *dir = &_dir;
417 const char *argv[2];
418 argv[0] = pszSubPath;
419 argv[1] = NULL;
421 pathspec = get_pathspec(prefix, argv);
423 // Verify that the pathspec matches the prefix
424 if (pathspec)
425 prefix = verify_pathspec(prefix);
427 // Treat unmatching pathspec elements as errors
428 if (pathspec && error_unmatch)
430 int num;
431 for (num = 0; pathspec[num]; num++)
433 ps_matched = xcalloc(1, num);
436 // vars used for path recursion check
437 int pathspec_len = 0;
438 if (pathspec && *pathspec)
440 // calc length of pathspec plus 1 for a / (unless it already ends with a slash)
441 pathspec_len = strlen(*pathspec);
442 if ((*pathspec)[pathspec_len-1] != '/' && (*pathspec)[pathspec_len-1] != '\\')
443 pathspec_len++;
445 const char *refpath = (pathspec && *pathspec) ? *pathspec : "";
448 // configure
451 l_bNoRecurseDir = FALSE;
453 BOOL single_dir = (nFlags & WGEFF_SingleFile) && (!pszSubPath || is_dir(pszProjectPath, pszSubPath));
454 // adjust other flags for best performance / correct results when WGEFF_SingleFile is set
455 if (single_dir && (nFlags & WGEFF_NoRecurse))
456 l_bNoRecurseDir = TRUE;
457 if (nFlags & WGEFF_SingleFile)
459 nFlags |= WGEFF_NoRecurse;
460 if (!single_dir)
461 nFlags &= ~(WGEFF_DirStatusAll|WGEFF_DirStatusDelta);
463 if (single_dir)
465 nFlags = (nFlags & ~WGEFF_DirStatusAll) | WGEFF_DirStatusDelta;
467 if ( !(nFlags & WGEFF_EmptyAsNormal) )
468 l_dirTree.nStatus = WGFS_Empty;
471 BOOL no_recurse = nFlags & WGEFF_NoRecurse;
472 l_bNoRecurse = no_recurse;
473 l_bFullPath = nFlags & WGEFF_FullPath;
474 l_bDirStatus = nFlags & (WGEFF_DirStatusDelta|WGEFF_DirStatusAll);
476 // when all dirs should be enumerated we need IsStatusRelevantForDirs to report files of normal status as relevant
477 // otherwise only above normal are considered, which is slightly more efficient
478 l_nMinStatusRelevantForDirs = (nFlags & WGEFF_DirStatusAll) ? WGFS_Normal : (WGFS_Normal+1);
480 // initial status of dirs
481 l_nEmptyDirStatus = (nFlags & WGEFF_EmptyAsNormal) ? WGFS_Normal : WGFS_Empty;
483 l_bSkipNormalDirs = ((nFlags & (WGEFF_DirStatusDelta|WGEFF_DirStatusAll)) == WGEFF_DirStatusDelta);
485 *l_sFullPathBuf = 0;
486 l_lpszFileName = NULL;
487 if (l_bFullPath)
489 strcpy(l_sFullPathBuf, pszProjectPath);
490 // slashify
491 LPSTR q = l_sFullPathBuf;
492 while (*q)
494 if (*q == '\\')
495 *q = '/';
496 q++;
498 // make sure it ends with a slash
499 if (q[-1] != '/')
501 *q++ = '/';
502 *q = 0;
504 // save pointer to where file paths, with project-relative names, can be concatenated
505 l_lpszFileName = q;
508 // shouldn't have any effect but set them to reflect what we want listed
509 show_cached = 1;
510 show_modified = 1;
511 show_deleted = 1;
512 show_unmerged = 1;
514 read_cache();
515 if (prefix)
516 prune_cache(prefix);
518 //if (pathspec && *pathspec) OutputDebugString(*pathspec);OutputDebugString(" (1)\r\n");
519 //if (prefix) OutputDebugString(prefix);OutputDebugString(" (2)\r\n");
522 // enum files
525 for (i=0; i<active_nr; i++)
527 struct cache_entry *ce = active_cache[i];
528 struct stat st;
529 int err;
531 int dtype = ce_to_dtype(ce);
533 if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
534 continue;
535 if (ce->ce_flags & CE_UPDATE)
536 continue;
538 // skip file if not inside specified sub-path
539 // this test was originally done in enum_ce_entry but in order to avoid unecessery lstat calls it was moved
540 if (prefix_len >= ce_namelen(ce))
541 die("git ls-files: internal error - cache entry not superset of prefix");
542 if (pathspec && !pathspec_match(pathspec, ps_matched, ce->name, prefix_len))
543 continue;
545 if (single_dir || (no_recurse && is_subpath(refpath, pathspec_len, ce->name)))
547 if (l_bDirStatus)
548 // this file would normally be skipped, but in order to determine correct dir status we need to process it
549 update_dirs(ce, pathspec_len, FALSE);
551 continue;
554 err = lstat(ce->name, &st);
556 if ( enum_ce_entry(ce, err ? NULL : &st) )
557 return TRUE;
559 // normally (always?) conflicted/unmerged files will have 3 entries in a row (one in stage 1, one in 2 and one in 3)
560 // skip redundant entries here
561 if ( ce_stage(ce) )
563 int j;
565 for (j=i+1; j<active_nr; j++)
567 struct cache_entry *nextce = active_cache[j];
569 if ( !is_ce_name_eq(ce, nextce) )
570 break;
572 i = j;
576 if (l_bDirStatus && IsStatusRelevantForDirs(l_nLastStatus))
577 update_dirs(ce, pathspec_len, TRUE);
580 if (l_bDirStatus)
582 // enumerate dirs
584 LPCSTR lpszRootDir="/";
585 if (l_bFullPath)
587 lpszRootDir = l_sFullPathBuf;
588 if (pathspec_len)
590 strcpy(l_lpszFileName, *pathspec);
591 l_lpszFileName += pathspec_len;
594 *l_lpszFileName = 0;
595 // remove trailng slash
596 l_lpszFileName[-1] = 0;
598 else if (pathspec_len)
600 lpszRootDir = *pathspec;
602 strcpy(l_sFullPathBuf, *pathspec);
603 l_sFullPathBuf[pathspec_len-1] = '/';
604 l_sFullPathBuf[pathspec_len] = 0;
605 l_lpszFileName = l_sFullPathBuf;
607 else
609 lpszRootDir = ".";
611 l_lpszFileName = l_sFullPathBuf;
614 if (single_dir)
616 // enumerate single dir
617 l_bSkipNormalDirs = FALSE;
618 enum_dir(&l_dirTree, lpszRootDir);
620 else if (!enum_dir(&l_dirTree, lpszRootDir) && l_dirTree.children)
622 if (l_bFullPath)
623 // re-add trailing slash
624 l_lpszFileName[-1] = '/';
626 struct DirStatus *p = l_dirTree.children;
630 if ( enum_dirs(p, l_lpszFileName) )
631 break;
633 while ( (p = p->next) );
637 return TRUE;
641 #if 0
644 * This merges the file listing in the directory cache index
645 * with the actual working directory list, and shows different
646 * combinations of the two.
648 * Copyright (C) Linus Torvalds, 2005
650 #include "cache.h"
651 #include "quote.h"
652 #include "dir.h"
653 #include "builtin.h"
654 #include "tree.h"
656 static int abbrev;
657 static int show_deleted;
658 static int show_cached;
659 static int show_others;
660 static int show_stage;
661 static int show_unmerged;
662 static int show_modified;
663 static int show_killed;
664 static int show_valid_bit;
665 static int line_terminator = '\n';
667 static int prefix_len;
668 static int prefix_offset;
669 static const char **pathspec;
670 static int error_unmatch;
671 static char *ps_matched;
672 static const char *with_tree;
674 static const char *tag_cached = "";
675 static const char *tag_unmerged = "";
676 static const char *tag_removed = "";
677 static const char *tag_other = "";
678 static const char *tag_killed = "";
679 static const char *tag_modified = "";
683 * Match a pathspec against a filename. The first "skiplen" characters
684 * are the common prefix
686 int pathspec_match(const char **spec, char *ps_matched,
687 const char *filename, int skiplen)
689 const char *m;
691 while ((m = *spec++) != NULL) {
692 int matchlen = strlen(m + skiplen);
694 if (!matchlen)
695 goto matched;
696 if (!strncmp(m + skiplen, filename + skiplen, matchlen)) {
697 if (m[skiplen + matchlen - 1] == '/')
698 goto matched;
699 switch (filename[skiplen + matchlen]) {
700 case '/': case '\0':
701 goto matched;
704 if (!fnmatch(m + skiplen, filename + skiplen, 0))
705 goto matched;
706 if (ps_matched)
707 ps_matched++;
708 continue;
709 matched:
710 if (ps_matched)
711 *ps_matched = 1;
712 return 1;
714 return 0;
717 static void show_dir_entry(const char *tag, struct dir_entry *ent)
719 int len = prefix_len;
720 int offset = prefix_offset;
722 if (len >= ent->len)
723 die("git ls-files: internal error - directory entry not superset of prefix");
725 if (pathspec && !pathspec_match(pathspec, ps_matched, ent->name, len))
726 return;
728 fputs(tag, stdout);
729 write_name_quoted(ent->name + offset, stdout, line_terminator);
732 static void show_other_files(struct dir_struct *dir)
734 int i;
736 for (i = 0; i < dir->nr; i++) {
737 struct dir_entry *ent = dir->entries[i];
738 if (!cache_name_is_other(ent->name, ent->len))
739 continue;
740 show_dir_entry(tag_other, ent);
744 static void show_killed_files(struct dir_struct *dir)
746 int i;
747 for (i = 0; i < dir->nr; i++) {
748 struct dir_entry *ent = dir->entries[i];
749 char *cp, *sp;
750 int pos, len, killed = 0;
752 for (cp = ent->name; cp - ent->name < ent->len; cp = sp + 1) {
753 sp = strchr(cp, '/');
754 if (!sp) {
755 /* If ent->name is prefix of an entry in the
756 * cache, it will be killed.
758 pos = cache_name_pos(ent->name, ent->len);
759 if (0 <= pos)
760 die("bug in show-killed-files");
761 pos = -pos - 1;
762 while (pos < active_nr &&
763 ce_stage(active_cache[pos]))
764 pos++; /* skip unmerged */
765 if (active_nr <= pos)
766 break;
767 /* pos points at a name immediately after
768 * ent->name in the cache. Does it expect
769 * ent->name to be a directory?
771 len = ce_namelen(active_cache[pos]);
772 if ((ent->len < len) &&
773 !strncmp(active_cache[pos]->name,
774 ent->name, ent->len) &&
775 active_cache[pos]->name[ent->len] == '/')
776 killed = 1;
777 break;
779 if (0 <= cache_name_pos(ent->name, sp - ent->name)) {
780 /* If any of the leading directories in
781 * ent->name is registered in the cache,
782 * ent->name will be killed.
784 killed = 1;
785 break;
788 if (killed)
789 show_dir_entry(tag_killed, dir->entries[i]);
793 static void show_ce_entry(const char *tag, struct cache_entry *ce)
795 int len = prefix_len;
796 int offset = prefix_offset;
798 if (len >= ce_namelen(ce))
799 die("git ls-files: internal error - cache entry not superset of prefix");
801 if (pathspec && !pathspec_match(pathspec, ps_matched, ce->name, len))
802 return;
804 if (tag && *tag && show_valid_bit &&
805 (ce->ce_flags & CE_VALID)) {
806 static char alttag[4];
807 memcpy(alttag, tag, 3);
808 if (isalpha(tag[0]))
809 alttag[0] = tolower(tag[0]);
810 else if (tag[0] == '?')
811 alttag[0] = '!';
812 else {
813 alttag[0] = 'v';
814 alttag[1] = tag[0];
815 alttag[2] = ' ';
816 alttag[3] = 0;
818 tag = alttag;
821 if (!show_stage) {
822 fputs(tag, stdout);
823 } else {
824 printf("%s%06o %s %d\t",
825 tag,
826 ce->ce_mode,
827 abbrev ? find_unique_abbrev(ce->sha1,abbrev)
828 : sha1_to_hex(ce->sha1),
829 ce_stage(ce));
831 write_name_quoted(ce->name + offset, stdout, line_terminator);
834 static void show_files(struct dir_struct *dir, const char *prefix)
836 int i;
838 /* For cached/deleted files we don't need to even do the readdir */
839 if (show_others || show_killed) {
840 const char *path = ".", *base = "";
841 int baselen = prefix_len;
843 if (baselen)
844 path = base = prefix;
845 read_directory(dir, path, base, baselen, pathspec);
846 if (show_others)
847 show_other_files(dir);
848 if (show_killed)
849 show_killed_files(dir);
851 if (show_cached | show_stage) {
852 for (i = 0; i < active_nr; i++) {
853 struct cache_entry *ce = active_cache[i];
854 int dtype = ce_to_dtype(ce);
855 if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
856 continue;
857 if (show_unmerged && !ce_stage(ce))
858 continue;
859 if (ce->ce_flags & CE_UPDATE)
860 continue;
861 show_ce_entry(ce_stage(ce) ? tag_unmerged : tag_cached, ce);
864 if (show_deleted | show_modified) {
865 for (i = 0; i < active_nr; i++) {
866 struct cache_entry *ce = active_cache[i];
867 struct stat st;
868 int err;
869 int dtype = ce_to_dtype(ce);
870 if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
871 continue;
872 if (ce->ce_flags & CE_UPDATE)
873 continue;
874 err = lstat(ce->name, &st);
875 if (show_deleted && err)
876 show_ce_entry(tag_removed, ce);
877 if (show_modified && ce_modified(ce, &st, 0))
878 show_ce_entry(tag_modified, ce);
884 * Prune the index to only contain stuff starting with "prefix"
886 static void prune_cache(const char *prefix)
888 int pos = cache_name_pos(prefix, prefix_len);
889 unsigned int first, last;
891 if (pos < 0)
892 pos = -pos-1;
893 memmove(active_cache, active_cache + pos,
894 (active_nr - pos) * sizeof(struct cache_entry *));
895 active_nr -= pos;
896 first = 0;
897 last = active_nr;
898 while (last > first) {
899 int next = (last + first) >> 1;
900 struct cache_entry *ce = active_cache[next];
901 if (!strncmp(ce->name, prefix, prefix_len)) {
902 first = next+1;
903 continue;
905 last = next;
907 active_nr = last;
910 static const char *verify_pathspec(const char *prefix)
912 const char **p, *n, *prev;
913 unsigned long max;
915 prev = NULL;
916 max = PATH_MAX;
917 for (p = pathspec; (n = *p) != NULL; p++) {
918 int i, len = 0;
919 for (i = 0; i < max; i++) {
920 char c = n[i];
921 if (prev && prev[i] != c)
922 break;
923 if (!c || c == '*' || c == '?')
924 break;
925 if (c == '/')
926 len = i+1;
928 prev = n;
929 if (len < max) {
930 max = len;
931 if (!max)
932 break;
936 if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
937 die("git ls-files: cannot generate relative filenames containing '..'");
939 prefix_len = max;
940 return max ? xmemdupz(prev, max) : NULL;
944 * Read the tree specified with --with-tree option
945 * (typically, HEAD) into stage #1 and then
946 * squash them down to stage #0. This is used for
947 * --error-unmatch to list and check the path patterns
948 * that were given from the command line. We are not
949 * going to write this index out.
951 void overlay_tree_on_cache(const char *tree_name, const char *prefix)
953 struct tree *tree;
954 unsigned char sha1[20];
955 const char **match;
956 struct cache_entry *last_stage0 = NULL;
957 int i;
959 if (get_sha1(tree_name, sha1))
960 die("tree-ish %s not found.", tree_name);
961 tree = parse_tree_indirect(sha1);
962 if (!tree)
963 die("bad tree-ish %s", tree_name);
965 /* Hoist the unmerged entries up to stage #3 to make room */
966 for (i = 0; i < active_nr; i++) {
967 struct cache_entry *ce = active_cache[i];
968 if (!ce_stage(ce))
969 continue;
970 ce->ce_flags |= CE_STAGEMASK;
973 if (prefix) {
974 static const char *(matchbuf[2]);
975 matchbuf[0] = prefix;
976 matchbuf[1] = NULL;
977 match = matchbuf;
978 } else
979 match = NULL;
980 if (read_tree(tree, 1, match))
981 die("unable to read tree entries %s", tree_name);
983 for (i = 0; i < active_nr; i++) {
984 struct cache_entry *ce = active_cache[i];
985 switch (ce_stage(ce)) {
986 case 0:
987 last_stage0 = ce;
988 /* fallthru */
989 default:
990 continue;
991 case 1:
993 * If there is stage #0 entry for this, we do not
994 * need to show it. We use CE_UPDATE bit to mark
995 * such an entry.
997 if (last_stage0 &&
998 !strcmp(last_stage0->name, ce->name))
999 ce->ce_flags |= CE_UPDATE;
1004 int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset)
1007 * Make sure all pathspec matched; otherwise it is an error.
1009 int num, errors = 0;
1010 for (num = 0; pathspec[num]; num++) {
1011 int other, found_dup;
1013 if (ps_matched[num])
1014 continue;
1016 * The caller might have fed identical pathspec
1017 * twice. Do not barf on such a mistake.
1019 for (found_dup = other = 0;
1020 !found_dup && pathspec[other];
1021 other++) {
1022 if (other == num || !ps_matched[other])
1023 continue;
1024 if (!strcmp(pathspec[other], pathspec[num]))
1026 * Ok, we have a match already.
1028 found_dup = 1;
1030 if (found_dup)
1031 continue;
1033 error("pathspec '%s' did not match any file(s) known to git.",
1034 pathspec[num] + prefix_offset);
1035 errors++;
1037 return errors;
1040 static const char ls_files_usage[] =
1041 "git ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
1042 "[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
1043 "[ --exclude-per-directory=<filename> ] [--exclude-standard] "
1044 "[--full-name] [--abbrev] [--] [<file>]*";
1046 int cmd_ls_files(int argc, const char **argv, const char *prefix)
1048 int i;
1049 int exc_given = 0, require_work_tree = 0;
1050 struct dir_struct dir;
1052 memset(&dir, 0, sizeof(dir));
1053 if (prefix)
1054 prefix_offset = strlen(prefix);
1055 git_config(git_default_config, NULL);
1057 for (i = 1; i < argc; i++) {
1058 const char *arg = argv[i];
1060 if (!strcmp(arg, "--")) {
1061 i++;
1062 break;
1064 if (!strcmp(arg, "-z")) {
1065 line_terminator = 0;
1066 continue;
1068 if (!strcmp(arg, "-t") || !strcmp(arg, "-v")) {
1069 tag_cached = "H ";
1070 tag_unmerged = "M ";
1071 tag_removed = "R ";
1072 tag_modified = "C ";
1073 tag_other = "? ";
1074 tag_killed = "K ";
1075 if (arg[1] == 'v')
1076 show_valid_bit = 1;
1077 continue;
1079 if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) {
1080 show_cached = 1;
1081 continue;
1083 if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) {
1084 show_deleted = 1;
1085 continue;
1087 if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) {
1088 show_modified = 1;
1089 require_work_tree = 1;
1090 continue;
1092 if (!strcmp(arg, "-o") || !strcmp(arg, "--others")) {
1093 show_others = 1;
1094 require_work_tree = 1;
1095 continue;
1097 if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) {
1098 dir.show_ignored = 1;
1099 require_work_tree = 1;
1100 continue;
1102 if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) {
1103 show_stage = 1;
1104 continue;
1106 if (!strcmp(arg, "-k") || !strcmp(arg, "--killed")) {
1107 show_killed = 1;
1108 require_work_tree = 1;
1109 continue;
1111 if (!strcmp(arg, "--directory")) {
1112 dir.show_other_directories = 1;
1113 continue;
1115 if (!strcmp(arg, "--no-empty-directory")) {
1116 dir.hide_empty_directories = 1;
1117 continue;
1119 if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) {
1120 /* There's no point in showing unmerged unless
1121 * you also show the stage information.
1123 show_stage = 1;
1124 show_unmerged = 1;
1125 continue;
1127 if (!strcmp(arg, "-x") && i+1 < argc) {
1128 exc_given = 1;
1129 add_exclude(argv[++i], "", 0, &dir.exclude_list[EXC_CMDL]);
1130 continue;
1132 if (!prefixcmp(arg, "--exclude=")) {
1133 exc_given = 1;
1134 add_exclude(arg+10, "", 0, &dir.exclude_list[EXC_CMDL]);
1135 continue;
1137 if (!strcmp(arg, "-X") && i+1 < argc) {
1138 exc_given = 1;
1139 add_excludes_from_file(&dir, argv[++i]);
1140 continue;
1142 if (!prefixcmp(arg, "--exclude-from=")) {
1143 exc_given = 1;
1144 add_excludes_from_file(&dir, arg+15);
1145 continue;
1147 if (!prefixcmp(arg, "--exclude-per-directory=")) {
1148 exc_given = 1;
1149 dir.exclude_per_dir = arg + 24;
1150 continue;
1152 if (!strcmp(arg, "--exclude-standard")) {
1153 exc_given = 1;
1154 setup_standard_excludes(&dir);
1155 continue;
1157 if (!strcmp(arg, "--full-name")) {
1158 prefix_offset = 0;
1159 continue;
1161 if (!strcmp(arg, "--error-unmatch")) {
1162 error_unmatch = 1;
1163 continue;
1165 if (!prefixcmp(arg, "--with-tree=")) {
1166 with_tree = arg + 12;
1167 continue;
1169 if (!prefixcmp(arg, "--abbrev=")) {
1170 abbrev = strtoul(arg+9, NULL, 10);
1171 if (abbrev && abbrev < MINIMUM_ABBREV)
1172 abbrev = MINIMUM_ABBREV;
1173 else if (abbrev > 40)
1174 abbrev = 40;
1175 continue;
1177 if (!strcmp(arg, "--abbrev")) {
1178 abbrev = DEFAULT_ABBREV;
1179 continue;
1181 if (*arg == '-')
1182 usage(ls_files_usage);
1183 break;
1186 if (require_work_tree && !is_inside_work_tree())
1187 setup_work_tree();
1189 pathspec = get_pathspec(prefix, argv + i);
1191 /* Verify that the pathspec matches the prefix */
1192 if (pathspec)
1193 prefix = verify_pathspec(prefix);
1195 /* Treat unmatching pathspec elements as errors */
1196 if (pathspec && error_unmatch) {
1197 int num;
1198 for (num = 0; pathspec[num]; num++)
1200 ps_matched = xcalloc(1, num);
1203 if (dir.show_ignored && !exc_given) {
1204 fprintf(stderr, "%s: --ignored needs some exclude pattern\n",
1205 argv[0]);
1206 exit(1);
1209 /* With no flags, we default to showing the cached files */
1210 if (!(show_stage | show_deleted | show_others | show_unmerged |
1211 show_killed | show_modified))
1212 show_cached = 1;
1214 read_cache();
1215 if (prefix)
1216 prune_cache(prefix);
1217 if (with_tree) {
1219 * Basic sanity check; show-stages and show-unmerged
1220 * would not make any sense with this option.
1222 if (show_stage || show_unmerged)
1223 die("ls-files --with-tree is incompatible with -s or -u");
1224 overlay_tree_on_cache(with_tree, prefix);
1226 show_files(&dir, prefix);
1228 if (ps_matched) {
1229 int bad;
1230 bad = report_path_error(ps_matched, pathspec, prefix_offset);
1231 if (bad)
1232 fprintf(stderr, "Did you forget to 'git add'?\n");
1234 return bad ? 1 : 0;
1237 return 0;
1240 #endif