@matheusbsbs / matheus-bagliano / commits / 1e5f764c93
Fork 0

builtin-add.c: optimize -A option and "git add ."

The earlier "git add -A" change was done in a quite inefficient way (i.e. it is as unefficient as "git add -u && git add ." modulo one fork/exec and read/write index). When the user asks "git add .", we do not have to examine all paths we encounter and perform the excluded() and dir_add_name() processing, both of which are slower code and use slower data structure by git standards, especially when the index is already populated. Instead, we implement "git add $pathspec..." as: - read the index; - read_directory() to process untracked, unignored files the current way, that is, recursively doing readdir(), filtering them by pathspec and excluded(), queueing them via dir_add_name() and finally do add_files(); and - iterate over the index, filtering them by pathspec, and update only the modified/type changed paths but not deleted ones. And "git add -A" becomes exactly the same as above, modulo: - missing $pathspec means "." instead of being an error; and - "iterate over the index" part handles deleted ones as well, i.e. exactly what the current update_callback() in builtin-add.c does. In either case, because fill_directory() does not use read_directory() to read everything in, we need to add an extra logic to iterate over the index to catch mistyped pathspec. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jul 22, 2008 at 22:30 UTC 1e5f764c93edfd8f6575b6ede769b079a1fc5a21
1 file changed +31 -12
builtin-add.c
+31 -12
@@ -18,6 +18,27 @@ static const char * const builtin_add_usage[] = {
18 static int patch_interactive = 0, add_interactive = 0;
19 static int take_worktree_changes;
20
21 +static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)
22 +{
23 + int num_unmatched = 0, i;
24 +
25 + /*
26 + * Since we are walking the index as if we are warlking the directory,
27 + * we have to mark the matched pathspec as seen; otherwise we will
28 + * mistakenly think that the user gave a pathspec that did not match
29 + * anything.
30 + */
31 + for (i = 0; i < specs; i++)
32 + if (!seen[i])
33 + num_unmatched++;
34 + if (!num_unmatched)
35 + return;
36 + for (i = 0; i < active_nr; i++) {
37 + struct cache_entry *ce = active_cache[i];
38 + match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);
39 + }
40 +}
41 +
42 static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
43 {
44 char *seen;
@@ -37,6 +58,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
58 *dst++ = entry;
59 }
60 dir->nr = dst - dir->entries;
61 + fill_pathspec_matches(pathspec, seen, specs);
62
63 for (i = 0; i < specs; i++) {
64 if (!seen[i] && !file_exists(pathspec[i]))
@@ -201,7 +223,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
223
224 if (addremove && take_worktree_changes)
225 die("-A and -u are mutually incompatible");
204 - if (addremove && !argc) {
226 + if ((addremove || take_worktree_changes) && !argc) {
227 static const char *here[2] = { ".", NULL };
228 argc = 1;
229 argv = here;
@@ -214,7 +236,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
236
237 flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
238 (show_only ? ADD_CACHE_PRETEND : 0) |
217 - (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0));
239 + (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
240 + (!(addremove || take_worktree_changes)
241 + ? ADD_CACHE_IGNORE_REMOVAL : 0));
242
243 if (require_pathspec && argc == 0) {
244 fprintf(stderr, "Nothing specified, nothing added.\n");
@@ -223,24 +247,19 @@ int cmd_add(int argc, const char **argv, const char *prefix)
247 }
248 pathspec = get_pathspec(prefix, argv);
249
226 - /*
227 - * If we are adding new files, we need to scan the working
228 - * tree to find the ones that match pathspecs; this needs
229 - * to be done before we read the index.
230 - */
231 - if (add_new_files)
232 - fill_directory(&dir, pathspec, ignored_too);
233 -
250 if (read_cache() < 0)
251 die("index file corrupt");
252
253 + if (add_new_files)
254 + /* This picks up the paths that are not tracked */
255 + fill_directory(&dir, pathspec, ignored_too);
256 +
257 if (refresh_only) {
258 refresh(verbose, pathspec);
259 goto finish;
260 }
261
242 - if (take_worktree_changes || addremove)
243 - exit_status |= add_files_to_cache(prefix, pathspec, flags);
262 + exit_status |= add_files_to_cache(prefix, pathspec, flags);
263
264 if (add_new_files)
265 exit_status |= add_files(&dir, flags);