in builtin/add.c [374:592]
int cmd_add(int argc,
const char **argv,
const char *prefix,
struct repository *repo)
{
int exit_status = 0;
struct pathspec pathspec;
struct dir_struct dir = DIR_INIT;
int flags;
int add_new_files;
int require_pathspec;
char *seen = NULL;
char *ps_matched = NULL;
struct lock_file lock_file = LOCK_INIT;
repo_config(repo, 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) {
if (show_only)
die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch");
if (pathspec_from_file)
die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch");
exit(interactive_add(repo, argv + 1, prefix, patch_interactive));
}
if (edit_interactive) {
if (pathspec_from_file)
die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--edit");
return(edit_patch(repo, argc, argv, prefix));
}
argc--;
argv++;
if (0 <= addremove_explicit)
addremove = addremove_explicit;
else if (take_worktree_changes && ADDREMOVE_DEFAULT)
addremove = 0; /* "-u" was given but not "-A" */
if (addremove && take_worktree_changes)
die(_("options '%s' and '%s' cannot be used together"), "-A", "-u");
if (!show_only && ignore_missing)
die(_("the option '%s' requires '%s'"), "--ignore-missing", "--dry-run");
if (chmod_arg && ((chmod_arg[0] != '-' && chmod_arg[0] != '+') ||
chmod_arg[1] != 'x' || chmod_arg[2]))
die(_("--chmod param '%s' must be either -x or +x"), chmod_arg);
add_new_files = !take_worktree_changes && !refresh_only && !add_renormalize;
require_pathspec = !(take_worktree_changes || (0 < addremove_explicit));
prepare_repo_settings(repo);
repo->settings.command_requires_full_index = 0;
repo_hold_locked_index(repo, &lock_file, LOCK_DIE_ON_ERROR);
/*
* Check the "pathspec '%s' did not match any files" block
* below before enabling new magic.
*/
parse_pathspec(&pathspec, 0,
PATHSPEC_PREFER_FULL |
PATHSPEC_SYMLINK_LEADING_PATH,
prefix, argv);
if (pathspec_from_file) {
if (pathspec.nr)
die(_("'%s' and pathspec arguments cannot be used together"), "--pathspec-from-file");
parse_pathspec_file(&pathspec, 0,
PATHSPEC_PREFER_FULL |
PATHSPEC_SYMLINK_LEADING_PATH,
prefix, pathspec_from_file, pathspec_file_nul);
} else if (pathspec_file_nul) {
die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file");
}
if (require_pathspec && pathspec.nr == 0) {
fprintf(stderr, _("Nothing specified, nothing added.\n"));
advise_if_enabled(ADVICE_ADD_EMPTY_PATHSPEC,
_("Maybe you wanted to say 'git add .'?"));
return 0;
}
if (!take_worktree_changes && addremove_explicit < 0 && pathspec.nr)
/* Turn "git add pathspec..." to "git add -A pathspec..." */
addremove = 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 (repo_read_index_preload(repo, &pathspec, 0) < 0)
die(_("index file corrupt"));
die_in_unpopulated_submodule(repo->index, prefix);
die_path_inside_submodule(repo->index, &pathspec);
if (add_new_files) {
int baselen;
/* Set up the default git porcelain excludes */
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, repo->index, &pathspec);
if (pathspec.nr)
seen = prune_directory(repo, &dir, &pathspec, baselen);
}
if (refresh_only) {
exit_status |= refresh(repo, verbose, &pathspec);
goto finish;
}
if (pathspec.nr) {
int i;
char *skip_worktree_seen = NULL;
struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP;
if (!seen)
seen = find_pathspecs_matching_against_index(&pathspec,
repo->index, PS_IGNORE_SKIP_WORKTREE);
/*
* file_exists() assumes exact match
*/
GUARD_PATHSPEC(&pathspec,
PATHSPEC_FROMTOP |
PATHSPEC_LITERAL |
PATHSPEC_GLOB |
PATHSPEC_ICASE |
PATHSPEC_EXCLUDE |
PATHSPEC_ATTR);
for (i = 0; i < pathspec.nr; i++) {
const char *path = pathspec.items[i].match;
if (pathspec.items[i].magic & PATHSPEC_EXCLUDE)
continue;
if (seen[i])
continue;
if (!include_sparse &&
matches_skip_worktree(&pathspec, i, &skip_worktree_seen)) {
string_list_append(&only_match_skip_worktree,
pathspec.items[i].original);
continue;
}
/* Don't complain at 'git add .' on empty repo */
if (!path[0])
continue;
if ((pathspec.items[i].magic & (PATHSPEC_GLOB | PATHSPEC_ICASE)) ||
!file_exists(path)) {
if (ignore_missing) {
int dtype = DT_UNKNOWN;
if (is_excluded(&dir, repo->index, path, &dtype))
dir_add_ignored(&dir, repo->index,
path, pathspec.items[i].len);
} else
die(_("pathspec '%s' did not match any files"),
pathspec.items[i].original);
}
}
if (only_match_skip_worktree.nr) {
advise_on_updating_sparse_paths(&only_match_skip_worktree);
exit_status = 1;
}
free(seen);
free(skip_worktree_seen);
string_list_clear(&only_match_skip_worktree, 0);
}
begin_odb_transaction();
ps_matched = xcalloc(pathspec.nr, 1);
if (add_renormalize)
exit_status |= renormalize_tracked_files(repo, &pathspec, flags);
else
exit_status |= add_files_to_cache(repo, prefix,
&pathspec, ps_matched,
include_sparse, flags);
if (take_worktree_changes && !add_renormalize && !ignore_add_errors &&
report_path_error(ps_matched, &pathspec))
exit(128);
if (add_new_files)
exit_status |= add_files(repo, &dir, flags);
if (chmod_arg && pathspec.nr)
exit_status |= chmod_pathspec(repo, &pathspec, chmod_arg[0], show_only);
end_odb_transaction();
finish:
if (write_locked_index(repo->index, &lock_file,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("unable to write new index file"));
free(ps_matched);
dir_clear(&dir);
clear_pathspec(&pathspec);
return exit_status;
}