Skip to content

Commit

Permalink
Merge branch 'nd/diff-apply-ita'
Browse files Browse the repository at this point in the history
"git diff" compares the index and the working tree.  For paths
added with intent-to-add bit, the command shows the full contents
of them as added, but the paths themselves were not marked as new
files.  They are now shown as new by default.

"git apply" learned the "--intent-to-add" option so that an
otherwise working-tree-only application of a patch will add new
paths to the index marked with the "intent-to-add" bit.

* nd/diff-apply-ita:
  apply: add --intent-to-add
  t2203: add a test about "diff HEAD" case
  diff: turn --ita-invisible-in-index on by default
  diff: ignore --ita-[in]visible-in-index when diffing worktree-to-tree
  • Loading branch information
gitster committed Jun 25, 2018
2 parents a856e7d + cff5dc0 commit ac997db
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 20 deletions.
10 changes: 9 additions & 1 deletion Documentation/git-apply.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ git-apply - Apply a patch to files and/or to the index
SYNOPSIS
--------
[verse]
'git apply' [--stat] [--numstat] [--summary] [--check] [--index] [--3way]
'git apply' [--stat] [--numstat] [--summary] [--check] [--index | --intent-to-add] [--3way]
[--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]
[--allow-binary-replacement | --binary] [--reject] [-z]
[-p<n>] [-C<n>] [--inaccurate-eof] [--recount] [--cached]
Expand Down Expand Up @@ -74,6 +74,14 @@ OPTIONS
cached data, apply the patch, and store the result in the index
without using the working tree. This implies `--index`.

--intent-to-add::
When applying the patch only to the working tree, mark new
files to be added to the index later (see `--intent-to-add`
option in linkgit:git-add[1]). This option is ignored unless
running in a Git repository and `--index` is not specified.
Note that `--index` could be implied by other options such
as `--cached` or `--3way`.

-3::
--3way::
When the patch does not apply cleanly, fall back on 3-way merge if
Expand Down
19 changes: 12 additions & 7 deletions apply.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ int check_apply_state(struct apply_state *state, int force_apply)
return error(_("--cached outside a repository"));
state->check_index = 1;
}
if (state->ita_only && (state->check_index || is_not_gitdir))
state->ita_only = 0;
if (state->check_index)
state->unsafe_paths = 0;

Expand Down Expand Up @@ -4242,7 +4244,7 @@ static void patch_stats(struct apply_state *state, struct patch *patch)

