summaryrefslogtreecommitdiff
path: root/builtin/add.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/add.c')
-rw-r--r--builtin/add.c165
1 files changed, 88 insertions, 77 deletions
diff --git a/builtin/add.c b/builtin/add.c
index c59b0c98fe..ab1c9e8fb7 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -6,6 +6,7 @@
#include "cache.h"
#include "builtin.h"
#include "dir.h"
+#include "pathspec.h"
#include "exec_cmd.h"
#include "cache-tree.h"
#include "run-command.h"
@@ -13,9 +14,10 @@
#include "diff.h"
#include "diffcore.h"
#include "revision.h"
+#include "bulk-checkin.h"
static const char * const builtin_add_usage[] = {
- "git add [options] [--] <filepattern>...",
+ N_("git add [options] [--] <pathspec>..."),
NULL
};
static int patch_interactive, add_interactive, edit_interactive;
@@ -96,39 +98,6 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
return !!data.add_errors;
}
-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 were walking 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 char *find_used_pathspec(const char **pathspec)
-{
- char *seen;
- int i;
-
- for (i = 0; pathspec[i]; i++)
- ; /* just counting */
- seen = xcalloc(i, 1);
- fill_pathspec_matches(pathspec, seen, i);
- return seen;
-}
-
static char *prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
{
char *seen;
@@ -148,10 +117,14 @@ static char *prune_directory(struct dir_struct *dir, const char **pathspec, int
*dst++ = entry;
}
dir->nr = dst - dir->entries;
- fill_pathspec_matches(pathspec, seen, specs);
+ add_pathspec_matches_against_index(pathspec, seen, specs);
return seen;
}
+/*
+ * Checks the index to see whether any path in pathspec refers to
+ * something inside a submodule. If so, dies with an error message.
+ */
static void treat_gitlinks(const char **pathspec)
{
int i;
@@ -159,24 +132,8 @@ static void treat_gitlinks(const char **pathspec)
if (!pathspec || !*pathspec)
return;
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
- if (S_ISGITLINK(ce->ce_mode)) {
- int len = ce_namelen(ce), j;
- for (j = 0; pathspec[j]; j++) {
- int len2 = strlen(pathspec[j]);
- if (len2 <= len || pathspec[j][len] != '/' ||
- memcmp(ce->name, pathspec[j], len))
- continue;
- if (len2 == len + 1)
- /* strip trailing slash */
- pathspec[j] = xstrndup(ce->name, len);
- else
- die (_("Path '%s' is in submodule '%.*s'"),
- pathspec[j], len, ce->name);
- }
- }
- }
+ for (i = 0; pathspec[i]; i++)
+ pathspec[i] = check_path_for_gitlink(pathspec[i]);
}
static void refresh(int verbose, const char **pathspec)
@@ -196,17 +153,19 @@ static void refresh(int verbose, const char **pathspec)
free(seen);
}
-static const char **validate_pathspec(int argc, const char **argv, const char *prefix)
+/*
+ * Normalizes argv relative to prefix, via get_pathspec(), and then
+ * runs die_if_path_beyond_symlink() on each path in the normalized
+ * list.
+ */
+static const char **validate_pathspec(const char **argv, const char *prefix)
{
const char **pathspec = get_pathspec(prefix, argv);
if (pathspec) {
const char **p;
for (p = pathspec; *p; p++) {
- if (has_symlink_leading_path(*p, strlen(*p))) {
- int len = prefix ? strlen(prefix) : 0;
- die(_("'%s' is beyond a symbolic link"), *p + len);
- }
+ die_if_path_beyond_symlink(*p, prefix);
}
}
@@ -247,7 +206,7 @@ int interactive_add(int argc, const char **argv, const char *prefix, int patch)
const char **pathspec = NULL;
if (argc) {
- pathspec = validate_pathspec(argc, argv, prefix);
+ pathspec = validate_pathspec(argv, prefix);
if (!pathspec)
return -1;
}
@@ -259,7 +218,7 @@ int interactive_add(int argc, const char **argv, const char *prefix, int patch)
static int edit_patch(int argc, const char **argv, const char *prefix)
{
- char *file = xstrdup(git_path("ADD_EDIT.patch"));
+ char *file = git_pathdup("ADD_EDIT.patch");
const char *apply_argv[] = { "apply", "--recount", "--cached",
NULL, NULL };
struct child_process child;
@@ -279,7 +238,8 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
argc = setup_revisions(argc, argv, &rev, NULL);
rev.diffopt.output_format = DIFF_FORMAT_PATCH;
- out = open(file, O_CREAT | O_WRONLY, 0644);
+ DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES);
+ out = open(file, O_CREAT | O_WRONLY, 0666);
if (out < 0)
die (_("Could not open '%s' for writing."), file);
rev.diffopt.file = xfdopen(out, "w");
@@ -301,6 +261,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
die (_("Could not apply '%s'"), file);
unlink(file);
+ free(file);
return 0;
}
@@ -313,19 +274,19 @@ static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
static int ignore_add_errors, addremove, intent_to_add, ignore_missing = 0;
static struct option builtin_add_options[] = {
- OPT__DRY_RUN(&show_only, "dry run"),
- OPT__VERBOSE(&verbose, "be verbose"),
+ OPT__DRY_RUN(&show_only, N_("dry run")),
+ OPT__VERBOSE(&verbose, N_("be verbose")),
OPT_GROUP(""),
- OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"),
- OPT_BOOLEAN('p', "patch", &patch_interactive, "select hunks interactively"),
- OPT_BOOLEAN('e', "edit", &edit_interactive, "edit current diff and apply"),
- OPT__FORCE(&ignored_too, "allow adding otherwise ignored files"),
- OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"),
- OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"),
- OPT_BOOLEAN('A', "all", &addremove, "add changes from all tracked and untracked files"),
- OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
- OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
- OPT_BOOLEAN( 0 , "ignore-missing", &ignore_missing, "check if - even missing - files are ignored in dry run"),
+ OPT_BOOLEAN('i', "interactive", &add_interactive, N_("interactive picking")),
+ OPT_BOOLEAN('p', "patch", &patch_interactive, N_("select hunks interactively")),
+ OPT_BOOLEAN('e', "edit", &edit_interactive, N_("edit current diff and apply")),
+ OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files")),
+ OPT_BOOLEAN('u', "update", &take_worktree_changes, N_("update tracked files")),
+ OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, N_("record only the fact that the path will be added later")),
+ OPT_BOOLEAN('A', "all", &addremove, N_("add changes from all tracked and untracked files")),
+ OPT_BOOLEAN( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")),
+ OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")),
+ OPT_BOOLEAN( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")),
OPT_END(),
};
@@ -360,6 +321,35 @@ static int add_files(struct dir_struct *dir, int flags)
return exit_status;
}
+static void warn_pathless_add(const char *option_name, const char *short_name) {
+ /*
+ * To be consistent with "git add -p" and most Git
+ * commands, we should default to being tree-wide, but
+ * this is not the original behavior and can't be
+ * changed until users trained themselves not to type
+ * "git add -u" or "git add -A". For now, we warn and
+ * keep the old behavior. Later, the behavior can be changed
+ * to tree-wide, keeping the warning for a while, and
+ * eventually we can drop the warning.
+ */
+ warning(_("The behavior of 'git add %s (or %s)' with no path argument from a\n"
+ "subdirectory of the tree will change in Git 2.0 and should not be used anymore.\n"
+ "To add content for the whole tree, run:\n"
+ "\n"
+ " git add %s :/\n"
+ " (or git add %s :/)\n"
+ "\n"
+ "To restrict the command to the current directory, run:\n"
+ "\n"
+ " git add %s .\n"
+ " (or git add %s .)\n"
+ "\n"
+ "With the current Git version, the command is restricted to the current directory."),
+ option_name, short_name,
+ option_name, short_name,
+ option_name, short_name);
+}
+
int cmd_add(int argc, const char **argv, const char *prefix)
{
int exit_status = 0;
@@ -370,6 +360,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
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);
@@ -389,8 +381,19 @@ int cmd_add(int argc, const char **argv, const char *prefix)
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 || take_worktree_changes) && !argc) {
+ 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;
}
@@ -412,7 +415,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
return 0;
}
- pathspec = validate_pathspec(argc, argv, prefix);
+ pathspec = validate_pathspec(argv, prefix);
if (read_cache() < 0)
die(_("index file corrupt"));
@@ -441,14 +444,17 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (pathspec) {
int i;
+ struct path_exclude_check check;
+
+ path_exclude_check_init(&check, &dir);
if (!seen)
- seen = find_used_pathspec(pathspec);
+ 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 (excluded(&dir, pathspec[i], &dtype))
+ 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"),
@@ -456,13 +462,18 @@ int cmd_add(int argc, const char **argv, const char *prefix)
}
}
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) ||