added index caching
[git/mingw/4msysgit/wingit-dll.git] / igit-enumfiles.c
blobb8a305daf83fcb3dcdb6d2cd3d379d8ecf29e2a4
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 struct EntryRef
47 struct cache_entry *ce;
48 struct EntryRef *next;
51 static struct EntryRef *l_delQueue = NULL;
54 // enable caching of last commit's index (to reduce disk i/o caused by unpacking trees and whatnot)
55 static BOOL l_bEnableIndexCache = TRUE;
57 static BOOL l_bNoRecurse;
58 static int l_nMinStatusRelevantForDirs;
59 static BOOL l_bSkipNormalDirs;
60 static int l_nEmptyDirStatus;
61 static BOOL l_bNoRecurseDir;
63 static BOOL l_bFullPath;
64 static char l_sFullPathBuf[2048];
65 static LPSTR l_lpszFileName;
67 static BOOL l_bDirStatus;
68 static int l_nLastStatus;
69 static int l_nEnumeratedCached = 0;
71 static BOOL l_bHasHistory = FALSE;
74 static inline char GetStatusChar(int nStatus)
76 switch (nStatus)
78 case WGFS_Normal: return 'N';
79 case WGFS_Modified: return 'M';
80 case WGFS_Staged: return 'S';
81 case WGFS_Added: return 'A';
82 case WGFS_Conflicted: return 'C';
83 case WGFS_Deleted: return 'D';
85 case WGFS_Unversioned: return 'U';
86 case WGFS_Ignored: return 'I';
87 case WGFS_Unknown: return '?';
88 case WGFS_Empty: return 'E';
91 return '?';
95 static inline void queue_deleted(struct cache_entry *ce)
97 struct EntryRef *p = (struct EntryRef*) malloc( sizeof(struct EntryRef) );
99 p->ce = ce;
101 p->next = l_delQueue;
102 l_delQueue = p;
106 static BOOL enum_ce_entry(struct cache_entry *ce, struct stat *st)
108 // is this of any use (ce->ce_flags & CE_VALID) ?
110 LPCSTR sFileName;
112 if (!l_bFullPath)
114 sFileName = ce->name + prefix_offset;
116 else
118 strcpy(l_lpszFileName, ce->name);
119 sFileName = l_sFullPathBuf;
122 const int nStage = ce_stage(ce);
124 int nStatus = WGFS_Unknown;
125 if (!st || (ce->ce_flags & CE_IG_DELETED))
126 nStatus = WGFS_Deleted;
127 else if (nStage)
128 nStatus = WGFS_Conflicted;
129 else if (ce->ce_flags & CE_IG_ADDED)
130 nStatus = WGFS_Added;
131 else if ( ce_modified(ce, st, 0) )
132 nStatus = WGFS_Modified;
133 else if (ce->ce_flags & CE_IG_STAGED)
134 nStatus = WGFS_Staged;
135 else if (!l_bHasHistory)
136 nStatus = WGFS_Added;
137 else
138 nStatus = WGFS_Normal;
139 l_nLastStatus = nStatus;
141 // output format: "F status sha1 filename"
143 fputs("F ", stdout);
144 fputc(GetStatusChar(nStatus), stdout);
145 fputc(' ', stdout);
146 fputsha1(ce->sha1, stdout);
147 fputc(' ', stdout);
148 fputs(sFileName, stdout);
149 fputc(0, stdout);
151 l_nEnumeratedCached++;
153 return FALSE;
156 // same as enum except it skips enumeration and just determines status (used for recursive folder status)
157 // returns TRUE if file was processed
158 static BOOL process_ce_entry_status(struct cache_entry *ce, struct stat *st)
160 // is this of any use (ce->ce_flags & CE_VALID) ?
162 /*if (!l_bFullPath)
164 ef.sFileName = ce->name + offset;
166 else
168 strcpy(l_lpszFileName, ce->name);
169 ef.sFileName = l_sFullPathBuf;
172 const int nStage = ce_stage(ce);
174 UINT nStatus = WGFS_Unknown;
175 if (!st || (ce->ce_flags & CE_IG_DELETED))
176 nStatus = WGFS_Deleted;
177 else if (nStage)
178 nStatus = WGFS_Conflicted;
179 else if (ce->ce_flags & CE_IG_ADDED)
180 nStatus = WGFS_Added;
181 else if ( ce_modified(ce, st, 0) )
182 nStatus = WGFS_Modified;
183 else if (ce->ce_flags & CE_IG_STAGED)
184 nStatus = WGFS_Staged;
185 else if (!l_bHasHistory)
186 nStatus = WGFS_Added;
187 else
188 nStatus = WGFS_Normal;
189 l_nLastStatus = nStatus;
191 //ef.nStage = st ? ce_stage(ce) : 0;
192 //ef.nFlags = 0;
193 //ef.sha1 = ce->sha1;
195 return TRUE;
199 static void update_dirs_unversioned(struct dir_entry *ce, int nPathNameOffset);
201 static void enum_unversioned(struct dir_entry **files, int nr, BOOL bIgnored)
203 int i;
204 for (i=0; i<nr; i++)
206 struct dir_entry *ent = files[i];
208 if (ent->name[ent->len-1] != '/' && !cache_name_is_other(ent->name, ent->len))
209 continue;
211 int len = prefix_len;
213 if (len >= ent->len)
214 die("igit status: internal error - directory entry not superset of prefix");
216 if (pathspec && !match_pathspec(pathspec, ent->name, ent->len, len, ps_matched))
217 continue;
219 LPCSTR sFileName;
221 if (!l_bFullPath)
223 sFileName = ent->name + prefix_offset;
225 else
227 strcpy(l_lpszFileName, ent->name);
228 sFileName = l_sFullPathBuf;
231 if (bIgnored)
233 // because we specified collect_all_ignored this may be a directory that was ignored
234 if (ent->name[ent->len-1] != '/')
236 if (l_bDirStatus)
238 l_nLastStatus = WGFS_Ignored;
239 update_dirs_unversioned(ent, len);
242 fputs("F I 0000000000000000000000000000000000000000 ", stdout);
244 else
246 if (l_bDirStatus)
248 const int nOrgEmptyDirStatus = l_nEmptyDirStatus;
249 l_nLastStatus = l_nEmptyDirStatus = WGFS_Ignored;
250 update_dirs_unversioned(ent, len);
251 l_nEmptyDirStatus = nOrgEmptyDirStatus;
254 continue;
257 else
259 if (ent->name[ent->len-1] != '/')
261 if (l_bDirStatus)
263 l_nLastStatus = WGFS_Unversioned;
264 update_dirs_unversioned(ent, len);
267 fputs("F U 0000000000000000000000000000000000000000 ", stdout);
269 else
271 if (l_bDirStatus)
273 l_nLastStatus = l_nEmptyDirStatus;
274 update_dirs_unversioned(ent, len);
277 continue;
280 fputs(sFileName, stdout);
281 fputc(0, stdout);
286 static inline BOOL enum_dir(struct DirStatus *dir, LPCSTR lpszPathName)
288 if (dir->nStatus == WGFS_Normal && l_bSkipNormalDirs)
289 return FALSE;
291 // output format: "D status pathname"
293 fputs("D ", stdout);
294 fputc(GetStatusChar(dir->nStatus), stdout);
295 fputc(' ', stdout);
296 fputs(lpszPathName, stdout);
297 fputc(0, stdout);
299 return FALSE;
302 static BOOL enum_dirs(struct DirStatus *dir, LPSTR sPathNameBuf)
304 const int len = strlen(dir->lpszName);
305 memcpy(sPathNameBuf, dir->lpszName, len);
306 sPathNameBuf += len;
307 *sPathNameBuf = 0;
309 if ( enum_dir(dir, l_bFullPath ? l_sFullPathBuf : l_sFullPathBuf+prefix_offset) )
310 return TRUE;
312 if (!l_bNoRecurse && dir->children)
314 // recurse
316 *sPathNameBuf++ = '/';
317 *sPathNameBuf = 0;
319 dir = dir->children;
321 while (dir)
323 if ( enum_dirs(dir, sPathNameBuf) )
324 return TRUE;
326 dir = dir->next;
330 return FALSE;
334 static struct DirStatus* GetSubDir(struct DirStatus *dir, LPCSTR lpszName, int nNameLenInclTerminator)
336 // check for cached access
337 if (dir->pLastAccessedChild
338 && !strcmp(dir->pLastAccessedChild->lpszName, lpszName))
340 return dir->pLastAccessedChild;
343 // search children
344 struct DirStatus *p = dir->children;
345 struct DirStatus *last = NULL;
346 while (p)
348 if ( !strcmp(p->lpszName, lpszName) )
349 return (dir->pLastAccessedChild = p);
351 last = p;
352 p = p->next;
355 // dir not accessed before, create new entry
356 // TODO: do more efficient allocator (allocate larger pools, they can still be fire and forget and let our garbage collector clean up)
357 p = dir->pLastAccessedChild = (struct DirStatus*) malloc(sizeof(struct DirStatus) + ((nNameLenInclTerminator+3)&~3));
359 p->pLastAccessedChild = NULL;
360 p->lpszName = (char*)p + sizeof(struct DirStatus);
361 p->next = NULL;
362 p->children = NULL;
363 p->parent = dir;
364 if (l_nEmptyDirStatus != WGFS_Ignored)
366 p->bExplicitlyIgnored = dir->bExplicitlyIgnored;
367 p->nStatus = (p->bExplicitlyIgnored && l_nEmptyDirStatus < WGFS_Ignored) ? WGFS_Ignored : l_nEmptyDirStatus;
369 else
371 p->nStatus = WGFS_Ignored;
372 p->bExplicitlyIgnored = TRUE;
375 // append to list
376 if (dir->children)
377 last->next = p;
378 else
379 dir->children = p;
381 // copy string
382 memcpy((char*)p->lpszName, lpszName, nNameLenInclTerminator);
384 return p;
388 static inline BOOL IsStatusRelevantForDirs(int nStatus)
390 return nStatus >= l_nMinStatusRelevantForDirs && nStatus != WGFS_Deleted;
394 static void update_dirs_unversioned_rec(LPCSTR lpszFileName, UINT nDirLen, struct dir_entry *ce, struct DirStatus *parentDir)
396 const int nDirLen1 = nDirLen+1;
397 char s[nDirLen1];
398 memcpy(s, lpszFileName, nDirLen);
399 s[nDirLen] = 0;
401 struct DirStatus *dir = GetSubDir(parentDir, s, nDirLen1);
402 //ASSERT(dir != NULL);
404 if (dir->nStatus >= WGFS_Conflicted && l_bNoRecurse)
406 // no further processing needed
407 return;
410 // process next subdir in lpszFileName
412 lpszFileName += nDirLen1;
414 LPCSTR p = strchr(lpszFileName, '/');
415 if (!p)
417 // no more dirs in pathname (ie we are in the dir the file is located)
419 if (l_nEmptyDirStatus == WGFS_Unknown)
420 // only want dirs enumerated without recursive status
421 return;
423 const int nFileStatus = l_nLastStatus;
425 if (nFileStatus > dir->nStatus)
427 // update status on dir and all parents
430 if (nFileStatus > dir->nStatus)
431 dir->nStatus = nFileStatus;
433 while ( (dir = dir->parent) );
436 else if (lpszFileName != p) // quick check to make sure we're not left with a "/" filename
438 update_dirs_unversioned_rec(lpszFileName, (UINT)(p-lpszFileName), ce, dir);
442 static void update_dirs_unversioned(struct dir_entry *ce, int nPathNameOffset)
444 // filename relative to enumerated path
445 LPCSTR lpszFileName = ce->name + nPathNameOffset;
447 LPCSTR p = strchr(lpszFileName, '/');
448 if (p <= lpszFileName)
450 // file is not in sub-dir
452 if (l_nEmptyDirStatus == WGFS_Unknown)
453 // only want dirst enumerated without recursive status
454 return;
456 const int nFileStatus = l_nLastStatus;
458 if (nFileStatus > l_dirTree.nStatus)
459 l_dirTree.nStatus = nFileStatus;
461 return;
464 if (!l_bNoRecurseDir)
466 update_dirs_unversioned_rec(lpszFileName, (UINT)(p-lpszFileName), ce, &l_dirTree);
471 static void update_dirs_rec(LPCSTR lpszFileName, UINT nDirLen, struct cache_entry *ce, BOOL bStatusCached, struct DirStatus *parentDir)
473 const int nDirLen1 = nDirLen+1;
474 char s[nDirLen1];
475 memcpy(s, lpszFileName, nDirLen);
476 s[nDirLen] = 0;
478 struct DirStatus *dir = GetSubDir(parentDir, s, nDirLen1);
479 //ASSERT(dir != NULL);
481 if (dir->nStatus >= WGFS_Conflicted && l_bNoRecurse)
483 // no further processing needed
484 return;
487 // process next subdir in lpszFileName
489 lpszFileName += nDirLen1;
491 LPCSTR p = strchr(lpszFileName, '/');
492 if (!p)
494 // no more dirs in pathname (ie we are in the dir the file is located)
496 if (l_nEmptyDirStatus == WGFS_Unknown)
497 // only want dirst enumerated without recursive status
498 return;
500 if (!bStatusCached)
502 // file status not determined yet, do it now
503 struct stat st;
504 int err = lstat(ce->name, &st);
505 if (!process_ce_entry_status(ce, err ? NULL : &st) || !IsStatusRelevantForDirs(l_nLastStatus))
506 return;
508 const int nFileStatus = l_nLastStatus;
510 if (nFileStatus > dir->nStatus)
512 // update status on dir and all parents
515 if (nFileStatus > dir->nStatus)
516 dir->nStatus = nFileStatus;
518 while ( (dir = dir->parent) );
521 else if (lpszFileName != p) // quick check to make sure we're not left with a "/" filename
523 update_dirs_rec(lpszFileName, (UINT)(p-lpszFileName), ce, bStatusCached, dir);
527 static void update_dirs(struct cache_entry *ce, int nPathNameOffset, BOOL bStatusCached)
529 // filename relative to enumerated path
530 LPCSTR lpszFileName = ce->name + nPathNameOffset;
532 LPCSTR p = strchr(lpszFileName, '/');
533 if (p <= lpszFileName)
535 // file is not in sub-dir
537 if (l_nEmptyDirStatus == WGFS_Unknown)
538 // only want dirs enumerated without recursive status
539 return;
541 if (!bStatusCached)
543 // file status not determined yet, do it now
544 struct stat st;
545 int err = lstat(ce->name, &st);
546 if (!process_ce_entry_status(ce, err ? NULL : &st) || !IsStatusRelevantForDirs(l_nLastStatus))
547 return;
549 const int nFileStatus = l_nLastStatus;
551 if (nFileStatus > l_dirTree.nStatus)
552 l_dirTree.nStatus = nFileStatus;
554 return;
557 if (!l_bNoRecurseDir)
559 update_dirs_rec(lpszFileName, (UINT)(p-lpszFileName), ce, bStatusCached, &l_dirTree);
564 static inline BOOL is_subpath(const char *sPath, int nPathLen, const char *sFile)
566 return strchr(sFile + nPathLen, '/') != NULL;
569 static BOOL is_dir(const char *sProjectPath, const char *sSubPath)
571 char s[2048];
573 strcpy(s, sProjectPath);
574 // backslashify
575 LPSTR q = s;
576 while (*q)
578 if (*q == '/')
579 *q = '\\';
580 q++;
582 // make sure it ends with a slash
583 if (q[-1] != '\\')
584 *q++ = '\\';
585 strcpy(q, sSubPath);
586 // backslashify sub-path
587 while (*q)
589 if (*q == '/')
590 *q = '\\';
591 q++;
594 struct stat st;
595 int err = lstat(s, &st);
597 return (!err && S_ISDIR(st.st_mode));
600 static inline BOOL is_ce_name_eq(struct cache_entry *ce1, struct cache_entry *ce2)
602 const size_t len1 = ce1->ce_flags & CE_NAMEMASK;
603 const size_t len2 = ce2->ce_flags & CE_NAMEMASK;
605 return (len1 == len2) ? !strcmp(ce1->name, ce2->name) : FALSE;
609 struct oneway_unpack_data {
610 struct rev_info *revs;
611 char symcache[PATH_MAX];
614 // modified version of function in diff-lib.c
615 static void do_oneway_diff(struct unpack_trees_options *o, struct cache_entry *idx, struct cache_entry *tree)
617 if (!tree)
619 if (idx)
621 // file has no previous commit, newly added
622 idx->ce_flags |= CE_IG_ADDED;
625 else if (!idx)
627 // file only in previous commit, deleted
628 tree->ce_flags |= CE_IG_DELETED;
629 queue_deleted(tree);
631 else if (!(idx->ce_flags & CE_INTENT_TO_ADD)
632 && hashcmp(tree->sha1, idx->sha1) && !is_null_sha1(idx->sha1))
634 // file modified and in both indices, staged
635 idx->ce_flags |= CE_IG_STAGED;
639 // function taken from diff-lib.c
640 static inline void skip_same_name(struct cache_entry *ce, struct unpack_trees_options *o)
642 int len = ce_namelen(ce);
643 const struct index_state *index = o->src_index;
645 while (o->pos < index->cache_nr) {
646 struct cache_entry *next = index->cache[o->pos];
647 if (len != ce_namelen(next))
648 break;
649 if (memcmp(ce->name, next->name, len))
650 break;
651 o->pos++;
655 // function taken from diff-lib.c
656 static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
658 struct cache_entry *idx = src[0];
659 struct cache_entry *tree = src[1];
660 struct oneway_unpack_data *cbdata = o->unpack_data;
661 struct rev_info *revs = cbdata->revs;
663 if (idx && ce_stage(idx))
664 skip_same_name(idx, o);
667 * Unpack-trees generates a DF/conflict entry if
668 * there was a directory in the index and a tree
669 * in the tree. From a diff standpoint, that's a
670 * delete of the tree and a create of the file.
672 if (tree == o->df_conflict_entry)
673 tree = NULL;
675 if (ce_path_match(idx ? idx : tree, revs->prune_data))
676 do_oneway_diff(o, idx, tree);
678 return 0;
681 static int dummy_diff(struct cache_entry **src, struct unpack_trees_options *o)
683 return 0;
687 * This turns all merge entries into "stage 3". That guarantees that
688 * when we read in the new tree (into "stage 1"), we won't lose sight
689 * of the fact that we had unmerged entries.
691 static void mark_merge_entries(void)
693 int i;
694 for (i = 0; i < active_nr; i++) {
695 struct cache_entry *ce = active_cache[i];
696 if (!ce_stage(ce))
697 continue;
698 ce->ce_flags |= CE_STAGEMASK;
702 static void preprocess_index(struct rev_info *revs)
704 // compare current index with index from last commit to detect staged and newly added files
706 struct unpack_trees_options opts;
707 struct index_state old_index = { 0 };
708 int fd;
709 char filename[MAX_PATH];
710 unsigned char cursha1[20+12];
712 if (l_bEnableIndexCache)
715 // load cached index if valid
718 if ( get_sha1("HEAD", cursha1) )
719 die("Could not resolve SHA1 of HEAD");
721 sprintf(filename, "%s_cached.igit", get_index_file());
723 fd = open(filename, O_RDONLY);
725 if (fd >= 0)
727 // check if cached file contains desired revision
729 // NOTE: the cache file header is 32 bytes, where the first 20 bytes is the SHA1 of the cached revision
730 // the remaining 12 bytes are for future use
732 unsigned char sha1[20+12];
733 BOOL res = (read(fd, sha1, 32) == 32);
734 close(fd);
736 if (res && !hashcmp(cursha1, sha1))
738 // cached index is valid, read it
739 read_index_from_ex(&old_index, filename, 32);
740 goto merge_index;
746 // based on run_diff_index()
749 // build old_index (and save to cached file)
751 struct object *ent;
752 struct tree *tree;
753 const char *tree_name;
754 struct tree_desc t;
755 struct oneway_unpack_data unpack_cb;
757 if (!l_bEnableIndexCache)
758 mark_merge_entries();
760 ent = revs->pending.objects[0].item;
761 tree_name = revs->pending.objects[0].name;
762 tree = parse_tree_indirect(ent->sha1);
763 if (!tree)
764 // bad tree object
765 return;
767 unpack_cb.revs = revs;
768 unpack_cb.symcache[0] = '\0';
769 memset(&opts, 0, sizeof(opts));
770 opts.head_idx = 1;
771 opts.index_only = 1;
772 if (!l_bEnableIndexCache)
774 opts.merge = 1;
775 opts.fn = oneway_diff;
776 opts.src_index = &the_index;
777 opts.dst_index = NULL;
779 else
781 opts.merge = 0;
782 opts.fn = dummy_diff;
783 opts.src_index = NULL;
784 opts.dst_index = &old_index;
786 opts.unpack_data = &unpack_cb;
788 init_tree_desc(&t, tree->buffer, tree->size);
790 if ( unpack_trees(1, &t, &opts) )
791 // failed to unpack
792 return;
794 if (l_bEnableIndexCache)
796 fd = open(filename, O_WRONLY | O_CREAT, 0666);
798 memset(cursha1+20, 0, 12);
799 write(fd, cursha1, 32);
801 if (fd < 0 || write_index(&old_index, fd) || close(fd))
802 die("Could not write cached index to %s", filename);
805 // merge/diff old index with current index
808 merge_index:
810 mark_merge_entries();
812 memset(&opts, 0, sizeof(opts));
813 opts.head_idx = 1;
814 opts.index_only = 1;
815 opts.merge = 1;
816 opts.fn = dummy_diff;
817 opts.unpack_data = &unpack_cb;
818 opts.src_index = NULL;
819 opts.dst_index = &old_index;
821 int i = 0;
822 int j = 0;
823 for (; i<active_nr && j<old_index.cache_nr;)
825 struct cache_entry *ce = active_cache[i];
826 struct cache_entry *oldce = old_index.cache[j];
828 int len = ce_namelen(ce);
829 int oldlen = ce_namelen(oldce);
831 int cmp = df_name_compare(
832 ce->name, len, ce->ce_mode,
833 oldce->name, oldlen, oldce->ce_mode);
835 if (cmp > 0)
837 // new entry is bigger than the old one (old file has been deleted)
838 do_oneway_diff(&opts, NULL, oldce);
840 j++;
842 else if (cmp < 0)
844 // new entry is smaller than the old one (newly added file)
845 do_oneway_diff(&opts, ce, NULL);
847 i++;
849 else
851 do_oneway_diff(&opts, ce, oldce);
853 // skip consecutive unmerged entries
854 if ( ce_stage(ce) )
856 int k;
857 for (k=i+1; k<active_nr; k++)
859 if ( !is_ce_name_eq(ce, active_cache[k]) )
860 break;
861 i = k;
865 i++;
866 j++;
870 // process remaining entries (should there be any)
871 for (; i<active_nr; i++)
873 struct cache_entry *ce = active_cache[i];
875 // newly added file
876 do_oneway_diff(&opts, ce, NULL);
878 // skip consecutive unmerged entries
879 if ( ce_stage(ce) )
881 int k;
882 for (k=i+1; k<active_nr; k++)
884 if ( !is_ce_name_eq(ce, active_cache[k]) )
885 break;
886 i = k;
890 for (; j<old_index.cache_nr; j++)
892 // old file has been deleted
893 do_oneway_diff(&opts, NULL, old_index.cache[j]);
897 // add deleted files to index (easier for enumeration functions to process)
898 if (l_delQueue)
900 struct EntryRef *p = l_delQueue;
902 while (p)
904 // only add file for enumeration if they still exist
905 struct stat st;
906 if ( lstat(p->ce->name, &st) )
908 struct cache_entry *ce = make_cache_entry(p->ce->ce_mode, null_sha1, p->ce->name, 0, 0);
910 add_index_entry(&the_index, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK|ADD_CACHE_NEW_ONLY);
911 ce->ce_flags &= ~CE_ADDED;
912 ce->ce_flags |= CE_IG_DELETED;
915 struct EntryRef *q = p;
916 p = p->next;
918 free(q);
921 l_delQueue = NULL;
924 if (l_bEnableIndexCache)
925 discard_index(&old_index);
929 static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
931 struct object *object;
933 object = parse_object(sha1);
934 if (!object)
935 return NULL;//die("bad object %s", name);
936 object->flags |= flags;
937 return object;
940 static int add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode)
942 if (revs->no_walk && (obj->flags & UNINTERESTING))
943 return 1;//die("object ranges do not make sense when not walking revisions");
944 if (revs->reflog_info && obj->type == OBJ_COMMIT
945 && add_reflog_for_walk(revs->reflog_info, (struct commit *)obj, name))
946 return 0;
947 add_object_array_with_mode(obj, name, &revs->pending, mode);
948 return 0;
951 static int setup_revisions_lite(struct rev_info *revs, const char *def)
953 if (revs->def == NULL)
954 revs->def = def;
955 if (revs->def && !revs->pending.nr) {
956 unsigned char sha1[20];
957 struct object *object;
958 unsigned mode;
959 if (get_sha1_with_mode(revs->def, sha1, &mode))
960 return 1;//die("bad default revision '%s'", revs->def);
961 object = get_reference(revs, revs->def, sha1, 0);
962 if (!object)
963 return 2;
964 if ( add_pending_object_with_mode(revs, object, revs->def, mode) )
965 return 3;
968 /* Did the user ask for any diff output? Run the diff! */
969 if (revs->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT)
970 revs->diff = 1;
972 /* Pickaxe, diff-filter and rename following need diffs */
973 if (revs->diffopt.pickaxe ||
974 revs->diffopt.filter ||
975 DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
976 revs->diff = 1;
978 if (revs->topo_order)
979 revs->limited = 1;
981 if (revs->prune_data) {
982 diff_tree_setup_paths(revs->prune_data, &revs->pruning);
983 /* Can't prune commits with rename following: the paths change.. */
984 if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
985 revs->prune = 1;
986 if (!revs->full_diff)
987 diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
989 if (revs->combine_merges) {
990 revs->ignore_merges = 0;
991 if (revs->dense_combined_merges && !revs->diffopt.output_format)
992 revs->diffopt.output_format = DIFF_FORMAT_PATCH;
994 revs->diffopt.abbrev = revs->abbrev;
995 if (diff_setup_done(&revs->diffopt) < 0)
996 return 4;//die("diff_setup_done failed");
998 compile_grep_patterns(&revs->grep_filter);
1000 /*if (revs->reverse && revs->reflog_info)
1001 die("cannot combine --reverse with --walk-reflogs");
1002 if (revs->rewrite_parents && revs->children.name)
1003 die("cannot combine --parents and --children");*/
1006 * Limitations on the graph functionality
1008 /*if (revs->reverse && revs->graph)
1009 die("cannot combine --reverse with --graph");
1011 if (revs->reflog_info && revs->graph)
1012 die("cannot combine --walk-reflogs with --graph");*/
1014 return 0;
1019 BOOL ig_enum_files(const char *pszProjectPath, const char *pszSubPath, const char *prefix, unsigned int nFlags)
1021 // reset all local vars of builtin-ls-files.c to default
1022 abbrev = 0;
1023 show_deleted = 0;
1024 show_cached = 0;
1025 show_others = 0;
1026 show_stage = 0;
1027 show_unmerged = 0;
1028 show_modified = 0;
1029 show_killed = 0;
1030 show_valid_bit = 0;
1031 line_terminator = '\n';
1032 prefix_len = 0;
1033 prefix_offset = 0;
1034 pathspec = 0;
1035 error_unmatch = 0;
1036 ps_matched = 0;
1037 with_tree = 0;
1038 tag_cached = "";
1039 tag_unmerged = "";
1040 tag_removed = "";
1041 tag_other = "";
1042 tag_killed = "";
1043 tag_modified = "";
1045 if (nFlags & WGEFF_NoCacheIndex)
1046 l_bEnableIndexCache = FALSE;
1048 const BOOL bSubDir = pszSubPath && is_dir(pszProjectPath, pszSubPath);
1050 LPCSTR pszSubPathSpec = pszSubPath;
1051 if (bSubDir && !(nFlags & WGEFF_SingleFile))
1053 int len = strlen(pszSubPath);
1054 char *s = (char*)malloc(len+3);
1055 strcpy(s, pszSubPath);
1056 strcpy(s+len, "/*");
1057 pszSubPathSpec = s;
1060 int i;
1061 //int exc_given = 0, require_work_tree = 0;
1062 struct dir_struct _dir;
1064 memset(&_dir, 0, sizeof(_dir));
1066 memset(&l_dirTree, 0, sizeof(l_dirTree));
1067 l_dirTree.nStatus = WGFS_Normal; // root dir is always at least WGFS_Normal even if empty
1068 if (pszSubPath && !(nFlags & WGEFF_EmptyAsNormal))
1069 l_dirTree.nStatus = WGFS_Empty;
1071 // NOTE: to force names to be relative to project root dir (no mater what current dir is) set prefix_offset to 0
1072 if (prefix)
1073 prefix_offset = strlen(prefix);
1074 git_config(git_default_config, NULL);
1076 struct dir_struct *dir = &_dir;
1078 const char *argv[2];
1079 argv[0] = pszSubPathSpec;
1080 argv[1] = NULL;
1082 if (/*require_work_tree &&*/ !is_inside_work_tree())
1083 setup_work_tree();
1085 pathspec = get_pathspec(prefix, argv);
1087 // TODO: in ls-files from v1.6.2 read_cache call was moved up here, not sure if that is needed or works correctly here
1088 //read_cache();
1090 // Verify that the pathspec matches the prefix
1091 if (pathspec)
1092 prefix = verify_pathspec(prefix);
1094 // Treat unmatching pathspec elements as errors
1095 if (pathspec && error_unmatch)
1097 int num;
1098 for (num = 0; pathspec[num]; num++)
1100 ps_matched = xcalloc(1, num);
1103 // vars used for path recursion check
1104 int pathspec_len = 0;
1105 if (pathspec && *pathspec)
1107 // calc length of pathspec plus 1 for a / (unless it already ends with a slash)
1108 pathspec_len = strlen(*pathspec);
1109 if ((*pathspec)[pathspec_len-1] == '*')
1110 pathspec_len--;
1111 if ((*pathspec)[pathspec_len-1] != '/')
1112 pathspec_len++;
1114 const char *refpath = (pathspec && *pathspec) ? *pathspec : "";
1117 // configure
1120 l_bNoRecurseDir = FALSE;
1122 BOOL single_dir = (nFlags & WGEFF_SingleFile) && (!pszSubPath || bSubDir);
1123 // adjust other flags for best performance / correct results when WGEFF_SingleFile is set
1124 if (single_dir && (nFlags & WGEFF_NoRecurse))
1125 l_bNoRecurseDir = TRUE;
1126 if (nFlags & WGEFF_SingleFile)
1128 nFlags |= WGEFF_NoRecurse;
1129 if (!single_dir)
1130 nFlags &= ~(WGEFF_DirStatusAll|WGEFF_DirStatusDelta);
1132 if (single_dir)
1134 nFlags = (nFlags & ~WGEFF_DirStatusAll) | WGEFF_DirStatusDelta;
1136 if ( !(nFlags & WGEFF_EmptyAsNormal) )
1137 l_dirTree.nStatus = WGFS_Empty;
1140 BOOL no_recurse = nFlags & WGEFF_NoRecurse;
1141 l_bNoRecurse = no_recurse;
1142 l_bFullPath = nFlags & WGEFF_FullPath;
1143 l_bDirStatus = nFlags & (WGEFF_DirStatusDelta|WGEFF_DirStatusAll);
1145 // when all dirs should be enumerated we need IsStatusRelevantForDirs to report files of any status as relevant
1146 // otherwise only above normal are considered, which is slightly more efficient
1147 l_nMinStatusRelevantForDirs = (nFlags & WGEFF_DirStatusAll) ? WGFS_Empty : (WGFS_Normal+1);
1149 // initial status of dirs
1150 l_nEmptyDirStatus = (nFlags & WGEFF_EmptyAsNormal) ? WGFS_Normal : WGFS_Empty;
1152 l_bSkipNormalDirs = ((nFlags & (WGEFF_DirStatusDelta|WGEFF_DirStatusAll)) == WGEFF_DirStatusDelta);
1154 if (!(nFlags & WGEFF_SingleFile) && !l_bDirStatus)
1156 // no recursive dir status requested, list all dirs as unknown
1157 l_bDirStatus = TRUE;
1158 l_nEmptyDirStatus = l_nMinStatusRelevantForDirs = WGFS_Unknown;
1159 l_bSkipNormalDirs = FALSE;
1160 l_dirTree.nStatus = WGFS_Unknown;
1163 *l_sFullPathBuf = 0;
1164 l_lpszFileName = NULL;
1165 if (l_bFullPath)
1167 strcpy(l_sFullPathBuf, pszProjectPath);
1168 // slashify
1169 LPSTR q = l_sFullPathBuf;
1170 while (*q)
1172 if (*q == '\\')
1173 *q = '/';
1174 q++;
1176 // make sure it ends with a slash
1177 if (q[-1] != '/')
1179 *q++ = '/';
1180 *q = 0;
1182 // save pointer to where file paths, with project-relative names, can be concatenated
1183 l_lpszFileName = q;
1186 // shouldn't have any effect but set them to reflect what we want listed
1187 show_cached = 1;
1188 show_modified = 1;
1189 show_deleted = 1;
1190 show_unmerged = 1;
1192 struct rev_info rev;
1193 init_revisions(&rev, prefix);
1194 rev.ignore_merges = 0;
1195 rev.no_walk = 1;
1196 rev.max_count = 1;
1197 l_bHasHistory = !setup_revisions_lite(&rev, "HEAD");
1199 read_cache();
1200 if (l_bHasHistory)
1201 preprocess_index(&rev);
1202 if (prefix)
1203 prune_cache(prefix);
1205 //if (pathspec && *pathspec) OutputDebugString(*pathspec);OutputDebugString(" (1)\r\n");
1206 //if (prefix) OutputDebugString(prefix);OutputDebugString(" (2)\r\n");
1209 // enum files
1212 for (i=0; i<active_nr; i++)
1214 struct cache_entry *ce = active_cache[i];
1215 struct stat st;
1216 int err;
1218 int dtype = ce_to_dtype(ce);
1220 if (excluded(dir, ce->name, &dtype) != !!(dir->flags & DIR_SHOW_IGNORED))
1221 continue;
1222 if (ce->ce_flags & CE_UPDATE)
1223 continue;
1225 // skip file if not inside specified sub-path
1226 // this test was originally done in enum_ce_entry but in order to avoid unecessery lstat calls it was moved
1227 if (prefix_len >= ce_namelen(ce))
1228 die("git ls-files: internal error - cache entry not superset of prefix");
1229 if (pathspec && !match_pathspec(pathspec, ce->name, ce_namelen(ce), prefix_len, ps_matched))
1230 continue;
1232 if (single_dir || (no_recurse && is_subpath(refpath, pathspec_len, ce->name)))
1234 if (l_bDirStatus)
1235 // this file would normally be skipped, but in order to determine correct dir status we need to process it
1236 update_dirs(ce, pathspec_len, FALSE);
1238 continue;
1241 err = (ce->ce_flags & CE_IG_DELETED) ? 1 : lstat(ce->name, &st);
1243 if ( enum_ce_entry(ce, err ? NULL : &st) )
1244 return TRUE;
1246 // normally (always?) conflicted/unmerged files will have 3 entries in a row (one in stage 1, one in 2 and one in 3)
1247 // skip redundant entries here
1248 if ( ce_stage(ce) )
1250 int j;
1252 for (j=i+1; j<active_nr; j++)
1254 struct cache_entry *nextce = active_cache[j];
1256 if ( !is_ce_name_eq(ce, nextce) )
1257 break;
1259 i = j;
1263 if (l_bDirStatus && IsStatusRelevantForDirs(l_nLastStatus))
1264 update_dirs(ce, pathspec_len, TRUE);
1267 BOOL bIgnoreInitialized = FALSE;
1269 if (pszSubPath)
1271 // check if root (pszSubPath) dir is ignored
1273 if (!bIgnoreInitialized)
1275 exc_given = 1;
1276 setup_standard_excludes(dir);
1277 bIgnoreInitialized = TRUE;
1280 char sDir[MAX_PATH];
1281 strcpy(sDir, pszSubPath);
1282 LPSTR p = strrchr(sDir, '/');
1283 if (p) *p = 0;
1285 int dtype = DT_DIR;
1286 // check for matching ignore for each subdir level
1287 p = strchr(sDir, '/');
1288 for (;;)
1290 if (p)
1291 *p = 0;
1293 if ( excluded(dir, sDir, &dtype) )
1295 l_dirTree.nStatus = WGFS_Ignored;
1296 l_dirTree.bExplicitlyIgnored = TRUE;
1299 if (p)
1301 *p = '/';
1302 p = strchr(p+1, '/');
1303 if (!p)
1304 break;
1306 else
1308 break;
1313 // enumerate unversioned files
1314 if ( !(nFlags & WGEFF_SingleFile) )
1316 const char *path = ".", *base = "";
1317 int baselen = prefix_len;
1319 if (baselen)
1320 path = base = prefix;
1322 if (!bIgnoreInitialized)
1324 exc_given = 1;
1325 setup_standard_excludes(dir);
1326 bIgnoreInitialized = TRUE;
1328 dir->flags |= DIR_COLLECT_IGNORED;
1329 dir->flags &= ~DIR_SHOW_IGNORED;
1330 dir->flags &= ~DIR_SHOW_OTHER_DIRECTORIES;
1331 dir->flags &= ~DIR_HIDE_EMPTY_DIRECTORIES;
1332 dir->flags |= DIR_COLLECT_ALL_IGNORED;
1333 dir->flags |= DIR_COLLECT_DIRECTORIES;
1334 if (no_recurse)
1335 dir->flags |= DIR_NO_RECURSE_READDIR;
1336 else
1337 dir->flags &= ~DIR_NO_RECURSE_READDIR;
1338 read_directory(dir, path, base, baselen, pathspec);
1340 // if root dir is ignored, then all unversioned files under it are considered ignore
1341 enum_unversioned(dir->entries, dir->nr, l_dirTree.bExplicitlyIgnored);
1342 enum_unversioned(dir->ignored, dir->ignored_nr, TRUE);
1344 else if (!single_dir && !l_nEnumeratedCached)
1346 // get status of a single unversioned file
1348 if (!bIgnoreInitialized)
1350 exc_given = 1;
1351 setup_standard_excludes(dir);
1352 bIgnoreInitialized = TRUE;
1355 LPCSTR sFileName;
1357 if (!l_bFullPath)
1359 sFileName = pszSubPath + prefix_offset;
1361 else
1363 strcpy(l_lpszFileName, pszSubPath);
1364 sFileName = l_sFullPathBuf;
1367 int dtype = DT_REG;
1368 // if root dir is ignored, then all unversioned files under it are considered ignore
1369 if (!l_dirTree.bExplicitlyIgnored && excluded(dir, pszSubPath, &dtype))
1370 fputs("F I 0000000000000000000000000000000000000000 ", stdout);
1371 else
1372 fputs("F U 0000000000000000000000000000000000000000 ", stdout);
1373 fputs(sFileName, stdout);
1374 fputc(0, stdout);
1377 if (l_bDirStatus)
1379 // enumerate dirs
1381 LPCSTR lpszRootDir="/";
1382 if (l_bFullPath)
1384 lpszRootDir = l_sFullPathBuf;
1385 if (pathspec_len)
1387 strcpy(l_lpszFileName, *pathspec);
1388 l_lpszFileName += pathspec_len;
1391 *l_lpszFileName = 0;
1392 // remove trailng slash
1393 l_lpszFileName[-1] = 0;
1395 else if (pathspec_len)
1397 lpszRootDir = ".";
1399 strcpy(l_sFullPathBuf, *pathspec);
1400 l_sFullPathBuf[pathspec_len-1] = '/';
1401 l_sFullPathBuf[pathspec_len] = 0;
1402 l_lpszFileName = l_sFullPathBuf;
1404 else
1406 lpszRootDir = ".";
1408 l_lpszFileName = l_sFullPathBuf;
1411 if (single_dir)
1413 // enumerate single dir
1414 l_bSkipNormalDirs = FALSE;
1415 enum_dir(&l_dirTree, lpszRootDir);
1417 else if (!enum_dir(&l_dirTree, lpszRootDir) && l_dirTree.children)
1419 if (l_bFullPath)
1420 // re-add trailing slash
1421 l_lpszFileName[-1] = '/';
1423 struct DirStatus *p = l_dirTree.children;
1427 if ( enum_dirs(p, l_lpszFileName) )
1428 break;
1430 while ( (p = p->next) );
1434 return TRUE;