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>
This commit is contained in:
Junio C Hamano 2008-07-22 22:30:40 -07:00
parent 041aee31be
commit 1e5f764c93

View File

@ -18,6 +18,27 @@ static const char * const builtin_add_usage[] = {
static int patch_interactive = 0, add_interactive = 0; static int patch_interactive = 0, add_interactive = 0;
static int take_worktree_changes; static int take_worktree_changes;
static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)
{
int num_unmatched = 0, i;
/*
* Since we are walking the index as if we are warlking the directory,
* we have to mark the matched pathspec as seen; otherwise we will
* mistakenly think that the user gave a pathspec that did not match
* anything.
*/
for (i = 0; i < specs; i++)
if (!seen[i])
num_unmatched++;
if (!num_unmatched)
return;
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);
}
}
static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix) static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
{ {
char *seen; char *seen;
@ -37,6 +58,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
*dst++ = entry; *dst++ = entry;
} }
dir->nr = dst - dir->entries; dir->nr = dst - dir->entries;
fill_pathspec_matches(pathspec, seen, specs);
for (i = 0; i < specs; i++) { for (i = 0; i < specs; i++) {
if (!seen[i] && !file_exists(pathspec[i])) if (!seen[i] && !file_exists(pathspec[i]))
@ -201,7 +223,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (addremove && take_worktree_changes) if (addremove && take_worktree_changes)
die("-A and -u are mutually incompatible"); die("-A and -u are mutually incompatible");
if (addremove && !argc) { if ((addremove || take_worktree_changes) && !argc) {
static const char *here[2] = { ".", NULL }; static const char *here[2] = { ".", NULL };
argc = 1; argc = 1;
argv = here; argv = here;
@ -214,7 +236,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
flags = ((verbose ? ADD_CACHE_VERBOSE : 0) | flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
(show_only ? ADD_CACHE_PRETEND : 0) | (show_only ? ADD_CACHE_PRETEND : 0) |
(ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0)); (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
(!(addremove || take_worktree_changes)
? ADD_CACHE_IGNORE_REMOVAL : 0));
if (require_pathspec && argc == 0) { if (require_pathspec && argc == 0) {
fprintf(stderr, "Nothing specified, nothing added.\n"); fprintf(stderr, "Nothing specified, nothing added.\n");
@ -223,24 +247,19 @@ int cmd_add(int argc, const char **argv, const char *prefix)
} }
pathspec = get_pathspec(prefix, argv); pathspec = get_pathspec(prefix, argv);
/*
* If we are adding new files, we need to scan the working
* tree to find the ones that match pathspecs; this needs
* to be done before we read the index.
*/
if (add_new_files)
fill_directory(&dir, pathspec, ignored_too);
if (read_cache() < 0) if (read_cache() < 0)
die("index file corrupt"); die("index file corrupt");
if (add_new_files)
/* This picks up the paths that are not tracked */
fill_directory(&dir, pathspec, ignored_too);
if (refresh_only) { if (refresh_only) {
refresh(verbose, pathspec); refresh(verbose, pathspec);
goto finish; goto finish;
} }
if (take_worktree_changes || addremove) exit_status |= add_files_to_cache(prefix, pathspec, flags);
exit_status |= add_files_to_cache(prefix, pathspec, flags);
if (add_new_files) if (add_new_files)
exit_status |= add_files(&dir, flags); exit_status |= add_files(&dir, flags);