static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, int prefix, unsigned flag) { char *seen; int i; struct dir_entry **src, **dst; seen = xcalloc(pathspec->nr, 1); src = dst = dir->entries; i = dir->nr; while (--i >= 0) { struct dir_entry *entry = *src++; if (match_pathspec_depth(pathspec, entry->name, entry->len, prefix, seen)) *dst++ = entry; else if (flag & WARN_IMPLICIT_DOT) /* * "git add -A" was run from a subdirectory with a * new file outside that directory. * * "git add -A" will behave like "git add -A :/" * instead of "git add -A ." in the future. * Warn about the coming behavior change. */ warn_pathless_add(); } dir->nr = dst - dir->entries; add_pathspec_matches_against_index(pathspec, seen); return seen; }
static void update_callback(struct diff_queue_struct *q, struct diff_options *opt, void *cbdata) { int i; struct update_callback_data *data = cbdata; const char *implicit_dot = data->implicit_dot; size_t implicit_dot_len = data->implicit_dot_len; for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; const char *path = p->one->path; /* * Check if "git add -A" or "git add -u" was run from a * subdirectory with a modified file outside that directory, * and warn if so. * * "git add -u" will behave like "git add -u :/" instead of * "git add -u ." in the future. This warning prepares for * that change. */ if (implicit_dot && strncmp_icase(path, implicit_dot, implicit_dot_len)) { warn_pathless_add(); continue; } switch (fix_unmerged_status(p, data)) { default: die(_("unexpected diff status %c"), p->status); case DIFF_STATUS_MODIFIED: case DIFF_STATUS_TYPE_CHANGED: if (add_file_to_index(&the_index, path, data->flags)) { if (!(data->flags & ADD_CACHE_IGNORE_ERRORS)) die(_("updating files failed")); data->add_errors++; } break; case DIFF_STATUS_DELETED: if (data->warn_add_would_remove) { warn_add_would_remove(path); data->warn_add_would_remove = 0; } if (data->flags & ADD_CACHE_IGNORE_REMOVAL) break; if (!(data->flags & ADD_CACHE_PRETEND)) remove_file_from_index(&the_index, path); if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE)) printf(_("remove '%s'\n"), path); break; } } }
int cmd_add(int argc, const char **argv, const char *prefix) { int exit_status = 0; int newfd; const char **pathspec; struct dir_struct dir; int flags; int add_new_files; int require_pathspec; char *seen = NULL; const char *option_with_implicit_dot = NULL; const char *short_option_with_implicit_dot = NULL; git_config(add_config, NULL); argc = parse_options(argc, argv, prefix, builtin_add_options, builtin_add_usage, PARSE_OPT_KEEP_ARGV0); if (patch_interactive) add_interactive = 1; if (add_interactive) exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive)); if (edit_interactive) return(edit_patch(argc, argv, prefix)); argc--; argv++; if (addremove && take_worktree_changes) die(_("-A and -u are mutually incompatible")); if (!show_only && ignore_missing) die(_("Option --ignore-missing can only be used together with --dry-run")); if (addremove) { option_with_implicit_dot = "--all"; short_option_with_implicit_dot = "-A"; } if (take_worktree_changes) { option_with_implicit_dot = "--update"; short_option_with_implicit_dot = "-u"; } if (option_with_implicit_dot && !argc) { static const char *here[2] = { ".", NULL }; if (prefix) warn_pathless_add(option_with_implicit_dot, short_option_with_implicit_dot); argc = 1; argv = here; } add_new_files = !take_worktree_changes && !refresh_only; require_pathspec = !take_worktree_changes; newfd = hold_locked_index(&lock_file, 1); flags = ((verbose ? ADD_CACHE_VERBOSE : 0) | (show_only ? ADD_CACHE_PRETEND : 0) | (intent_to_add ? ADD_CACHE_INTENT : 0) | (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) | (!(addremove || take_worktree_changes) ? ADD_CACHE_IGNORE_REMOVAL : 0)); if (require_pathspec && argc == 0) { fprintf(stderr, _("Nothing specified, nothing added.\n")); fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n")); return 0; } pathspec = validate_pathspec(argv, prefix); if (read_cache() < 0) die(_("index file corrupt")); treat_gitlinks(pathspec); if (add_new_files) { int baselen; /* Set up the default git porcelain excludes */ memset(&dir, 0, sizeof(dir)); if (!ignored_too) { dir.flags |= DIR_COLLECT_IGNORED; setup_standard_excludes(&dir); } /* This picks up the paths that are not tracked */ baselen = fill_directory(&dir, pathspec); if (pathspec) seen = prune_directory(&dir, pathspec, baselen); } if (refresh_only) { refresh(verbose, pathspec); goto finish; } if (pathspec) { int i; struct path_exclude_check check; path_exclude_check_init(&check, &dir); if (!seen) seen = find_pathspecs_matching_against_index(pathspec); for (i = 0; pathspec[i]; i++) { if (!seen[i] && pathspec[i][0] && !file_exists(pathspec[i])) { if (ignore_missing) { int dtype = DT_UNKNOWN; if (is_path_excluded(&check, pathspec[i], -1, &dtype)) dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i])); } else die(_("pathspec '%s' did not match any files"), pathspec[i]); } } free(seen); path_exclude_check_clear(&check); } plug_bulk_checkin(); exit_status |= add_files_to_cache(prefix, pathspec, flags); if (add_new_files) exit_status |= add_files(&dir, flags); unplug_bulk_checkin(); finish: if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_locked_index(&lock_file)) die(_("Unable to write new index file")); } return exit_status; }