static int remove_file(struct apply_state *state, struct patch *patch, int rmdir_empty)
{
if (state->update_index) {
if (state->update_index && !state->ita_only) {
if (remove_file_from_cache(patch->old_name) < 0)
return error(_("unable to remove %s from index"), patch->old_name);
}
Expand All @@ -4265,15 +4267,15 @@ static int add_index_file(struct apply_state *state,
int namelen = strlen(path);
unsigned ce_size = cache_entry_size(namelen);

if (!state->update_index)
return 0;

ce = xcalloc(1, ce_size);
memcpy(ce->name, path, namelen);
ce->ce_mode = create_ce_mode(mode);
ce->ce_flags = create_ce_flags(0);
ce->ce_namelen = namelen;
if (S_ISGITLINK(mode)) {
if (state->ita_only) {
ce->ce_flags |= CE_INTENT_TO_ADD;
set_object_name_for_intent_to_add_entry(ce);
} else if (S_ISGITLINK(mode)) {
const char *s;

if (!skip_prefix(buf, "Subproject commit ", &s) ||
Expand Down Expand Up @@ -4465,8 +4467,9 @@ static int create_file(struct apply_state *state, struct patch *patch)

if (patch->conflicted_threeway)
return add_conflicted_stages_file(state, patch);
else
else if (state->update_index)
return add_index_file(state, path, mode, buf, size);
return 0;
}

/* phase zero is to remove, phase one is to create */
Expand Down Expand Up @@ -4686,7 +4689,7 @@ static int apply_patch(struct apply_state *state,
if (state->whitespace_error && (state->ws_error_action == die_on_ws_error))
state->apply = 0;

state->update_index = state->check_index && state->apply;
state->update_index = (state->check_index || state->ita_only) && state->apply;
if (state->update_index && !is_lock_file_locked(&state->lock_file)) {
if (state->index_file)
hold_lock_file_for_update(&state->lock_file,
Expand Down Expand Up @@ -4941,6 +4944,8 @@ int apply_parse_options(int argc, const char **argv,
N_("instead of applying the patch, see if the patch is applicable")),
OPT_BOOL(0, "index", &state->check_index,
N_("make sure the patch is applicable to the current index")),
OPT_BOOL('N', "intent-to-add", &state->ita_only,
N_("mark new files with `git add --intent-to-add`")),
OPT_BOOL(0, "cached", &state->cached,
N_("apply a patch without touching the working tree")),
OPT_BOOL_F(0, "unsafe-paths", &state->unsafe_paths,
Expand Down
1 change: 1 addition & 0 deletions apply.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ struct apply_state {
int check; /* preimage must match working tree, don't actually apply */
int check_index; /* preimage must match the indexed version */
int update_index; /* check_index && apply */
int ita_only; /* add intent-to-add entries to the index */

/* These control cosmetic aspect of the output */
int diffstat; /* just show a diffstat, and don't actually apply */
Expand Down
7 changes: 7 additions & 0 deletions builtin/diff.c
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,13 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
rev.diffopt.flags.allow_external = 1;
rev.diffopt.flags.allow_textconv = 1;

/*
* Default to intent-to-add entries invisible in the
* index. This makes them show up as new files in diff-files
* and not at all in diff-cached.
*/
rev.diffopt.ita_invisible_in_index = 1;

if (nongit)
die(_("Not a git repository"));
argc = setup_revisions(argc, argv, &rev, NULL);
Expand Down
8 changes: 6 additions & 2 deletions diff-lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -389,8 +389,12 @@ static void do_oneway_diff(struct unpack_trees_options *o,
struct rev_info *revs = o->unpack_data;
int match_missing, cached;

/* i-t-a entries do not actually exist in the index */
if (revs->diffopt.ita_invisible_in_index &&
/*
* i-t-a entries do not actually exist in the index (if we're
* looking at its content)
*/
if (o->index_only &&
revs->diffopt.ita_invisible_in_index &&
idx && ce_intent_to_add(idx)) {
idx = NULL;
if (!tree)
Expand Down
64 changes: 58 additions & 6 deletions t/t2203-add-intent.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ test_expect_success 'i-t-a entry is simply ignored' '
git commit -m second &&
test $(git ls-tree HEAD -- nitfol | wc -l) = 0 &&
test $(git diff --name-only HEAD -- nitfol | wc -l) = 1 &&
test $(git diff --name-only --ita-invisible-in-index HEAD -- nitfol | wc -l) = 0 &&
test $(git diff --name-only --ita-invisible-in-index -- nitfol | wc -l) = 1
test $(git diff --name-only -- nitfol | wc -l) = 1
'

test_expect_success 'can commit with an unrelated i-t-a entry in index' '
Expand Down Expand Up @@ -99,13 +98,13 @@ test_expect_success 'cache-tree invalidates i-t-a paths' '
: >dir/bar &&
git add -N dir/bar &&
git diff --cached --name-only >actual &&
git diff --name-only >actual &&
echo dir/bar >expect &&
test_cmp expect actual &&
git write-tree >/dev/null &&
git diff --cached --name-only >actual &&
git diff --name-only >actual &&
echo dir/bar >expect &&
test_cmp expect actual
'
Expand Down Expand Up @@ -186,7 +185,19 @@ test_expect_success 'rename detection finds the right names' '
cat >expected.3 <<-EOF &&
2 .R N... 100644 100644 100644 $hash $hash R100 third first
EOF
test_cmp expected.3 actual.3
test_cmp expected.3 actual.3 &&
git diff --stat >actual.4 &&
cat >expected.4 <<-EOF &&
first => third | 0
1 file changed, 0 insertions(+), 0 deletions(-)
EOF
test_cmp expected.4 actual.4 &&
git diff --cached --stat >actual.5 &&
: >expected.5 &&
test_cmp expected.5 actual.5
)
'

Expand Down Expand Up @@ -222,5 +233,46 @@ test_expect_success 'double rename detection in status' '
)
'

test_done
test_expect_success 'diff-files/diff-cached shows ita as new/not-new files' '
git reset --hard &&
echo new >new-ita &&
git add -N new-ita &&
git diff --summary >actual &&
echo " create mode 100644 new-ita" >expected &&
test_cmp expected actual &&
git diff --cached --summary >actual2 &&
: >expected2 &&
test_cmp expected2 actual2
'


test_expect_success '"diff HEAD" includes ita as new files' '
git reset --hard &&
echo new >new-ita &&
git add -N new-ita &&
git diff HEAD >actual &&
cat >expected <<-\EOF &&
diff --git a/new-ita b/new-ita
new file mode 100644
index 0000000..3e75765
--- /dev/null
+++ b/new-ita
@@ -0,0 +1 @@
+new
EOF
test_cmp expected actual
'

test_expect_success 'apply --intent-to-add' '
git reset --hard &&
echo new >new-ita &&
git add -N new-ita &&
git diff >expected &&
grep "new file" expected &&
git reset --hard &&
git apply --intent-to-add expected &&
git diff >actual &&
test_cmp expected actual
'

test_done
10 changes: 6 additions & 4 deletions t/t4011-diff-symlink.sh
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,13 @@ test_expect_success SYMLINKS 'setup symlinks with attributes' '
test_expect_success SYMLINKS 'symlinks do not respect userdiff config by path' '
cat >expect <<-\EOF &&
diff --git a/file.bin b/file.bin
index e69de29..d95f3ad 100644
Binary files a/file.bin and b/file.bin differ
new file mode 100644
index 0000000..d95f3ad
Binary files /dev/null and b/file.bin differ
diff --git a/link.bin b/link.bin
index e69de29..dce41ec 120000
--- a/link.bin
new file mode 120000
index 0000000..dce41ec
--- /dev/null
+++ b/link.bin
@@ -0,0 +1 @@
+file.bin
Expand Down

0 comments on commit ac997db

Please sign in to comment.