apply: add --intent-to-add

Similar to 'git reset -N', this option makes 'git apply' automatically
mark new files as intent-to-add so they are visible in the following
'git diff' command and could also be committed with 'git commit -a'.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Nguyễn Thái Ngọc Duy 2018-05-26 14:08:46 +02:00 committed by Junio C Hamano
parent 8fc8f05cef
commit cff5dc09ed
4 changed files with 35 additions and 8 deletions

View File

@ -9,7 +9,7 @@ git-apply - Apply a patch to files and/or to the index
SYNOPSIS SYNOPSIS
-------- --------
[verse] [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] [--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]
[--allow-binary-replacement | --binary] [--reject] [-z] [--allow-binary-replacement | --binary] [--reject] [-z]
[-p<n>] [-C<n>] [--inaccurate-eof] [--recount] [--cached] [-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 cached data, apply the patch, and store the result in the index
without using the working tree. This implies `--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:: -3::
--3way:: --3way::
When the patch does not apply cleanly, fall back on 3-way merge if When the patch does not apply cleanly, fall back on 3-way merge if

19
apply.c
View File

@ -141,6 +141,8 @@ int check_apply_state(struct apply_state *state, int force_apply)
return error(_("--cached outside a repository")); return error(_("--cached outside a repository"));
state->check_index = 1; state->check_index = 1;
} }
if (state->ita_only && (state->check_index || is_not_gitdir))
state->ita_only = 0;
if (state->check_index) if (state->check_index)
state->unsafe_paths = 0; 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) 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) if (remove_file_from_cache(patch->old_name) < 0)
return error(_("unable to remove %s from index"), patch->old_name); 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); int namelen = strlen(path);
unsigned ce_size = cache_entry_size(namelen); unsigned ce_size = cache_entry_size(namelen);
if (!state->update_index)
return 0;
ce = xcalloc(1, ce_size); ce = xcalloc(1, ce_size);
memcpy(ce->name, path, namelen); memcpy(ce->name, path, namelen);
ce->ce_mode = create_ce_mode(mode); ce->ce_mode = create_ce_mode(mode);
ce->ce_flags = create_ce_flags(0); ce->ce_flags = create_ce_flags(0);
ce->ce_namelen = namelen; 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; const char *s;
if (!skip_prefix(buf, "Subproject commit ", &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) if (patch->conflicted_threeway)
return add_conflicted_stages_file(state, patch); return add_conflicted_stages_file(state, patch);
else else if (state->update_index)
return add_index_file(state, path, mode, buf, size); return add_index_file(state, path, mode, buf, size);
return 0;
} }
/* phase zero is to remove, phase one is to create */ /* 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)) if (state->whitespace_error && (state->ws_error_action == die_on_ws_error))
state->apply = 0; 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->update_index && !is_lock_file_locked(&state->lock_file)) {
if (state->index_file) if (state->index_file)
hold_lock_file_for_update(&state->lock_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")), N_("instead of applying the patch, see if the patch is applicable")),
OPT_BOOL(0, "index", &state->check_index, OPT_BOOL(0, "index", &state->check_index,
N_("make sure the patch is applicable to the current 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, OPT_BOOL(0, "cached", &state->cached,
N_("apply a patch without touching the working tree")), N_("apply a patch without touching the working tree")),
OPT_BOOL_F(0, "unsafe-paths", &state->unsafe_paths, OPT_BOOL_F(0, "unsafe-paths", &state->unsafe_paths,

View File

@ -45,6 +45,7 @@ struct apply_state {
int check; /* preimage must match working tree, don't actually apply */ int check; /* preimage must match working tree, don't actually apply */
int check_index; /* preimage must match the indexed version */ int check_index; /* preimage must match the indexed version */
int update_index; /* check_index && apply */ int update_index; /* check_index && apply */
int ita_only; /* add intent-to-add entries to the index */
/* These control cosmetic aspect of the output */ /* These control cosmetic aspect of the output */
int diffstat; /* just show a diffstat, and don't actually apply */ int diffstat; /* just show a diffstat, and don't actually apply */

View File

@ -245,6 +245,7 @@ test_expect_success 'diff-files/diff-cached shows ita as new/not-new files' '
test_cmp expected2 actual2 test_cmp expected2 actual2
' '
test_expect_success '"diff HEAD" includes ita as new files' ' test_expect_success '"diff HEAD" includes ita as new files' '
git reset --hard && git reset --hard &&
echo new >new-ita && echo new >new-ita &&
@ -262,4 +263,16 @@ test_expect_success '"diff HEAD" includes ita as new files' '
test_cmp expected actual 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 test_done