{"thread":{"id":"53566","subject":"[PATCH] checkout -p: handle new files correctly","startedAt":"2020-05-27T21:09:13Z","lastAt":"2020-05-28T12:32:04Z","messageCount":5,"participants":["Johannes Schindelin via GitGitGadget","Junio C Hamano","Jeff King","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"398693","messageId":"pull.646.git.1590613746507.gitgitgadget@gmail.com","threadId":"53566","inReplyTo":null,"subject":"[PATCH] checkout -p: handle new files correctly","fromName":"Johannes Schindelin via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-05-27T21:09:06Z","receivedAt":"2020-05-27T21:09:13Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"From: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThe original patch selection code was written for `git add -p`, and the\nfundamental unit on which it works is a hunk.\n\nWe hacked around that to handle deletions back in 24ab81ae4d\n(add-interactive: handle deletion of empty files, 2009-10-27). But `git\nadd -p` would never see a new file, since we only consider the set of\ntracked files in the index.\n\nHowever, since the same machinery was used for `git checkout -p` &\nfriends, we can see new files.\n\nHandle this case specifically, adding a new prompt for it that is\nmodeled after the `deleted file` case.\n\nThis also fixes the problem where added _empty_ files could not be\nstaged via `git checkout -p`.\n\nReported-by: Merlin Büge <toni@bluenox07.de>\nHelped-by: Jeff King <peff@peff.net>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n    Allow empty, new files to be staged via git checkout -p\n    \n    For ease of backporting, this patch is based on js/add-p-leftover-bits.\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-646%2Fdscho%2Fcheckout-p-empty-file-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-646/dscho/checkout-p-empty-file-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/646\n\n add-patch.c                | 30 +++++++++++++++++++++++-------\n git-add--interactive.perl  | 21 +++++++++++++++++++--\n t/t3701-add-interactive.sh | 19 +++++++++++++++++++\n 3 files changed, 61 insertions(+), 9 deletions(-)\n\ndiff --git a/add-patch.c b/add-patch.c\nindex d8dafa8168d..eaace1b824f 100644\n--- a/add-patch.c\n+++ b/add-patch.c\n@@ -9,7 +9,7 @@\n #include \"compat/terminal.h\"\n \n enum prompt_mode_type {\n-\tPROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK,\n+\tPROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_ADDITION, PROMPT_HUNK,\n \tPROMPT_MODE_MAX, /* must be last */\n };\n \n@@ -32,6 +32,7 @@ static struct patch_mode patch_mode_add = {\n \t.prompt_mode = {\n \t\tN_(\"Stage mode change [y,n,q,a,d%s,?]? \"),\n \t\tN_(\"Stage deletion [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Stage addition [y,n,q,a,d%s,?]? \"),\n \t\tN_(\"Stage this hunk [y,n,q,a,d%s,?]? \")\n \t},\n \t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n@@ -53,6 +54,7 @@ static struct patch_mode patch_mode_stash = {\n \t.prompt_mode = {\n \t\tN_(\"Stash mode change [y,n,q,a,d%s,?]? \"),\n \t\tN_(\"Stash deletion [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Stash addition [y,n,q,a,d%s,?]? \"),\n \t\tN_(\"Stash this hunk [y,n,q,a,d%s,?]? \"),\n \t},\n \t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n@@ -76,6 +78,7 @@ static struct patch_mode patch_mode_reset_head = {\n \t.prompt_mode = {\n \t\tN_(\"Unstage mode change [y,n,q,a,d%s,?]? \"),\n \t\tN_(\"Unstage deletion [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Unstage addition [y,n,q,a,d%s,?]? \"),\n \t\tN_(\"Unstage this hunk [y,n,q,a,d%s,?]? \"),\n \t},\n \t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n@@ -98,6 +101,7 @@ static struct patch_mode patch_mode_reset_nothead = {\n \t.prompt_mode = {\n \t\tN_(\"Apply mode change to index [y,n,q,a,d%s,?]? \"),\n \t\tN_(\"Apply deletion to index [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Apply addition to index [y,n,q,a,d%s,?]? \"),\n \t\tN_(\"Apply this hunk to index [y,n,q,a,d%s,?]? \"),\n \t},\n \t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n@@ -120,6 +124,7 @@ static struct patch_mode patch_mode_checkout_index = {\n \t.prompt_mode = {\n \t\tN_(\"Discard mode change from worktree [y,n,q,a,d%s,?]? \"),\n \t\tN_(\"Discard deletion from worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Discard addition from worktree [y,n,q,a,d%s,?]? \"),\n \t\tN_(\"Discard this hunk from worktree [y,n,q,a,d%s,?]? \"),\n \t},\n \t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n@@ -142,6 +147,7 @@ static struct patch_mode patch_mode_checkout_head = {\n \t.prompt_mode = {\n \t\tN_(\"Discard mode change from index and worktree [y,n,q,a,d%s,?]? \"),\n \t\tN_(\"Discard deletion from index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Discard addition from index and worktree [y,n,q,a,d%s,?]? \"),\n \t\tN_(\"Discard this hunk from index and worktree [y,n,q,a,d%s,?]? \"),\n \t},\n \t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n@@ -163,6 +169,7 @@ static struct patch_mode patch_mode_checkout_nothead = {\n \t.prompt_mode = {\n \t\tN_(\"Apply mode change to index and worktree [y,n,q,a,d%s,?]? \"),\n \t\tN_(\"Apply deletion to index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Apply addition to index and worktree [y,n,q,a,d%s,?]? \"),\n \t\tN_(\"Apply this hunk to index and worktree [y,n,q,a,d%s,?]? \"),\n \t},\n \t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n@@ -185,6 +192,7 @@ static struct patch_mode patch_mode_worktree_head = {\n \t.prompt_mode = {\n \t\tN_(\"Discard mode change from index and worktree [y,n,q,a,d%s,?]? \"),\n \t\tN_(\"Discard deletion from index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Discard addition from index and worktree [y,n,q,a,d%s,?]? \"),\n \t\tN_(\"Discard this hunk from index and worktree [y,n,q,a,d%s,?]? \"),\n \t},\n \t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n@@ -206,6 +214,7 @@ static struct patch_mode patch_mode_worktree_nothead = {\n \t.prompt_mode = {\n \t\tN_(\"Apply mode change to index and worktree [y,n,q,a,d%s,?]? \"),\n \t\tN_(\"Apply deletion to index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\tN_(\"Apply addition to index and worktree [y,n,q,a,d%s,?]? \"),\n \t\tN_(\"Apply this hunk to index and worktree [y,n,q,a,d%s,?]? \"),\n \t},\n \t.edit_hunk_hint = N_(\"If the patch applies cleanly, the edited hunk \"\n@@ -247,7 +256,7 @@ struct add_p_state {\n \t\tstruct hunk head;\n \t\tstruct hunk *hunk;\n \t\tsize_t hunk_nr, hunk_alloc;\n-\t\tunsigned deleted:1, mode_change:1,binary:1;\n+\t\tunsigned deleted:1, added:1, mode_change:1,binary:1;\n \t} *file_diff;\n \tsize_t file_diff_nr;\n \n@@ -441,7 +450,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)\n \tpend = p + plain->len;\n \twhile (p != pend) {\n \t\tchar *eol = memchr(p, '\\n', pend - p);\n-\t\tconst char *deleted = NULL, *mode_change = NULL;\n+\t\tconst char *deleted = NULL, *added = NULL, *mode_change = NULL;\n \n \t\tif (!eol)\n \t\t\teol = pend;\n@@ -460,11 +469,12 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)\n \t\t} else if (p == plain->buf)\n \t\t\tBUG(\"diff starts with unexpected line:\\n\"\n \t\t\t    \"%.*s\\n\", (int)(eol - p), p);\n-\t\telse if (file_diff->deleted)\n+\t\telse if (file_diff->deleted || file_diff->added)\n \t\t\t; /* keep the rest of the file in a single \"hunk\" */\n \t\telse if (starts_with(p, \"@@ \") ||\n \t\t\t (hunk == &file_diff->head &&\n-\t\t\t  skip_prefix(p, \"deleted file\", &deleted))) {\n+\t\t\t  (skip_prefix(p, \"deleted file\", &deleted) ||\n+\t\t\t   skip_prefix(p, \"new file\", &added)))) {\n \t\t\tif (marker == '-' || marker == '+')\n \t\t\t\t/*\n \t\t\t\t * Should not happen; previous hunk did not end\n@@ -484,6 +494,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)\n \n \t\t\tif (deleted)\n \t\t\t\tfile_diff->deleted = 1;\n+\t\t\telse if (added)\n+\t\t\t\tfile_diff->added = 1;\n \t\t\telse if (parse_hunk_header(s, hunk) < 0)\n \t\t\t\treturn -1;\n \n@@ -536,8 +548,10 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)\n \t\t\t   starts_with(p, \"Binary files \"))\n \t\t\tfile_diff->binary = 1;\n \n-\t\tif (file_diff->deleted && file_diff->mode_change)\n-\t\t\tBUG(\"diff contains delete *and* a mode change?!?\\n%.*s\",\n+\t\tif (!!file_diff->deleted + !!file_diff->added +\n+\t\t    !!file_diff->mode_change > 1)\n+\t\t\tBUG(\"diff can only contain delete *or* add *or* a \"\n+\t\t\t    \"mode change?!?\\n%.*s\",\n \t\t\t    (int)(eol - (plain->buf + file_diff->head.start)),\n \t\t\t    plain->buf + file_diff->head.start);\n \n@@ -1397,6 +1411,8 @@ static int patch_update_file(struct add_p_state *s,\n \n \t\tif (file_diff->deleted)\n \t\t\tprompt_mode_type = PROMPT_DELETION;\n+\t\telse if (file_diff->added)\n+\t\t\tprompt_mode_type = PROMPT_ADDITION;\n \t\telse if (file_diff->mode_change && !hunk_index)\n \t\t\tprompt_mode_type = PROMPT_MODE_CHANGE;\n \t\telse\ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nindex 10fd30ae16a..f36c0078ac9 100755\n--- a/git-add--interactive.perl\n+++ b/git-add--interactive.perl\n@@ -754,16 +754,18 @@ sub parse_diff_header {\n \tmy $head = { TEXT => [], DISPLAY => [], TYPE => 'header' };\n \tmy $mode = { TEXT => [], DISPLAY => [], TYPE => 'mode' };\n \tmy $deletion = { TEXT => [], DISPLAY => [], TYPE => 'deletion' };\n+\tmy $addition = { TEXT => [], DISPLAY => [], TYPE => 'addition' };\n \n \tfor (my $i = 0; $i < @{$src->{TEXT}}; $i++) {\n \t\tmy $dest =\n \t\t   $src->{TEXT}->[$i] =~ /^(old|new) mode (\\d+)$/ ? $mode :\n \t\t   $src->{TEXT}->[$i] =~ /^deleted file/ ? $deletion :\n+\t\t   $src->{TEXT}->[$i] =~ /^new file/ ? $addition :\n \t\t   $head;\n \t\tpush @{$dest->{TEXT}}, $src->{TEXT}->[$i];\n \t\tpush @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i];\n \t}\n-\treturn ($head, $mode, $deletion);\n+\treturn ($head, $mode, $deletion, $addition);\n }\n \n sub hunk_splittable {\n@@ -1427,46 +1429,55 @@ sub display_hunks {\n \tstage => {\n \t\tmode => N__(\"Stage mode change [y,n,q,a,d%s,?]? \"),\n \t\tdeletion => N__(\"Stage deletion [y,n,q,a,d%s,?]? \"),\n+\t\taddition => N__(\"Stage addition [y,n,q,a,d%s,?]? \"),\n \t\thunk => N__(\"Stage this hunk [y,n,q,a,d%s,?]? \"),\n \t},\n \tstash => {\n \t\tmode => N__(\"Stash mode change [y,n,q,a,d%s,?]? \"),\n \t\tdeletion => N__(\"Stash deletion [y,n,q,a,d%s,?]? \"),\n+\t\taddition => N__(\"Stash addition [y,n,q,a,d%s,?]? \"),\n \t\thunk => N__(\"Stash this hunk [y,n,q,a,d%s,?]? \"),\n \t},\n \treset_head => {\n \t\tmode => N__(\"Unstage mode change [y,n,q,a,d%s,?]? \"),\n \t\tdeletion => N__(\"Unstage deletion [y,n,q,a,d%s,?]? \"),\n+\t\taddition => N__(\"Unstage addition [y,n,q,a,d%s,?]? \"),\n \t\thunk => N__(\"Unstage this hunk [y,n,q,a,d%s,?]? \"),\n \t},\n \treset_nothead => {\n \t\tmode => N__(\"Apply mode change to index [y,n,q,a,d%s,?]? \"),\n \t\tdeletion => N__(\"Apply deletion to index [y,n,q,a,d%s,?]? \"),\n+\t\taddition => N__(\"Apply addition to index [y,n,q,a,d%s,?]? \"),\n \t\thunk => N__(\"Apply this hunk to index [y,n,q,a,d%s,?]? \"),\n \t},\n \tcheckout_index => {\n \t\tmode => N__(\"Discard mode change from worktree [y,n,q,a,d%s,?]? \"),\n \t\tdeletion => N__(\"Discard deletion from worktree [y,n,q,a,d%s,?]? \"),\n+\t\taddition => N__(\"Discard addition from worktree [y,n,q,a,d%s,?]? \"),\n \t\thunk => N__(\"Discard this hunk from worktree [y,n,q,a,d%s,?]? \"),\n \t},\n \tcheckout_head => {\n \t\tmode => N__(\"Discard mode change from index and worktree [y,n,q,a,d%s,?]? \"),\n \t\tdeletion => N__(\"Discard deletion from index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\taddition => N__(\"Discard addition from index and worktree [y,n,q,a,d%s,?]? \"),\n \t\thunk => N__(\"Discard this hunk from index and worktree [y,n,q,a,d%s,?]? \"),\n \t},\n \tcheckout_nothead => {\n \t\tmode => N__(\"Apply mode change to index and worktree [y,n,q,a,d%s,?]? \"),\n \t\tdeletion => N__(\"Apply deletion to index and worktree [y,n,q,a,d%s,?]? \"),\n+\t\taddition => N__(\"Apply addition to index and worktree [y,n,q,a,d%s,?]? \"),\n \t\thunk => N__(\"Apply this hunk to index and worktree [y,n,q,a,d%s,?]? \"),\n \t},\n \tworktree_head => {\n \t\tmode => N__(\"Discard mode change from worktree [y,n,q,a,d%s,?]? \"),\n \t\tdeletion => N__(\"Discard deletion from worktree [y,n,q,a,d%s,?]? \"),\n+\t\taddition => N__(\"Discard addition from worktree [y,n,q,a,d%s,?]? \"),\n \t\thunk => N__(\"Discard this hunk from worktree [y,n,q,a,d%s,?]? \"),\n \t},\n \tworktree_nothead => {\n \t\tmode => N__(\"Apply mode change to worktree [y,n,q,a,d%s,?]? \"),\n \t\tdeletion => N__(\"Apply deletion to worktree [y,n,q,a,d%s,?]? \"),\n+\t\taddition => N__(\"Apply addition to worktree [y,n,q,a,d%s,?]? \"),\n \t\thunk => N__(\"Apply this hunk to worktree [y,n,q,a,d%s,?]? \"),\n \t},\n );\n@@ -1476,7 +1487,7 @@ sub patch_update_file {\n \tmy ($ix, $num);\n \tmy $path = shift;\n \tmy ($head, @hunk) = parse_diff($path);\n-\t($head, my $mode, my $deletion) = parse_diff_header($head);\n+\t($head, my $mode, my $deletion, my $addition) = parse_diff_header($head);\n \tfor (@{$head->{DISPLAY}}) {\n \t\tprint;\n \t}\n@@ -1490,6 +1501,12 @@ sub patch_update_file {\n \t\t\tpush @{$deletion->{DISPLAY}}, @{$hunk->{DISPLAY}};\n \t\t}\n \t\t@hunk = ($deletion);\n+\t} elsif (@{$addition->{TEXT}}) {\n+\t\tforeach my $hunk (@hunk) {\n+\t\t\tpush @{$addition->{TEXT}}, @{$hunk->{TEXT}};\n+\t\t\tpush @{$addition->{DISPLAY}}, @{$hunk->{DISPLAY}};\n+\t\t}\n+\t\t@hunk = ($addition);\n \t}\n \n \t$num = scalar @hunk;\ndiff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh\nindex ac43f835a5b..7bc0c3fe6ef 100755\n--- a/t/t3701-add-interactive.sh\n+++ b/t/t3701-add-interactive.sh\n@@ -403,6 +403,25 @@ test_expect_success 'deleting an empty file' '\n \tdiff_cmp expected diff\n '\n \n+test_expect_success 'adding an empty file' '\n+\tgit init added &&\n+\t(\n+\t\tcd added &&\n+\t\ttest_commit initial &&\n+\t\t>empty &&\n+\t\tgit add empty &&\n+\t\ttest_tick &&\n+\t\tgit commit -m empty &&\n+\t\tgit tag added-file &&\n+\t\tgit reset --hard HEAD^ &&\n+\t\ttest_path_is_missing empty &&\n+\n+\t\techo y | git checkout -p added-file -- >actual &&\n+\t\ttest_path_is_file empty &&\n+\t\ttest_i18ngrep \"Apply addition to index and worktree\" actual\n+\t)\n+'\n+\n test_expect_success 'split hunk setup' '\n \tgit reset --hard &&\n \ttest_write_lines 10 20 30 40 50 60 >test &&\n\nbase-commit: b2627cc3d4be2f8086711097f99d79a32c6a703a\n-- \ngitgitgadget\n"},{"id":"398699","messageId":"xmqqwo4xjbcq.fsf@gitster.c.googlers.com","threadId":"53566","inReplyTo":"pull.646.git.1590613746507.gitgitgadget@gmail.com","subject":"Re: [PATCH] checkout -p: handle new files correctly","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-05-27T21:51:01Z","receivedAt":"2020-05-27T21:51:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Johannes Schindelin via GitGitGadget\" <gitgitgadget@gmail.com>\nwrites:\n\n> This also fixes the problem where added _empty_ files could not be\n> staged via `git checkout -p`.\n\nNice.  Will queue.\n\n>     For ease of backporting, this patch is based on js/add-p-leftover-bits.\n\nYup, I agree that it would be a sensible fork point.\n"},{"id":"398706","messageId":"20200527230357.GB546534@coredump.intra.peff.net","threadId":"53566","inReplyTo":"pull.646.git.1590613746507.gitgitgadget@gmail.com","subject":"Re: [PATCH] checkout -p: handle new files correctly","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2020-05-27T23:03:57Z","receivedAt":"2020-05-27T23:03:59Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, May 27, 2020 at 09:09:06PM +0000, Johannes Schindelin via GitGitGadget wrote:\n\n> However, since the same machinery was used for `git checkout -p` &\n> friends, we can see new files.\n> \n> Handle this case specifically, adding a new prompt for it that is\n> modeled after the `deleted file` case.\n\nThanks! I was planning to dig further into this topic today, and here it\nis all wrapped up with a bow. :)\n\n>  add-patch.c                | 30 +++++++++++++++++++++++-------\n>  git-add--interactive.perl  | 21 +++++++++++++++++++--\n\nOoh, you even fixed the perl version, too. I was just going to leave it\nin the dust and add a test that set GIT_TEST_ADD_I_USE_BUILTIN.\n\nBoth versions look good, and are similar to what I expected from looking\nat it last night.\n\n> The original patch selection code was written for `git add -p`, and the\n> fundamental unit on which it works is a hunk.\n> \n> We hacked around that to handle deletions back in 24ab81ae4d\n> (add-interactive: handle deletion of empty files, 2009-10-27). But `git\n> add -p` would never see a new file, since we only consider the set of\n> tracked files in the index.\n\nI lied a little with \"would never see a new file\". There _is_ a related\ncase with \"add -p\" that might be worth thinking about: intent-to-add\nfiles.\n\n  $ git init\n  $ >empty\n  $ echo content >not-empty\n  $ git add -N .\n  $ git add -p\n  diff --git a/not-empty b/not-empty\n  index e69de29..d95f3ad 100644\n  --- a/not-empty\n  +++ b/not-empty\n  @@ -0,0 +1 @@\n  +content\n  (1/1) Stage this hunk [y,n,q,a,d,e,?]? n\n\n  [no mention of empty file!]\n\nI think the culprit here is diff-files, though, which doesn't show a\npatch for intent-to-add:\n\n  $ git diff-files\n  :100644 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0000000000000000000000000000000000000000 M\tempty\n  :100644 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0000000000000000000000000000000000000000 M\tnot-empty\n\n  $ git diff-files -p\n  diff --git a/not-empty b/not-empty\n  index e69de29..d95f3ad 100644\n  --- a/not-empty\n  +++ b/not-empty\n  @@ -0,0 +1 @@\n  +content\n\nI don't think this really intersects with the patch here at all, because\ndiff-files is not producing \"new file\" lines for these entries (even for\nthe non-empty one).\n\nThe solution _might_ be to convince diff-files to treat i-t-a entries as\ncreations. And then with your patch here, we'd naturally do the right\nthing. So I don't think this needs to hold up your patch in any way, nor\ndo we necessarily need to deal with i-t-a now. I was mostly curious how\nthey worked, since we don't support added files. The answer is just that\nthey don't always. ;)\n\n-Peff\n"},{"id":"398728","messageId":"nycvar.QRO.7.76.6.2005272150220.56@tvgsbejvaqbjf.bet","threadId":"53566","inReplyTo":"20200527230357.GB546534@coredump.intra.peff.net","subject":"Re: [PATCH] checkout -p: handle new files correctly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2020-05-27T19:51:31Z","receivedAt":"2020-05-28T12:25:37Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Peff,\n\nOn Wed, 27 May 2020, Jeff King wrote:\n\n> On Wed, May 27, 2020 at 09:09:06PM +0000, Johannes Schindelin via GitGitGadget wrote:\n>\n> > However, since the same machinery was used for `git checkout -p` &\n> > friends, we can see new files.\n> >\n> > Handle this case specifically, adding a new prompt for it that is\n> > modeled after the `deleted file` case.\n>\n> Thanks! I was planning to dig further into this topic today, and here it\n> is all wrapped up with a bow. :)\n\n:-)\n\n>\n> >  add-patch.c                | 30 +++++++++++++++++++++++-------\n> >  git-add--interactive.perl  | 21 +++++++++++++++++++--\n>\n> Ooh, you even fixed the perl version, too. I was just going to leave it\n> in the dust and add a test that set GIT_TEST_ADD_I_USE_BUILTIN.\n\nAs long as there is an escape hatch, I try to keep it working.\n\n> Both versions look good, and are similar to what I expected from looking\n> at it last night.\n\nThank you!\n\n> > The original patch selection code was written for `git add -p`, and the\n> > fundamental unit on which it works is a hunk.\n> >\n> > We hacked around that to handle deletions back in 24ab81ae4d\n> > (add-interactive: handle deletion of empty files, 2009-10-27). But `git\n> > add -p` would never see a new file, since we only consider the set of\n> > tracked files in the index.\n>\n> I lied a little with \"would never see a new file\". There _is_ a related\n> case with \"add -p\" that might be worth thinking about: intent-to-add\n> files.\n\nIndeed. Maybe I can leave that as #leftoverbits?\n\nCiao,\nDscho\n\n>\n>   $ git init\n>   $ >empty\n>   $ echo content >not-empty\n>   $ git add -N .\n>   $ git add -p\n>   diff --git a/not-empty b/not-empty\n>   index e69de29..d95f3ad 100644\n>   --- a/not-empty\n>   +++ b/not-empty\n>   @@ -0,0 +1 @@\n>   +content\n>   (1/1) Stage this hunk [y,n,q,a,d,e,?]? n\n>\n>   [no mention of empty file!]\n>\n> I think the culprit here is diff-files, though, which doesn't show a\n> patch for intent-to-add:\n>\n>   $ git diff-files\n>   :100644 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0000000000000000000000000000000000000000 M\tempty\n>   :100644 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0000000000000000000000000000000000000000 M\tnot-empty\n>\n>   $ git diff-files -p\n>   diff --git a/not-empty b/not-empty\n>   index e69de29..d95f3ad 100644\n>   --- a/not-empty\n>   +++ b/not-empty\n>   @@ -0,0 +1 @@\n>   +content\n>\n> I don't think this really intersects with the patch here at all, because\n> diff-files is not producing \"new file\" lines for these entries (even for\n> the non-empty one).\n>\n> The solution _might_ be to convince diff-files to treat i-t-a entries as\n> creations. And then with your patch here, we'd naturally do the right\n> thing. So I don't think this needs to hold up your patch in any way, nor\n> do we necessarily need to deal with i-t-a now. I was mostly curious how\n> they worked, since we don't support added files. The answer is just that\n> they don't always. ;)\n>\n> -Peff\n>\n"},{"id":"398729","messageId":"nycvar.QRO.7.76.6.2005272157120.56@tvgsbejvaqbjf.bet","threadId":"53566","inReplyTo":"nycvar.QRO.7.76.6.2005272150220.56@tvgsbejvaqbjf.bet","subject":"Re: [PATCH] checkout -p: handle new files correctly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2020-05-27T19:58:04Z","receivedAt":"2020-05-28T12:32:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 27 May 2020, Johannes Schindelin wrote:\n\n> On Wed, 27 May 2020, Jeff King wrote:\n>\n> > I lied a little with \"would never see a new file\". There _is_ a\n> > related case with \"add -p\" that might be worth thinking about:\n> > intent-to-add files.\n>\n> Indeed. Maybe I can leave that as #leftoverbits?\n\nAdded as https://github.com/gitgitgadget/git/issues/647\n\nCiao,\nDscho\n"}]}