Commit ac997db0 authored by Junio C Hamano's avatar Junio C Hamano

Merge branch 'nd/diff-apply-ita'

"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
parents a856e7d6 cff5dc09
......@@ -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]
......@@ -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
......
......@@ -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;
......@@ -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);
}
......@@ -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) ||
......@@ -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 */
......@@ -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,
......@@ -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,
......
......@@ -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 */
......
......@@ -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);
......
......@@ -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)
......
......@@ -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' '
......@@ -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
'
......@@ -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
)
'
......@@ -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
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment