Skip to content
This repository was archived by the owner on Nov 9, 2017. It is now read-only.

Commit de91daf

Browse files
committed
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
2 parents d7bffe9 + 9df84e9 commit de91daf

File tree

2 files changed

+95
-40
lines changed

2 files changed

+95
-40
lines changed

builtin/add.c

Lines changed: 94 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,52 @@ static int take_worktree_changes;
2626
struct update_callback_data {
2727
int flags;
2828
int add_errors;
29+
const char *implicit_dot;
30+
size_t implicit_dot_len;
2931
};
3032

33+
static const char *option_with_implicit_dot;
34+
static const char *short_option_with_implicit_dot;
35+
36+
static void warn_pathless_add(void)
37+
{
38+
static int shown;
39+
assert(option_with_implicit_dot && short_option_with_implicit_dot);
40+
41+
if (shown)
42+
return;
43+
shown = 1;
44+
45+
/*
46+
* To be consistent with "git add -p" and most Git
47+
* commands, we should default to being tree-wide, but
48+
* this is not the original behavior and can't be
49+
* changed until users trained themselves not to type
50+
* "git add -u" or "git add -A". For now, we warn and
51+
* keep the old behavior. Later, the behavior can be changed
52+
* to tree-wide, keeping the warning for a while, and
53+
* eventually we can drop the warning.
54+
*/
55+
warning(_("The behavior of 'git add %s (or %s)' with no path argument from a\n"
56+
"subdirectory of the tree will change in Git 2.0 and should not be used anymore.\n"
57+
"To add content for the whole tree, run:\n"
58+
"\n"
59+
" git add %s :/\n"
60+
" (or git add %s :/)\n"
61+
"\n"
62+
"To restrict the command to the current directory, run:\n"
63+
"\n"
64+
" git add %s .\n"
65+
" (or git add %s .)\n"
66+
"\n"
67+
"With the current Git version, the command is restricted to "
68+
"the current directory.\n"
69+
""),
70+
option_with_implicit_dot, short_option_with_implicit_dot,
71+
option_with_implicit_dot, short_option_with_implicit_dot,
72+
option_with_implicit_dot, short_option_with_implicit_dot);
73+
}
74+
3175
static int fix_unmerged_status(struct diff_filepair *p,
3276
struct update_callback_data *data)
3377
{
@@ -54,10 +98,26 @@ static void update_callback(struct diff_queue_struct *q,
5498
{
5599
int i;
56100
struct update_callback_data *data = cbdata;
101+
const char *implicit_dot = data->implicit_dot;
102+
size_t implicit_dot_len = data->implicit_dot_len;
57103

58104
for (i = 0; i < q->nr; i++) {
59105
struct diff_filepair *p = q->queue[i];
60106
const char *path = p->one->path;
107+
/*
108+
* Check if "git add -A" or "git add -u" was run from a
109+
* subdirectory with a modified file outside that directory,
110+
* and warn if so.
111+
*
112+
* "git add -u" will behave like "git add -u :/" instead of
113+
* "git add -u ." in the future. This warning prepares for
114+
* that change.
115+
*/
116+
if (implicit_dot &&
117+
strncmp_icase(path, implicit_dot, implicit_dot_len)) {
118+
warn_pathless_add();
119+
continue;
120+
}
61121
switch (fix_unmerged_status(p, data)) {
62122
default:
63123
die(_("unexpected diff status %c"), p->status);
@@ -85,20 +145,34 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
85145
{
86146
struct update_callback_data data;
87147
struct rev_info rev;
148+
149+
memset(&data, 0, sizeof(data));
150+
data.flags = flags & ~ADD_CACHE_IMPLICIT_DOT;
151+
if ((flags & ADD_CACHE_IMPLICIT_DOT) && prefix) {
152+
/*
153+
* Check for modified files throughout the worktree so
154+
* update_callback has a chance to warn about changes
155+
* outside the cwd.
156+
*/
157+
data.implicit_dot = prefix;
158+
data.implicit_dot_len = strlen(prefix);
159+
pathspec = NULL;
160+
}
161+
88162
init_revisions(&rev, prefix);
89163
setup_revisions(0, NULL, &rev, NULL);
90164
init_pathspec(&rev.prune_data, pathspec);
91165
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
92166
rev.diffopt.format_callback = update_callback;
93-
data.flags = flags;
94-
data.add_errors = 0;
95167
rev.diffopt.format_callback_data = &data;
96168
rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
97169
run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
98170
return !!data.add_errors;
99171
}
100172

101-
static char *prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
173+
#define WARN_IMPLICIT_DOT (1u << 0)
174+
static char *prune_directory(struct dir_struct *dir, const char **pathspec,
175+
int prefix, unsigned flag)
102176
{
103177
char *seen;
104178
int i, specs;
@@ -115,6 +189,16 @@ static char *prune_directory(struct dir_struct *dir, const char **pathspec, int
115189
if (match_pathspec(pathspec, entry->name, entry->len,
116190
prefix, seen))
117191
*dst++ = entry;
192+
else if (flag & WARN_IMPLICIT_DOT)
193+
/*
194+
* "git add -A" was run from a subdirectory with a
195+
* new file outside that directory.
196+
*
197+
* "git add -A" will behave like "git add -A :/"
198+
* instead of "git add -A ." in the future.
199+
* Warn about the coming behavior change.
200+
*/
201+
warn_pathless_add();
118202
}
119203
dir->nr = dst - dir->entries;
120204
add_pathspec_matches_against_index(pathspec, seen, specs);
@@ -321,35 +405,6 @@ static int add_files(struct dir_struct *dir, int flags)
321405
return exit_status;
322406
}
323407

324-
static void warn_pathless_add(const char *option_name, const char *short_name) {
325-
/*
326-
* To be consistent with "git add -p" and most Git
327-
* commands, we should default to being tree-wide, but
328-
* this is not the original behavior and can't be
329-
* changed until users trained themselves not to type
330-
* "git add -u" or "git add -A". For now, we warn and
331-
* keep the old behavior. Later, the behavior can be changed
332-
* to tree-wide, keeping the warning for a while, and
333-
* eventually we can drop the warning.
334-
*/
335-
warning(_("The behavior of 'git add %s (or %s)' with no path argument from a\n"
336-
"subdirectory of the tree will change in Git 2.0 and should not be used anymore.\n"
337-
"To add content for the whole tree, run:\n"
338-
"\n"
339-
" git add %s :/\n"
340-
" (or git add %s :/)\n"
341-
"\n"
342-
"To restrict the command to the current directory, run:\n"
343-
"\n"
344-
" git add %s .\n"
345-
" (or git add %s .)\n"
346-
"\n"
347-
"With the current Git version, the command is restricted to the current directory."),
348-
option_name, short_name,
349-
option_name, short_name,
350-
option_name, short_name);
351-
}
352-
353408
int cmd_add(int argc, const char **argv, const char *prefix)
354409
{
355410
int exit_status = 0;
@@ -360,8 +415,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
360415
int add_new_files;
361416
int require_pathspec;
362417
char *seen = NULL;
363-
const char *option_with_implicit_dot = NULL;
364-
const char *short_option_with_implicit_dot = NULL;
418+
int implicit_dot = 0;
365419

366420
git_config(add_config, NULL);
367421

@@ -391,11 +445,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
391445
}
392446
if (option_with_implicit_dot && !argc) {
393447
static const char *here[2] = { ".", NULL };
394-
if (prefix)
395-
warn_pathless_add(option_with_implicit_dot,
396-
short_option_with_implicit_dot);
397448
argc = 1;
398449
argv = here;
450+
implicit_dot = 1;
399451
}
400452

401453
add_new_files = !take_worktree_changes && !refresh_only;
@@ -408,7 +460,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
408460
(intent_to_add ? ADD_CACHE_INTENT : 0) |
409461
(ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
410462
(!(addremove || take_worktree_changes)
411-
? ADD_CACHE_IGNORE_REMOVAL : 0));
463+
? ADD_CACHE_IGNORE_REMOVAL : 0)) |
464+
(implicit_dot ? ADD_CACHE_IMPLICIT_DOT : 0);
412465

413466
if (require_pathspec && argc == 0) {
414467
fprintf(stderr, _("Nothing specified, nothing added.\n"));
@@ -432,9 +485,10 @@ int cmd_add(int argc, const char **argv, const char *prefix)
432485
}
433486

434487
/* This picks up the paths that are not tracked */
435-
baselen = fill_directory(&dir, pathspec);
488+
baselen = fill_directory(&dir, implicit_dot ? NULL : pathspec);
436489
if (pathspec)
437-
seen = prune_directory(&dir, pathspec, baselen);
490+
seen = prune_directory(&dir, pathspec, baselen,
491+
implicit_dot ? WARN_IMPLICIT_DOT : 0);
438492
}
439493

440494
if (refresh_only) {

cache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ extern int remove_file_from_index(struct index_state *, const char *path);
465465
#define ADD_CACHE_IGNORE_ERRORS 4
466466
#define ADD_CACHE_IGNORE_REMOVAL 8
467467
#define ADD_CACHE_INTENT 16
468+
#define ADD_CACHE_IMPLICIT_DOT 32 /* internal to "git add -u/-A" */
468469
extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
469470
extern int add_file_to_index(struct index_state *, const char *path, int flags);
470471
extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);

0 commit comments

Comments
 (0)