added support for Added and Staged file status
[git/mingw/4msysgit/wingit-dll.git] / igit-enumfiles.c
bloba474d62044cfd2396c254428a45025b079dff766
1 // igit-enumfiles.c
2 //
4 #include <windows.h>
5 #include "igit.h"
7 #include "cache.h"
8 #include "commit.h"
9 #include "diff.h"
10 #include "diffcore.h"
11 #include "revision.h"
12 #include "cache-tree.h"
13 #include "unpack-trees.h"
14 #include "reflog-walk.h"
17 // uses the ls-files code
18 #include "builtin-ls-files.c"
21 // custom cache entry flags (just to make sure that no git functions get confused)
22 #define CE_IG_ADDED 0x2000000
23 #define CE_IG_DELETED 0x4000000
24 #define CE_IG_STAGED 0x8000000
27 struct DirStatus
29 // cached last access, to speed up searches (since we get a sorted list from git code)
30 struct DirStatus *pLastAccessedChild;
32 LPCSTR lpszName;
34 struct DirStatus *next;
35 struct DirStatus *children;
36 struct DirStatus *parent;
38 int nStatus;
39 BOOL bExplicitlyIgnored;
42 static struct DirStatus l_dirTree;
45 static BOOL l_bNoRecurse;
46 static int l_nMinStatusRelevantForDirs;
47 static BOOL l_bSkipNormalDirs;
48 static int l_nEmptyDirStatus;
49 static BOOL l_bNoRecurseDir;
51 static BOOL l_bFullPath;
52 static char l_sFullPathBuf[2048];
53 static LPSTR l_lpszFileName;
55 static BOOL l_bDirStatus;
56 static int l_nLastStatus;
57 static int l_nEnumeratedCached = 0;
59 static BOOL l_bHasHistory = FALSE;
62 static inline char GetStatusChar(int nStatus)
64 switch (nStatus)
66 case WGFS_Normal: return 'N';
67 case WGFS_Modified: return 'M';
68 case WGFS_Staged: return 'S';
69 case WGFS_Added: return 'A';
70 case WGFS_Conflicted: return 'C';
71 case WGFS_Deleted: return 'D';
73 case WGFS_Unversioned: return 'U';
74 case WGFS_Ignored: return 'I';
75 case WGFS_Unknown: return '?';
76 case WGFS_Empty: return 'E';
79 return '?';
84 static BOOL enum_ce_entry(struct cache_entry *ce, struct stat *st)
86 // is this of any use (ce->ce_flags & CE_VALID) ?
88 LPCSTR sFileName;
90 if (!l_bFullPath)
92 sFileName = ce->name + prefix_offset;
94 else
96 strcpy(l_lpszFileName, ce->name);
97 sFileName = l_sFullPathBuf;
100 const int nStage = ce_stage(ce);
102 int nStatus = WGFS_Unknown;
103 if (!st || (ce->ce_flags & CE_IG_DELETED))
104 nStatus = WGFS_Deleted;
105 else if (ce->ce_flags & CE_IG_ADDED)
106 nStatus = WGFS_Added;
107 else if (nStage)
108 nStatus = WGFS_Conflicted;
109 else if ( ce_modified(ce, st, 0) )
110 nStatus = WGFS_Modified;
111 else if (ce->ce_flags & CE_IG_STAGED)
112 nStatus = WGFS_Staged;
113 else if (!l_bHasHistory)
114 nStatus = WGFS_Added;
115 else
116 nStatus = WGFS_Normal;
117 l_nLastStatus = nStatus;
119 // output format: "F status sha1 filename"
121 fputs("F ", stdout);
122 fputc(GetStatusChar(nStatus), stdout);
123 fputc(' ', stdout);
124 fputsha1(ce->sha1, stdout);
125 fputc(' ', stdout);
126 fputs(sFileName, stdout);
127 fputc(0, stdout);
129 l_nEnumeratedCached++;
131 return FALSE;
134 // same as enum except it skips enumeration and just determines status (used for recursive folder status)
135 // returns TRUE if file was processed
136 static BOOL process_ce_entry_status(struct cache_entry *ce, struct stat *st)
138 // is this of any use (ce->ce_flags & CE_VALID) ?
140 /*if (!l_bFullPath)
142 ef.sFileName = ce->name + offset;
144 else
146 strcpy(l_lpszFileName, ce->name);
147 ef.sFileName = l_sFullPathBuf;
150 const int nStage = ce_stage(ce);
152 UINT nStatus = WGFS_Unknown;
153 if (!st || (ce->ce_flags & CE_IG_DELETED))
154 nStatus = WGFS_Deleted;
155 else if (ce->ce_flags & CE_IG_ADDED)
156 nStatus = WGFS_Added;
157 else if (nStage)
158 nStatus = WGFS_Conflicted;
159 else if ( ce_modified(ce, st, 0) )
160 nStatus = WGFS_Modified;
161 else if (ce->ce_flags & CE_IG_STAGED)
162 nStatus = WGFS_Staged;
163 else if (!l_bHasHistory)
164 nStatus = WGFS_Added;
165 else
166 nStatus = WGFS_Normal;
167 l_nLastStatus = nStatus;
169 //ef.nStage = st ? ce_stage(ce) : 0;
170 //ef.nFlags = 0;
171 //ef.sha1 = ce->sha1;
173 return TRUE;
177 static void update_dirs_unversioned(struct dir_entry *ce, int nPathNameOffset);
179 static void enum_unversioned(struct dir_entry **files, int nr, BOOL bIgnored)
181 int i;
182 for (i=0; i<nr; i++)
184 struct dir_entry *ent = files[i];
186 if (ent->name[ent->len-1] != '/' && !cache_name_is_other(ent->name, ent->len))
187 continue;
189 int len = prefix_len;
191 if (len >= ent->len)
192 die("igit status: internal error - directory entry not superset of prefix");
194 if (pathspec && !pathspec_match(pathspec, ps_matched, ent->name, len))
195 continue;
197 LPCSTR sFileName;
199 if (!l_bFullPath)
201 sFileName = ent->name + prefix_offset;
203 else
205 strcpy(l_lpszFileName, ent->name);
206 sFileName = l_sFullPathBuf;
209 if (bIgnored)
211 // because we specified collect_all_ignored this may be a directory that was ignored
212 if (ent->name[ent->len-1] != '/')
214 if (l_bDirStatus)
216 l_nLastStatus = WGFS_Ignored;
217 update_dirs_unversioned(ent, len);
220 fputs("F I 0000000000000000000000000000000000000000 ", stdout);
222 else
224 if (l_bDirStatus)
226 const int nOrgEmptyDirStatus = l_nEmptyDirStatus;
227 l_nLastStatus = l_nEmptyDirStatus = WGFS_Ignored;
228 update_dirs_unversioned(ent, len);
229 l_nEmptyDirStatus = nOrgEmptyDirStatus;
232 continue;
235 else
237 if (ent->name[ent->len-1] != '/')
239 if (l_bDirStatus)
241 l_nLastStatus = WGFS_Unversioned;
242 update_dirs_unversioned(ent, len);
245 fputs("F U 0000000000000000000000000000000000000000 ", stdout);
247 else
249 if (l_bDirStatus)
251 l_nLastStatus = l_nEmptyDirStatus;
252 update_dirs_unversioned(ent, len);
255 continue;
258 fputs(sFileName, stdout);
259 fputc(0, stdout);
264 static inline BOOL enum_dir(struct DirStatus *dir, LPCSTR lpszPathName)
266 if (dir->nStatus == WGFS_Normal && l_bSkipNormalDirs)
267 return FALSE;
269 // output format: "D status pathname"
271 fputs("D ", stdout);
272 fputc(GetStatusChar(dir->nStatus), stdout);
273 fputc(' ', stdout);
274 fputs(lpszPathName, stdout);
275 fputc(0, stdout);
277 return FALSE;
280 static BOOL enum_dirs(struct DirStatus *dir, LPSTR sPathNameBuf)
282 const int len = strlen(dir->lpszName);
283 memcpy(sPathNameBuf, dir->lpszName, len);
284 sPathNameBuf += len;
285 *sPathNameBuf = 0;
287 if ( enum_dir(dir, l_bFullPath ? l_sFullPathBuf : l_sFullPathBuf+prefix_offset) )
288 return TRUE;
290 if (!l_bNoRecurse && dir->children)
292 // recurse
294 *sPathNameBuf++ = '/';
295 *sPathNameBuf = 0;
297 dir = dir->children;
299 while (dir)
301 if ( enum_dirs(dir, sPathNameBuf) )
302 return TRUE;
304 dir = dir->next;
308 return FALSE;
312 static struct DirStatus* GetSubDir(struct DirStatus *dir, LPCSTR lpszName, int nNameLenInclTerminator)
314 // check for cached access
315 if (dir->pLastAccessedChild
316 && !strcmp(dir->pLastAccessedChild->lpszName, lpszName))
318 return dir->pLastAccessedChild;
321 // search children
322 struct DirStatus *p = dir->children;
323 struct DirStatus *last = NULL;
324 while (p)
326 if ( !strcmp(p->lpszName, lpszName) )
327 return (dir->pLastAccessedChild = p);
329 last = p;
330 p = p->next;
333 // dir not accessed before, create new entry
334 // TODO: do more efficient allocator (allocate larger pools, they can still be fire and forget and let our garbage collector clean up)
335 p = dir->pLastAccessedChild = (struct DirStatus*) malloc(sizeof(struct DirStatus) + ((nNameLenInclTerminator+3)&~3));
337 p->pLastAccessedChild = NULL;
338 p->lpszName = (char*)p + sizeof(struct DirStatus);
339 p->next = NULL;
340 p->children = NULL;
341 p->parent = dir;
342 if (l_nEmptyDirStatus != WGFS_Ignored)
344 p->bExplicitlyIgnored = dir->bExplicitlyIgnored;
345 p->nStatus = (p->bExplicitlyIgnored && l_nEmptyDirStatus < WGFS_Ignored) ? WGFS_Ignored : l_nEmptyDirStatus;
347 else
349 p->nStatus = WGFS_Ignored;
350 p->bExplicitlyIgnored = TRUE;
353 // append to list
354 if (dir->children)
355 last->next = p;
356 else
357 dir->children = p;
359 // copy string
360 memcpy((char*)p->lpszName, lpszName, nNameLenInclTerminator);
362 return p;
366 static inline BOOL IsStatusRelevantForDirs(int nStatus)
368 return nStatus >= l_nMinStatusRelevantForDirs && nStatus != WGFS_Deleted;
372 static void update_dirs_unversioned_rec(LPCSTR lpszFileName, UINT nDirLen, struct dir_entry *ce, struct DirStatus *parentDir)
374 const int nDirLen1 = nDirLen+1;
375 char s[nDirLen1];
376 memcpy(s, lpszFileName, nDirLen);
377 s[nDirLen] = 0;
379 struct DirStatus *dir = GetSubDir(parentDir, s, nDirLen1);
380 //ASSERT(dir != NULL);
382 // TODO: if 'conflicted' status is added then need to check for that as highest prio
383 if (dir->nStatus >= WGFS_Modified && l_bNoRecurse)
385 // no further processing needed
386 return;
389 // process next subdir in lpszFileName
391 lpszFileName += nDirLen1;
393 LPCSTR p = strchr(lpszFileName, '/');
394 if (!p)
396 // no more dirs in pathname (ie we are in the dir the file is located)
398 if (l_nEmptyDirStatus == WGFS_Unknown)
399 // only want dirst enumerated without recursive status
400 return;
402 const int nFileStatus = l_nLastStatus;
404 if (nFileStatus > dir->nStatus)
406 // update status on dir and all parents
409 if (nFileStatus > dir->nStatus)
410 dir->nStatus = nFileStatus;
412 while ( (dir = dir->parent) );
415 else if (lpszFileName != p) // quick check to make sure we're not left with a "/" filename
417 update_dirs_unversioned_rec(lpszFileName, (UINT)(p-lpszFileName), ce, dir);
421 static void update_dirs_unversioned(struct dir_entry *ce, int nPathNameOffset)
423 // filename relative to enumerated path
424 LPCSTR lpszFileName = ce->name + nPathNameOffset;
426 LPCSTR p = strchr(lpszFileName, '/');
427 if (p <= lpszFileName)
429 // file is not in sub-dir
431 const int nFileStatus = l_nLastStatus;
433 if (nFileStatus > l_dirTree.nStatus)
434 l_dirTree.nStatus = nFileStatus;
436 return;
439 if (!l_bNoRecurseDir)
441 update_dirs_unversioned_rec(lpszFileName, (UINT)(p-lpszFileName), ce, &l_dirTree);
446 static void update_dirs_rec(LPCSTR lpszFileName, UINT nDirLen, struct cache_entry *ce, BOOL bStatusCached, struct DirStatus *parentDir)
448 const int nDirLen1 = nDirLen+1;
449 char s[nDirLen1];
450 memcpy(s, lpszFileName, nDirLen);
451 s[nDirLen] = 0;
453 struct DirStatus *dir = GetSubDir(parentDir, s, nDirLen1);
454 //ASSERT(dir != NULL);
456 // TODO: if 'conflicted' status is added then need to check for that as highest prio
457 if (dir->nStatus >= WGFS_Modified && l_bNoRecurse)
459 // no further processing needed
460 return;
463 // process next subdir in lpszFileName
465 lpszFileName += nDirLen1;
467 LPCSTR p = strchr(lpszFileName, '/');
468 if (!p)
470 // no more dirs in pathname (ie we are in the dir the file is located)
472 if (l_nEmptyDirStatus == WGFS_Unknown)
473 // only want dirst enumerated without recursive status
474 return;
476 if (!bStatusCached)
478 // file status not determined yet, do it now
479 struct stat st;
480 int err = lstat(ce->name, &st);
481 if (!process_ce_entry_status(ce, err ? NULL : &st) || !IsStatusRelevantForDirs(l_nLastStatus))
482 return;
484 const int nFileStatus = l_nLastStatus;
486 if (nFileStatus > dir->nStatus)
488 // update status on dir and all parents
491 if (nFileStatus > dir->nStatus)
492 dir->nStatus = nFileStatus;
494 while ( (dir = dir->parent) );
497 else if (lpszFileName != p) // quick check to make sure we're not left with a "/" filename
499 update_dirs_rec(lpszFileName, (UINT)(p-lpszFileName), ce, bStatusCached, dir);
503 static void update_dirs(struct cache_entry *ce, int nPathNameOffset, BOOL bStatusCached)
505 // filename relative to enumerated path
506 LPCSTR lpszFileName = ce->name + nPathNameOffset;
508 LPCSTR p = strchr(lpszFileName, '/');
509 if (p <= lpszFileName)
511 // file is not in sub-dir
513 if (!bStatusCached)
515 // file status not determined yet, do it now
516 struct stat st;
517 int err = lstat(ce->name, &st);
518 if (!process_ce_entry_status(ce, err ? NULL : &st) || !IsStatusRelevantForDirs(l_nLastStatus))
519 return;
521 const int nFileStatus = l_nLastStatus;
523 if (nFileStatus > l_dirTree.nStatus)
524 l_dirTree.nStatus = nFileStatus;
526 return;
529 if (!l_bNoRecurseDir)
531 update_dirs_rec(lpszFileName, (UINT)(p-lpszFileName), ce, bStatusCached, &l_dirTree);
536 static inline BOOL is_subpath(const char *sPath, int nPathLen, const char *sFile)
538 return strchr(sFile + nPathLen, '/') != NULL;
541 static BOOL is_dir(const char *sProjectPath, const char *sSubPath)
543 char s[2048];
545 strcpy(s, sProjectPath);
546 // backslashify
547 LPSTR q = s;
548 while (*q)
550 if (*q == '/')
551 *q = '\\';
552 q++;
554 // make sure it ends with a slash
555 if (q[-1] != '\\')
556 *q++ = '\\';
557 strcpy(q, sSubPath);
558 // backslashify sub-path
559 while (*q)
561 if (*q == '/')
562 *q = '\\';
563 q++;
566 struct stat st;
567 int err = lstat(s, &st);
569 return (!err && S_ISDIR(st.st_mode));
572 static inline BOOL is_ce_name_eq(struct cache_entry *ce1, struct cache_entry *ce2)
574 const size_t len1 = ce1->ce_flags & CE_NAMEMASK;
575 const size_t len2 = ce2->ce_flags & CE_NAMEMASK;
577 return (len1 == len2) ? !strcmp(ce1->name, ce2->name) : FALSE;
581 struct oneway_unpack_data {
582 struct rev_info *revs;
583 char symcache[PATH_MAX];
586 // modified version of function in diff-lib.c
587 static void do_oneway_diff(struct unpack_trees_options *o, struct cache_entry *idx, struct cache_entry *tree)
589 if (!tree)
591 // file has no previous commit, newly added
592 idx->ce_flags |= CE_IG_ADDED;
594 else if (!idx)
596 // file only in previous commit, deleted
597 idx->ce_flags |= CE_IG_DELETED;
599 else if (!(idx->ce_flags & CE_INTENT_TO_ADD)
600 && hashcmp(tree->sha1, idx->sha1) && !is_null_sha1(idx->sha1))
602 // file modified and in both indices, staged
603 idx->ce_flags |= CE_IG_STAGED;
607 // function taken from diff-lib.c
608 static inline void skip_same_name(struct cache_entry *ce, struct unpack_trees_options *o)
610 int len = ce_namelen(ce);
611 const struct index_state *index = o->src_index;
613 while (o->pos < index->cache_nr) {
614 struct cache_entry *next = index->cache[o->pos];
615 if (len != ce_namelen(next))
616 break;
617 if (memcmp(ce->name, next->name, len))
618 break;
619 o->pos++;
623 // function taken from diff-lib.c
624 static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
626 struct cache_entry *idx = src[0];
627 struct cache_entry *tree = src[1];
628 struct oneway_unpack_data *cbdata = o->unpack_data;
629 struct rev_info *revs = cbdata->revs;
631 if (idx && ce_stage(idx))
632 skip_same_name(idx, o);
635 * Unpack-trees generates a DF/conflict entry if
636 * there was a directory in the index and a tree
637 * in the tree. From a diff standpoint, that's a
638 * delete of the tree and a create of the file.
640 if (tree == o->df_conflict_entry)
641 tree = NULL;
643 if (ce_path_match(idx ? idx : tree, revs->prune_data))
644 do_oneway_diff(o, idx, tree);
646 return 0;
650 * This turns all merge entries into "stage 3". That guarantees that
651 * when we read in the new tree (into "stage 1"), we won't lose sight
652 * of the fact that we had unmerged entries.
654 static void mark_merge_entries(void)
656 int i;
657 for (i = 0; i < active_nr; i++) {
658 struct cache_entry *ce = active_cache[i];
659 if (!ce_stage(ce))
660 continue;
661 ce->ce_flags |= CE_STAGEMASK;
665 static void preprocess_index(struct rev_info *revs)
667 // compare current index with index from last commit to detect staged and newly added files
670 // based on run_diff_index()
673 struct object *ent;
674 struct tree *tree;
675 const char *tree_name;
676 struct unpack_trees_options opts;
677 struct tree_desc t;
678 struct oneway_unpack_data unpack_cb;
680 mark_merge_entries();
682 ent = revs->pending.objects[0].item;
683 tree_name = revs->pending.objects[0].name;
684 tree = parse_tree_indirect(ent->sha1);
685 if (!tree)
686 // bad tree object
687 return;
689 unpack_cb.revs = revs;
690 unpack_cb.symcache[0] = '\0';
691 memset(&opts, 0, sizeof(opts));
692 opts.head_idx = 1;
693 opts.index_only = 1;
694 opts.merge = 1;
695 opts.fn = oneway_diff;
696 opts.unpack_data = &unpack_cb;
697 opts.src_index = &the_index;
698 opts.dst_index = NULL;
700 init_tree_desc(&t, tree->buffer, tree->size);
702 if ( unpack_trees(1, &t, &opts) )
703 // failed to unpack
704 return;
708 static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
710 struct object *object;
712 object = parse_object(sha1);
713 if (!object)
714 return NULL;//die("bad object %s", name);
715 object->flags |= flags;
716 return object;
719 static int add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode)
721 if (revs->no_walk && (obj->flags & UNINTERESTING))
722 return 1;//die("object ranges do not make sense when not walking revisions");
723 if (revs->reflog_info && obj->type == OBJ_COMMIT
724 && add_reflog_for_walk(revs->reflog_info, (struct commit *)obj, name))
725 return 0;
726 add_object_array_with_mode(obj, name, &revs->pending, mode);
727 return 0;
730 static int setup_revisions_lite(struct rev_info *revs, const char *def)
732 if (revs->def == NULL)
733 revs->def = def;
734 if (revs->def && !revs->pending.nr) {
735 unsigned char sha1[20];
736 struct object *object;
737 unsigned mode;
738 if (get_sha1_with_mode(revs->def, sha1, &mode))
739 return 1;//die("bad default revision '%s'", revs->def);
740 object = get_reference(revs, revs->def, sha1, 0);
741 if (!object)
742 return 2;
743 if ( add_pending_object_with_mode(revs, object, revs->def, mode) )
744 return 3;
747 /* Did the user ask for any diff output? Run the diff! */
748 if (revs->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT)
749 revs->diff = 1;
751 /* Pickaxe, diff-filter and rename following need diffs */
752 if (revs->diffopt.pickaxe ||
753 revs->diffopt.filter ||
754 DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
755 revs->diff = 1;
757 if (revs->topo_order)
758 revs->limited = 1;
760 if (revs->prune_data) {
761 diff_tree_setup_paths(revs->prune_data, &revs->pruning);
762 /* Can't prune commits with rename following: the paths change.. */
763 if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
764 revs->prune = 1;
765 if (!revs->full_diff)
766 diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
768 if (revs->combine_merges) {
769 revs->ignore_merges = 0;
770 if (revs->dense_combined_merges && !revs->diffopt.output_format)
771 revs->diffopt.output_format = DIFF_FORMAT_PATCH;
773 revs->diffopt.abbrev = revs->abbrev;
774 if (diff_setup_done(&revs->diffopt) < 0)
775 return 4;//die("diff_setup_done failed");
777 compile_grep_patterns(&revs->grep_filter);
779 /*if (revs->reverse && revs->reflog_info)
780 die("cannot combine --reverse with --walk-reflogs");
781 if (revs->rewrite_parents && revs->children.name)
782 die("cannot combine --parents and --children");*/
785 * Limitations on the graph functionality
787 /*if (revs->reverse && revs->graph)
788 die("cannot combine --reverse with --graph");
790 if (revs->reflog_info && revs->graph)
791 die("cannot combine --walk-reflogs with --graph");*/
793 return 0;
798 BOOL ig_enum_files(const char *pszProjectPath, const char *pszSubPath, const char *prefix, unsigned int nFlags)
800 // reset all local vars of builtin-ls-files.c to default
801 abbrev = 0;
802 show_deleted = 0;
803 show_cached = 0;
804 show_others = 0;
805 show_stage = 0;
806 show_unmerged = 0;
807 show_modified = 0;
808 show_killed = 0;
809 show_valid_bit = 0;
810 line_terminator = '\n';
811 prefix_len = 0;
812 prefix_offset = 0;
813 pathspec = 0;
814 error_unmatch = 0;
815 ps_matched = 0;
816 with_tree = 0;
817 tag_cached = "";
818 tag_unmerged = "";
819 tag_removed = "";
820 tag_other = "";
821 tag_killed = "";
822 tag_modified = "";
824 const BOOL bSubDir = pszSubPath && is_dir(pszProjectPath, pszSubPath);
826 LPCSTR pszSubPathSpec = pszSubPath;
827 if (bSubDir && !(nFlags & WGEFF_SingleFile))
829 int len = strlen(pszSubPath);
830 char *s = (char*)malloc(len+3);
831 strcpy(s, pszSubPath);
832 strcpy(s+len, "/*");
833 pszSubPathSpec = s;
836 int i;
837 //int exc_given = 0, require_work_tree = 0;
838 struct dir_struct _dir;
840 memset(&_dir, 0, sizeof(_dir));
842 memset(&l_dirTree, 0, sizeof(l_dirTree));
843 l_dirTree.nStatus = WGFS_Normal; // root dir is always at least WGFS_Normal even if empty
844 if (pszSubPath && !(nFlags & WGEFF_EmptyAsNormal))
845 l_dirTree.nStatus = WGFS_Empty;
847 // NOTE: to force names to be relative to project root dir (no mater what current dir is) set prefix_offset to 0
848 if (prefix)
849 prefix_offset = strlen(prefix);
850 git_config(git_default_config, NULL);
852 struct dir_struct *dir = &_dir;
854 const char *argv[2];
855 argv[0] = pszSubPathSpec;
856 argv[1] = NULL;
858 if (/*require_work_tree &&*/ !is_inside_work_tree())
859 setup_work_tree();
861 pathspec = get_pathspec(prefix, argv);
863 // Verify that the pathspec matches the prefix
864 if (pathspec)
865 prefix = verify_pathspec(prefix);
867 // Treat unmatching pathspec elements as errors
868 if (pathspec && error_unmatch)
870 int num;
871 for (num = 0; pathspec[num]; num++)
873 ps_matched = xcalloc(1, num);
876 // vars used for path recursion check
877 int pathspec_len = 0;
878 if (pathspec && *pathspec)
880 // calc length of pathspec plus 1 for a / (unless it already ends with a slash)
881 pathspec_len = strlen(*pathspec);
882 if ((*pathspec)[pathspec_len-1] == '*')
883 pathspec_len--;
884 if ((*pathspec)[pathspec_len-1] != '/')
885 pathspec_len++;
887 const char *refpath = (pathspec && *pathspec) ? *pathspec : "";
890 // configure
893 l_bNoRecurseDir = FALSE;
895 BOOL single_dir = (nFlags & WGEFF_SingleFile) && (!pszSubPath || bSubDir);
896 // adjust other flags for best performance / correct results when WGEFF_SingleFile is set
897 if (single_dir && (nFlags & WGEFF_NoRecurse))
898 l_bNoRecurseDir = TRUE;
899 if (nFlags & WGEFF_SingleFile)
901 nFlags |= WGEFF_NoRecurse;
902 if (!single_dir)
903 nFlags &= ~(WGEFF_DirStatusAll|WGEFF_DirStatusDelta);
905 if (single_dir)
907 nFlags = (nFlags & ~WGEFF_DirStatusAll) | WGEFF_DirStatusDelta;
909 if ( !(nFlags & WGEFF_EmptyAsNormal) )
910 l_dirTree.nStatus = WGFS_Empty;
913 BOOL no_recurse = nFlags & WGEFF_NoRecurse;
914 l_bNoRecurse = no_recurse;
915 l_bFullPath = nFlags & WGEFF_FullPath;
916 l_bDirStatus = nFlags & (WGEFF_DirStatusDelta|WGEFF_DirStatusAll);
918 // when all dirs should be enumerated we need IsStatusRelevantForDirs to report files of any status as relevant
919 // otherwise only above normal are considered, which is slightly more efficient
920 l_nMinStatusRelevantForDirs = (nFlags & WGEFF_DirStatusAll) ? WGFS_Empty : (WGFS_Normal+1);
922 // initial status of dirs
923 l_nEmptyDirStatus = (nFlags & WGEFF_EmptyAsNormal) ? WGFS_Normal : WGFS_Empty;
925 l_bSkipNormalDirs = ((nFlags & (WGEFF_DirStatusDelta|WGEFF_DirStatusAll)) == WGEFF_DirStatusDelta);
927 if (!(nFlags & WGEFF_SingleFile) && !l_bDirStatus)
929 // no recursive dir status requested, list all dirs as unknown
930 l_bDirStatus = TRUE;
931 l_nEmptyDirStatus = l_nMinStatusRelevantForDirs = WGFS_Unknown;
932 l_bSkipNormalDirs = FALSE;
933 l_dirTree.nStatus = WGFS_Unknown;
936 *l_sFullPathBuf = 0;
937 l_lpszFileName = NULL;
938 if (l_bFullPath)
940 strcpy(l_sFullPathBuf, pszProjectPath);
941 // slashify
942 LPSTR q = l_sFullPathBuf;
943 while (*q)
945 if (*q == '\\')
946 *q = '/';
947 q++;
949 // make sure it ends with a slash
950 if (q[-1] != '/')
952 *q++ = '/';
953 *q = 0;
955 // save pointer to where file paths, with project-relative names, can be concatenated
956 l_lpszFileName = q;
959 // shouldn't have any effect but set them to reflect what we want listed
960 show_cached = 1;
961 show_modified = 1;
962 show_deleted = 1;
963 show_unmerged = 1;
965 struct rev_info rev;
966 init_revisions(&rev, prefix);
967 rev.ignore_merges = 0;
968 rev.no_walk = 1;
969 rev.max_count = 1;
970 l_bHasHistory = !setup_revisions_lite(&rev, "HEAD");
972 read_cache();
973 if (l_bHasHistory)
974 preprocess_index(&rev);
975 if (prefix)
976 prune_cache(prefix);
978 //if (pathspec && *pathspec) OutputDebugString(*pathspec);OutputDebugString(" (1)\r\n");
979 //if (prefix) OutputDebugString(prefix);OutputDebugString(" (2)\r\n");
982 // enum files
985 for (i=0; i<active_nr; i++)
987 struct cache_entry *ce = active_cache[i];
988 struct stat st;
989 int err;
991 int dtype = ce_to_dtype(ce);
993 if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
994 continue;
995 if (ce->ce_flags & CE_UPDATE)
996 continue;
998 // skip file if not inside specified sub-path
999 // this test was originally done in enum_ce_entry but in order to avoid unecessery lstat calls it was moved
1000 if (prefix_len >= ce_namelen(ce))
1001 die("git ls-files: internal error - cache entry not superset of prefix");
1002 if (pathspec && !pathspec_match(pathspec, ps_matched, ce->name, prefix_len))
1003 continue;
1005 if (single_dir || (no_recurse && is_subpath(refpath, pathspec_len, ce->name)))
1007 if (l_bDirStatus)
1008 // this file would normally be skipped, but in order to determine correct dir status we need to process it
1009 update_dirs(ce, pathspec_len, FALSE);
1011 continue;
1014 err = lstat(ce->name, &st);
1016 if ( enum_ce_entry(ce, err ? NULL : &st) )
1017 return TRUE;
1019 // normally (always?) conflicted/unmerged files will have 3 entries in a row (one in stage 1, one in 2 and one in 3)
1020 // skip redundant entries here
1021 if ( ce_stage(ce) )
1023 int j;
1025 for (j=i+1; j<active_nr; j++)
1027 struct cache_entry *nextce = active_cache[j];
1029 if ( !is_ce_name_eq(ce, nextce) )
1030 break;
1032 i = j;
1036 if (l_bDirStatus && IsStatusRelevantForDirs(l_nLastStatus))
1037 update_dirs(ce, pathspec_len, TRUE);
1040 BOOL bIgnoreInitialized = FALSE;
1042 if (pszSubPath)
1044 // check if root (pszSubPath) dir is ignored
1046 if (!bIgnoreInitialized)
1048 setup_standard_excludes(dir);
1049 bIgnoreInitialized = TRUE;
1052 char sDir[MAX_PATH];
1053 strcpy(sDir, pszSubPath);
1054 LPSTR p = strrchr(sDir, '/');
1055 if (p) *p = 0;
1057 int dtype = DT_DIR;
1058 // check for matching ignore for each subdir level
1059 p = strchr(sDir, '/');
1060 for (;;)
1062 if (p)
1063 *p = 0;
1065 if ( excluded(dir, sDir, &dtype) )
1067 l_dirTree.nStatus = WGFS_Ignored;
1068 l_dirTree.bExplicitlyIgnored = TRUE;
1071 if (p)
1073 *p = '/';
1074 p = strchr(p+1, '/');
1075 if (!p)
1076 break;
1078 else
1080 break;
1085 // enumerate unversioned files
1086 if ( !(nFlags & WGEFF_SingleFile) )
1088 const char *path = ".", *base = "";
1089 int baselen = prefix_len;
1091 if (baselen)
1092 path = base = prefix;
1094 if (!bIgnoreInitialized)
1096 setup_standard_excludes(dir);
1097 bIgnoreInitialized = TRUE;
1099 dir->collect_ignored = 1;
1100 dir->show_ignored = 0;
1101 dir->show_other_directories = 0;
1102 dir->hide_empty_directories = 0;
1103 dir->collect_all_ignored = 1;
1104 dir->collect_directories = 1;
1105 dir->no_recurse_readdir = no_recurse ? 1 : 0;
1106 read_directory(dir, path, base, baselen, pathspec);
1108 // if root dir is ignored, then all unversioned files under it are considered ignore
1109 enum_unversioned(dir->entries, dir->nr, l_dirTree.bExplicitlyIgnored);
1110 enum_unversioned(dir->ignored, dir->ignored_nr, TRUE);
1112 else if (!single_dir && !l_nEnumeratedCached)
1114 // get status of a single unversioned file
1116 if (!bIgnoreInitialized)
1118 setup_standard_excludes(dir);
1119 bIgnoreInitialized = TRUE;
1122 LPCSTR sFileName;
1124 if (!l_bFullPath)
1126 sFileName = pszSubPath + prefix_offset;
1128 else
1130 strcpy(l_lpszFileName, pszSubPath);
1131 sFileName = l_sFullPathBuf;
1134 int dtype = DT_REG;
1135 // if root dir is ignored, then all unversioned files under it are considered ignore
1136 if (!l_dirTree.bExplicitlyIgnored && excluded(dir, pszSubPath, &dtype))
1137 fputs("F I 0000000000000000000000000000000000000000 ", stdout);
1138 else
1139 fputs("F U 0000000000000000000000000000000000000000 ", stdout);
1140 fputs(sFileName, stdout);
1141 fputc(0, stdout);
1144 if (l_bDirStatus)
1146 // enumerate dirs
1148 LPCSTR lpszRootDir="/";
1149 if (l_bFullPath)
1151 lpszRootDir = l_sFullPathBuf;
1152 if (pathspec_len)
1154 strcpy(l_lpszFileName, *pathspec);
1155 l_lpszFileName += pathspec_len;
1158 *l_lpszFileName = 0;
1159 // remove trailng slash
1160 l_lpszFileName[-1] = 0;
1162 else if (pathspec_len)
1164 lpszRootDir = ".";
1166 strcpy(l_sFullPathBuf, *pathspec);
1167 l_sFullPathBuf[pathspec_len-1] = '/';
1168 l_sFullPathBuf[pathspec_len] = 0;
1169 l_lpszFileName = l_sFullPathBuf;
1171 else
1173 lpszRootDir = ".";
1175 l_lpszFileName = l_sFullPathBuf;
1178 if (single_dir)
1180 // enumerate single dir
1181 l_bSkipNormalDirs = FALSE;
1182 enum_dir(&l_dirTree, lpszRootDir);
1184 else if (!enum_dir(&l_dirTree, lpszRootDir) && l_dirTree.children)
1186 if (l_bFullPath)
1187 // re-add trailing slash
1188 l_lpszFileName[-1] = '/';
1190 struct DirStatus *p = l_dirTree.children;
1194 if ( enum_dirs(p, l_lpszFileName) )
1195 break;
1197 while ( (p = p->next) );
1201 return TRUE;
1205 #if 0
1208 * This merges the file listing in the directory cache index
1209 * with the actual working directory list, and shows different
1210 * combinations of the two.
1212 * Copyright (C) Linus Torvalds, 2005
1214 #include "cache.h"
1215 #include "quote.h"
1216 #include "dir.h"
1217 #include "builtin.h"
1218 #include "tree.h"
1220 static int abbrev;
1221 static int show_deleted;
1222 static int show_cached;
1223 static int show_others;
1224 static int show_stage;
1225 static int show_unmerged;
1226 static int show_modified;
1227 static int show_killed;
1228 static int show_valid_bit;
1229 static int line_terminator = '\n';
1231 static int prefix_len;
1232 static int prefix_offset;
1233 static const char **pathspec;
1234 static int error_unmatch;
1235 static char *ps_matched;
1236 static const char *with_tree;
1238 static const char *tag_cached = "";
1239 static const char *tag_unmerged = "";
1240 static const char *tag_removed = "";
1241 static const char *tag_other = "";
1242 static const char *tag_killed = "";
1243 static const char *tag_modified = "";
1247 * Match a pathspec against a filename. The first "skiplen" characters
1248 * are the common prefix
1250 int pathspec_match(const char **spec, char *ps_matched,
1251 const char *filename, int skiplen)
1253 const char *m;
1255 while ((m = *spec++) != NULL) {
1256 int matchlen = strlen(m + skiplen);
1258 if (!matchlen)
1259 goto matched;
1260 if (!strncmp(m + skiplen, filename + skiplen, matchlen)) {
1261 if (m[skiplen + matchlen - 1] == '/')
1262 goto matched;
1263 switch (filename[skiplen + matchlen]) {
1264 case '/': case '\0':
1265 goto matched;
1268 if (!fnmatch(m + skiplen, filename + skiplen, 0))
1269 goto matched;
1270 if (ps_matched)
1271 ps_matched++;
1272 continue;
1273 matched:
1274 if (ps_matched)
1275 *ps_matched = 1;
1276 return 1;
1278 return 0;
1281 static void show_dir_entry(const char *tag, struct dir_entry *ent)
1283 int len = prefix_len;
1284 int offset = prefix_offset;
1286 if (len >= ent->len)
1287 die("git ls-files: internal error - directory entry not superset of prefix");
1289 if (pathspec && !pathspec_match(pathspec, ps_matched, ent->name, len))
1290 return;
1292 fputs(tag, stdout);
1293 write_name_quoted(ent->name + offset, stdout, line_terminator);
1296 static void show_other_files(struct dir_struct *dir)
1298 int i;
1300 for (i = 0; i < dir->nr; i++) {
1301 struct dir_entry *ent = dir->entries[i];
1302 if (!cache_name_is_other(ent->name, ent->len))
1303 continue;
1304 show_dir_entry(tag_other, ent);
1308 static void show_killed_files(struct dir_struct *dir)
1310 int i;
1311 for (i = 0; i < dir->nr; i++) {
1312 struct dir_entry *ent = dir->entries[i];
1313 char *cp, *sp;
1314 int pos, len, killed = 0;
1316 for (cp = ent->name; cp - ent->name < ent->len; cp = sp + 1) {
1317 sp = strchr(cp, '/');
1318 if (!sp) {
1319 /* If ent->name is prefix of an entry in the
1320 * cache, it will be killed.
1322 pos = cache_name_pos(ent->name, ent->len);
1323 if (0 <= pos)
1324 die("bug in show-killed-files");
1325 pos = -pos - 1;
1326 while (pos < active_nr &&
1327 ce_stage(active_cache[pos]))
1328 pos++; /* skip unmerged */
1329 if (active_nr <= pos)
1330 break;
1331 /* pos points at a name immediately after
1332 * ent->name in the cache. Does it expect
1333 * ent->name to be a directory?
1335 len = ce_namelen(active_cache[pos]);
1336 if ((ent->len < len) &&
1337 !strncmp(active_cache[pos]->name,
1338 ent->name, ent->len) &&
1339 active_cache[pos]->name[ent->len] == '/')
1340 killed = 1;
1341 break;
1343 if (0 <= cache_name_pos(ent->name, sp - ent->name)) {
1344 /* If any of the leading directories in
1345 * ent->name is registered in the cache,
1346 * ent->name will be killed.
1348 killed = 1;
1349 break;
1352 if (killed)
1353 show_dir_entry(tag_killed, dir->entries[i]);
1357 static void show_ce_entry(const char *tag, struct cache_entry *ce)
1359 int len = prefix_len;
1360 int offset = prefix_offset;
1362 if (len >= ce_namelen(ce))
1363 die("git ls-files: internal error - cache entry not superset of prefix");
1365 if (pathspec && !pathspec_match(pathspec, ps_matched, ce->name, len))
1366 return;
1368 if (tag && *tag && show_valid_bit &&
1369 (ce->ce_flags & CE_VALID)) {
1370 static char alttag[4];
1371 memcpy(alttag, tag, 3);
1372 if (isalpha(tag[0]))
1373 alttag[0] = tolower(tag[0]);
1374 else if (tag[0] == '?')
1375 alttag[0] = '!';
1376 else {
1377 alttag[0] = 'v';
1378 alttag[1] = tag[0];
1379 alttag[2] = ' ';
1380 alttag[3] = 0;
1382 tag = alttag;
1385 if (!show_stage) {
1386 fputs(tag, stdout);
1387 } else {
1388 printf("%s%06o %s %d\t",
1389 tag,
1390 ce->ce_mode,
1391 abbrev ? find_unique_abbrev(ce->sha1,abbrev)
1392 : sha1_to_hex(ce->sha1),
1393 ce_stage(ce));
1395 write_name_quoted(ce->name + offset, stdout, line_terminator);
1398 static void show_files(struct dir_struct *dir, const char *prefix)
1400 int i;
1402 /* For cached/deleted files we don't need to even do the readdir */
1403 if (show_others || show_killed) {
1404 const char *path = ".", *base = "";
1405 int baselen = prefix_len;
1407 if (baselen)
1408 path = base = prefix;
1409 read_directory(dir, path, base, baselen, pathspec);
1410 if (show_others)
1411 show_other_files(dir);
1412 if (show_killed)
1413 show_killed_files(dir);
1415 if (show_cached | show_stage) {
1416 for (i = 0; i < active_nr; i++) {
1417 struct cache_entry *ce = active_cache[i];
1418 int dtype = ce_to_dtype(ce);
1419 if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
1420 continue;
1421 if (show_unmerged && !ce_stage(ce))
1422 continue;
1423 if (ce->ce_flags & CE_UPDATE)
1424 continue;
1425 show_ce_entry(ce_stage(ce) ? tag_unmerged : tag_cached, ce);
1428 if (show_deleted | show_modified) {
1429 for (i = 0; i < active_nr; i++) {
1430 struct cache_entry *ce = active_cache[i];
1431 struct stat st;
1432 int err;
1433 int dtype = ce_to_dtype(ce);
1434 if (excluded(dir, ce->name, &dtype) != dir->show_ignored)
1435 continue;
1436 if (ce->ce_flags & CE_UPDATE)
1437 continue;
1438 err = lstat(ce->name, &st);
1439 if (show_deleted && err)
1440 show_ce_entry(tag_removed, ce);
1441 if (show_modified && ce_modified(ce, &st, 0))
1442 show_ce_entry(tag_modified, ce);
1448 * Prune the index to only contain stuff starting with "prefix"
1450 static void prune_cache(const char *prefix)
1452 int pos = cache_name_pos(prefix, prefix_len);
1453 unsigned int first, last;
1455 if (pos < 0)
1456 pos = -pos-1;
1457 memmove(active_cache, active_cache + pos,
1458 (active_nr - pos) * sizeof(struct cache_entry *));
1459 active_nr -= pos;
1460 first = 0;
1461 last = active_nr;
1462 while (last > first) {
1463 int next = (last + first) >> 1;
1464 struct cache_entry *ce = active_cache[next];
1465 if (!strncmp(ce->name, prefix, prefix_len)) {
1466 first = next+1;
1467 continue;
1469 last = next;
1471 active_nr = last;
1474 static const char *verify_pathspec(const char *prefix)
1476 const char **p, *n, *prev;
1477 unsigned long max;
1479 prev = NULL;
1480 max = PATH_MAX;
1481 for (p = pathspec; (n = *p) != NULL; p++) {
1482 int i, len = 0;
1483 for (i = 0; i < max; i++) {
1484 char c = n[i];
1485 if (prev && prev[i] != c)
1486 break;
1487 if (!c || c == '*' || c == '?')
1488 break;
1489 if (c == '/')
1490 len = i+1;
1492 prev = n;
1493 if (len < max) {
1494 max = len;
1495 if (!max)
1496 break;
1500 if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
1501 die("git ls-files: cannot generate relative filenames containing '..'");
1503 prefix_len = max;
1504 return max ? xmemdupz(prev, max) : NULL;
1508 * Read the tree specified with --with-tree option
1509 * (typically, HEAD) into stage #1 and then
1510 * squash them down to stage #0. This is used for
1511 * --error-unmatch to list and check the path patterns
1512 * that were given from the command line. We are not
1513 * going to write this index out.
1515 void overlay_tree_on_cache(const char *tree_name, const char *prefix)
1517 struct tree *tree;
1518 unsigned char sha1[20];
1519 const char **match;
1520 struct cache_entry *last_stage0 = NULL;
1521 int i;
1523 if (get_sha1(tree_name, sha1))
1524 die("tree-ish %s not found.", tree_name);
1525 tree = parse_tree_indirect(sha1);
1526 if (!tree)
1527 die("bad tree-ish %s", tree_name);
1529 /* Hoist the unmerged entries up to stage #3 to make room */
1530 for (i = 0; i < active_nr; i++) {
1531 struct cache_entry *ce = active_cache[i];
1532 if (!ce_stage(ce))
1533 continue;
1534 ce->ce_flags |= CE_STAGEMASK;
1537 if (prefix) {
1538 static const char *(matchbuf[2]);
1539 matchbuf[0] = prefix;
1540 matchbuf[1] = NULL;
1541 match = matchbuf;
1542 } else
1543 match = NULL;
1544 if (read_tree(tree, 1, match))
1545 die("unable to read tree entries %s", tree_name);
1547 for (i = 0; i < active_nr; i++) {
1548 struct cache_entry *ce = active_cache[i];
1549 switch (ce_stage(ce)) {
1550 case 0:
1551 last_stage0 = ce;
1552 /* fallthru */
1553 default:
1554 continue;
1555 case 1:
1557 * If there is stage #0 entry for this, we do not
1558 * need to show it. We use CE_UPDATE bit to mark
1559 * such an entry.
1561 if (last_stage0 &&
1562 !strcmp(last_stage0->name, ce->name))
1563 ce->ce_flags |= CE_UPDATE;
1568 int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset)
1571 * Make sure all pathspec matched; otherwise it is an error.
1573 int num, errors = 0;
1574 for (num = 0; pathspec[num]; num++) {
1575 int other, found_dup;
1577 if (ps_matched[num])
1578 continue;
1580 * The caller might have fed identical pathspec
1581 * twice. Do not barf on such a mistake.
1583 for (found_dup = other = 0;
1584 !found_dup && pathspec[other];
1585 other++) {
1586 if (other == num || !ps_matched[other])
1587 continue;
1588 if (!strcmp(pathspec[other], pathspec[num]))
1590 * Ok, we have a match already.
1592 found_dup = 1;
1594 if (found_dup)
1595 continue;
1597 error("pathspec '%s' did not match any file(s) known to git.",
1598 pathspec[num] + prefix_offset);
1599 errors++;
1601 return errors;
1604 static const char ls_files_usage[] =
1605 "git ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
1606 "[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
1607 "[ --exclude-per-directory=<filename> ] [--exclude-standard] "
1608 "[--full-name] [--abbrev] [--] [<file>]*";
1610 int cmd_ls_files(int argc, const char **argv, const char *prefix)
1612 int i;
1613 int exc_given = 0, require_work_tree = 0;
1614 struct dir_struct dir;
1616 memset(&dir, 0, sizeof(dir));
1617 if (prefix)
1618 prefix_offset = strlen(prefix);
1619 git_config(git_default_config, NULL);
1621 for (i = 1; i < argc; i++) {
1622 const char *arg = argv[i];
1624 if (!strcmp(arg, "--")) {
1625 i++;
1626 break;
1628 if (!strcmp(arg, "-z")) {
1629 line_terminator = 0;
1630 continue;
1632 if (!strcmp(arg, "-t") || !strcmp(arg, "-v")) {
1633 tag_cached = "H ";
1634 tag_unmerged = "M ";
1635 tag_removed = "R ";
1636 tag_modified = "C ";
1637 tag_other = "? ";
1638 tag_killed = "K ";
1639 if (arg[1] == 'v')
1640 show_valid_bit = 1;
1641 continue;
1643 if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) {
1644 show_cached = 1;
1645 continue;
1647 if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) {
1648 show_deleted = 1;
1649 continue;
1651 if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) {
1652 show_modified = 1;
1653 require_work_tree = 1;
1654 continue;
1656 if (!strcmp(arg, "-o") || !strcmp(arg, "--others")) {
1657 show_others = 1;
1658 require_work_tree = 1;
1659 continue;
1661 if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) {
1662 dir.show_ignored = 1;
1663 require_work_tree = 1;
1664 continue;
1666 if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) {
1667 show_stage = 1;
1668 continue;
1670 if (!strcmp(arg, "-k") || !strcmp(arg, "--killed")) {
1671 show_killed = 1;
1672 require_work_tree = 1;
1673 continue;
1675 if (!strcmp(arg, "--directory")) {
1676 dir.show_other_directories = 1;
1677 continue;
1679 if (!strcmp(arg, "--no-empty-directory")) {
1680 dir.hide_empty_directories = 1;
1681 continue;
1683 if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) {
1684 /* There's no point in showing unmerged unless
1685 * you also show the stage information.
1687 show_stage = 1;
1688 show_unmerged = 1;
1689 continue;
1691 if (!strcmp(arg, "-x") && i+1 < argc) {
1692 exc_given = 1;
1693 add_exclude(argv[++i], "", 0, &dir.exclude_list[EXC_CMDL]);
1694 continue;
1696 if (!prefixcmp(arg, "--exclude=")) {
1697 exc_given = 1;
1698 add_exclude(arg+10, "", 0, &dir.exclude_list[EXC_CMDL]);
1699 continue;
1701 if (!strcmp(arg, "-X") && i+1 < argc) {
1702 exc_given = 1;
1703 add_excludes_from_file(&dir, argv[++i]);
1704 continue;
1706 if (!prefixcmp(arg, "--exclude-from=")) {
1707 exc_given = 1;
1708 add_excludes_from_file(&dir, arg+15);
1709 continue;
1711 if (!prefixcmp(arg, "--exclude-per-directory=")) {
1712 exc_given = 1;
1713 dir.exclude_per_dir = arg + 24;
1714 continue;
1716 if (!strcmp(arg, "--exclude-standard")) {
1717 exc_given = 1;
1718 setup_standard_excludes(&dir);
1719 continue;
1721 if (!strcmp(arg, "--full-name")) {
1722 prefix_offset = 0;
1723 continue;
1725 if (!strcmp(arg, "--error-unmatch")) {
1726 error_unmatch = 1;
1727 continue;
1729 if (!prefixcmp(arg, "--with-tree=")) {
1730 with_tree = arg + 12;
1731 continue;
1733 if (!prefixcmp(arg, "--abbrev=")) {
1734 abbrev = strtoul(arg+9, NULL, 10);
1735 if (abbrev && abbrev < MINIMUM_ABBREV)
1736 abbrev = MINIMUM_ABBREV;
1737 else if (abbrev > 40)
1738 abbrev = 40;
1739 continue;
1741 if (!strcmp(arg, "--abbrev")) {
1742 abbrev = DEFAULT_ABBREV;
1743 continue;
1745 if (*arg == '-')
1746 usage(ls_files_usage);
1747 break;
1750 if (require_work_tree && !is_inside_work_tree())
1751 setup_work_tree();
1753 pathspec = get_pathspec(prefix, argv + i);
1755 /* Verify that the pathspec matches the prefix */
1756 if (pathspec)
1757 prefix = verify_pathspec(prefix);
1759 /* Treat unmatching pathspec elements as errors */
1760 if (pathspec && error_unmatch) {
1761 int num;
1762 for (num = 0; pathspec[num]; num++)
1764 ps_matched = xcalloc(1, num);
1767 if (dir.show_ignored && !exc_given) {
1768 fprintf(stderr, "%s: --ignored needs some exclude pattern\n",
1769 argv[0]);
1770 exit(1);
1773 /* With no flags, we default to showing the cached files */
1774 if (!(show_stage | show_deleted | show_others | show_unmerged |
1775 show_killed | show_modified))
1776 show_cached = 1;
1778 read_cache();
1779 if (prefix)
1780 prune_cache(prefix);
1781 if (with_tree) {
1783 * Basic sanity check; show-stages and show-unmerged
1784 * would not make any sense with this option.
1786 if (show_stage || show_unmerged)
1787 die("ls-files --with-tree is incompatible with -s or -u");
1788 overlay_tree_on_cache(with_tree, prefix);
1790 show_files(&dir, prefix);
1792 if (ps_matched) {
1793 int bad;
1794 bad = report_path_error(ps_matched, pathspec, prefix_offset);
1795 if (bad)
1796 fprintf(stderr, "Did you forget to 'git add'?\n");
1798 return bad ? 1 : 0;
1801 return 0;
1804 #endif