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
This commit is contained in:
commit
ac997db0c1
@ -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
|
||||
|
19
apply.c
19
apply.c
@ -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,
|
||||
|
1
apply.h
1
apply.h
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user