Merge branch 'jn/add-2.0-u-A-sans-pathspec' (early part)
In Git 2.0, "git add -u" and "git add -A" without any pathspec will update the index for all paths, including those outside the current directory, making it more consistent with "commit -a". To help the migration pain, a warning is issued when the differences between the current behaviour and the upcoming behaviour matters, i.e. when the user has local changes outside the current directory. * 'jn/add-2.0-u-A-sans-pathspec' (early part): add -A: only show pathless 'add -A' warning when changes exist outside cwd add -u: only show pathless 'add -u' warning when changes exist outside cwd add: make warn_pathless_add() a no-op after first call add: add a blank line at the end of pathless 'add [-u|-A]' warning add: make pathless 'add [-u|-A]' warning a file-global function
This commit is contained in:
commit
de91daf5e6
134
builtin/add.c
134
builtin/add.c
@ -26,8 +26,52 @@ static int take_worktree_changes;
|
||||
struct update_callback_data {
|
||||
int flags;
|
||||
int add_errors;
|
||||
const char *implicit_dot;
|
||||
size_t implicit_dot_len;
|
||||
};
|
||||
|
||||
static const char *option_with_implicit_dot;
|
||||
static const char *short_option_with_implicit_dot;
|
||||
|
||||
static void warn_pathless_add(void)
|
||||
{
|
||||
static int shown;
|
||||
assert(option_with_implicit_dot && short_option_with_implicit_dot);
|
||||
|
||||
if (shown)
|
||||
return;
|
||||
shown = 1;
|
||||
|
||||
/*
|
||||
* 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.\n"
|
||||
""),
|
||||
option_with_implicit_dot, short_option_with_implicit_dot,
|
||||
option_with_implicit_dot, short_option_with_implicit_dot,
|
||||
option_with_implicit_dot, short_option_with_implicit_dot);
|
||||
}
|
||||
|
||||
static int fix_unmerged_status(struct diff_filepair *p,
|
||||
struct update_callback_data *data)
|
||||
{
|
||||
@ -54,10 +98,26 @@ static void update_callback(struct diff_queue_struct *q,
|
||||
{
|
||||
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);
|
||||
@ -85,20 +145,34 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
|
||||
{
|
||||
struct update_callback_data data;
|
||||
struct rev_info rev;
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.flags = flags & ~ADD_CACHE_IMPLICIT_DOT;
|
||||
if ((flags & ADD_CACHE_IMPLICIT_DOT) && prefix) {
|
||||
/*
|
||||
* Check for modified files throughout the worktree so
|
||||
* update_callback has a chance to warn about changes
|
||||
* outside the cwd.
|
||||
*/
|
||||
data.implicit_dot = prefix;
|
||||
data.implicit_dot_len = strlen(prefix);
|
||||
pathspec = NULL;
|
||||
}
|
||||
|
||||
init_revisions(&rev, prefix);
|
||||
setup_revisions(0, NULL, &rev, NULL);
|
||||
init_pathspec(&rev.prune_data, pathspec);
|
||||
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
|
||||
rev.diffopt.format_callback = update_callback;
|
||||
data.flags = flags;
|
||||
data.add_errors = 0;
|
||||
rev.diffopt.format_callback_data = &data;
|
||||
rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
|
||||
run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
|
||||
return !!data.add_errors;
|
||||
}
|
||||
|
||||
static char *prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
|
||||
#define WARN_IMPLICIT_DOT (1u << 0)
|
||||
static char *prune_directory(struct dir_struct *dir, const char **pathspec,
|
||||
int prefix, unsigned flag)
|
||||
{
|
||||
char *seen;
|
||||
int i, specs;
|
||||
@ -115,6 +189,16 @@ static char *prune_directory(struct dir_struct *dir, const char **pathspec, int
|
||||
if (match_pathspec(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, specs);
|
||||
@ -321,35 +405,6 @@ 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;
|
||||
@ -360,8 +415,7 @@ 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;
|
||||
int implicit_dot = 0;
|
||||
|
||||
git_config(add_config, NULL);
|
||||
|
||||
@ -391,11 +445,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
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;
|
||||
implicit_dot = 1;
|
||||
}
|
||||
|
||||
add_new_files = !take_worktree_changes && !refresh_only;
|
||||
@ -408,7 +460,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
(intent_to_add ? ADD_CACHE_INTENT : 0) |
|
||||
(ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
|
||||
(!(addremove || take_worktree_changes)
|
||||
? ADD_CACHE_IGNORE_REMOVAL : 0));
|
||||
? ADD_CACHE_IGNORE_REMOVAL : 0)) |
|
||||
(implicit_dot ? ADD_CACHE_IMPLICIT_DOT : 0);
|
||||
|
||||
if (require_pathspec && argc == 0) {
|
||||
fprintf(stderr, _("Nothing specified, nothing added.\n"));
|
||||
@ -432,9 +485,10 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
||||
}
|
||||
|
||||
/* This picks up the paths that are not tracked */
|
||||
baselen = fill_directory(&dir, pathspec);
|
||||
baselen = fill_directory(&dir, implicit_dot ? NULL : pathspec);
|
||||
if (pathspec)
|
||||
seen = prune_directory(&dir, pathspec, baselen);
|
||||
seen = prune_directory(&dir, pathspec, baselen,
|
||||
implicit_dot ? WARN_IMPLICIT_DOT : 0);
|
||||
}
|
||||
|
||||
if (refresh_only) {
|
||||
|
1
cache.h
1
cache.h
@ -465,6 +465,7 @@ extern int remove_file_from_index(struct index_state *, const char *path);
|
||||
#define ADD_CACHE_IGNORE_ERRORS 4
|
||||
#define ADD_CACHE_IGNORE_REMOVAL 8
|
||||
#define ADD_CACHE_INTENT 16
|
||||
#define ADD_CACHE_IMPLICIT_DOT 32 /* internal to "git add -u/-A" */
|
||||
extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
|
||||
extern int add_file_to_index(struct index_state *, const char *path, int flags);
|
||||
extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
|
||||
|
Loading…
Reference in New Issue
Block a user