From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
To: Jeff King <peff@peff.net>
Cc: "Merlin Büge" <toni@bluenox07.de>, git@vger.kernel.org
Subject: Re: Question about behaviour of git-checkout --patch option
Date: Wed, 27 May 2020 03:49:25 +0200 (CEST) [thread overview]
Message-ID: <nycvar.QRO.7.76.6.2005270348240.56@tvgsbejvaqbjf.bet> (raw)
In-Reply-To: <20200527075648.GA4006373@coredump.intra.peff.net>
[-- Attachment #1: Type: text/plain, Size: 8891 bytes --]
Hi Peff & Merlin,
On Wed, 27 May 2020, Jeff King wrote:
> On Mon, May 25, 2020 at 10:11:00PM +0200, Merlin Büge wrote:
>
> > This works like expected, I end up with a 1:1 copy of the original
> > worktree, including empty files. However, if I include the -p option in
> > the last step:
> >
> > git checkout -p master .
> >
> > ... I correctly get asked for any non-empty files/hunks if I want to
> > apply them - but not for empty ones. It would just display e.g.
> >
> > diff --git b/emptyfile a/emptyfile
> > new file mode 100644
> > index 0000000..e69de29
> >
> > and then skip over it, asking for the next non-empty hunk.
> >
> > Why does it skip over empty hunks?
>
> I think this case was just never anticipated, and it's a bug. The
> original patch-selection code was written for "add -p", and the
> fundamental unit it works on is a hunk. We hacked around that to handle
> deletions back in 24ab81ae4d (add-interactive: handle deletion of empty
> files, 2009-10-27). But "add -p" would never see a new file, since we
> only consider the set of tracked files in the index.
>
> However, when it was extended for "checkout -p", etc, we could see new
> files. I guess the right fix is something along these lines, extending
> the deletion concept to new files:
>
> diff --git a/git-add--interactive.perl b/git-add--interactive.perl
> index c4b75bcb7f..9c8844434e 100755
> --- a/git-add--interactive.perl
> +++ b/git-add--interactive.perl
> @@ -768,7 +768,7 @@ sub parse_diff_header {
> for (my $i = 0; $i < @{$src->{TEXT}}; $i++) {
> my $dest =
> $src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ? $mode :
> - $src->{TEXT}->[$i] =~ /^deleted file/ ? $deletion :
> + $src->{TEXT}->[$i] =~ /^(deleted|new) file/ ? $deletion :
> $head;
> push @{$dest->{TEXT}}, $src->{TEXT}->[$i];
> push @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i];
>
> which does do the right thing. But:
>
> - all of this is in the process of being rewritten in C, so it should
> probably go into add-interactive.c (it's not yet the default to use
> the C version, but it probably will be soon).
>
> - that still says "Apply deletion..." in the UI. We'd need to update
> the messages to differentiate the two cases.
The C version (with "Apply addition"... in the UI) would look somewhat
like this:
-- snipsnap --
diff --git a/add-patch.c b/add-patch.c
index d8bfe379be4d..c0d8bf2df0c5 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -10,7 +10,7 @@
#include "prompt.h"
enum prompt_mode_type {
- PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK,
+ PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_ADDITION, PROMPT_HUNK,
PROMPT_MODE_MAX, /* must be last */
};
@@ -33,6 +33,7 @@ static struct patch_mode patch_mode_add = {
.prompt_mode = {
N_("Stage mode change [y,n,q,a,d%s,?]? "),
N_("Stage deletion [y,n,q,a,d%s,?]? "),
+ N_("Stage addition [y,n,q,a,d%s,?]? "),
N_("Stage this hunk [y,n,q,a,d%s,?]? ")
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@@ -54,6 +55,7 @@ static struct patch_mode patch_mode_stash = {
.prompt_mode = {
N_("Stash mode change [y,n,q,a,d%s,?]? "),
N_("Stash deletion [y,n,q,a,d%s,?]? "),
+ N_("Stash addition [y,n,q,a,d%s,?]? "),
N_("Stash this hunk [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@@ -77,6 +79,7 @@ static struct patch_mode patch_mode_reset_head = {
.prompt_mode = {
N_("Unstage mode change [y,n,q,a,d%s,?]? "),
N_("Unstage deletion [y,n,q,a,d%s,?]? "),
+ N_("Unstage addition [y,n,q,a,d%s,?]? "),
N_("Unstage this hunk [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@@ -99,6 +102,7 @@ static struct patch_mode patch_mode_reset_nothead = {
.prompt_mode = {
N_("Apply mode change to index [y,n,q,a,d%s,?]? "),
N_("Apply deletion to index [y,n,q,a,d%s,?]? "),
+ N_("Apply addition to index [y,n,q,a,d%s,?]? "),
N_("Apply this hunk to index [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@@ -121,6 +125,7 @@ static struct patch_mode patch_mode_checkout_index = {
.prompt_mode = {
N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
+ N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@@ -143,6 +148,7 @@ static struct patch_mode patch_mode_checkout_head = {
.prompt_mode = {
N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
+ N_("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@@ -164,6 +170,7 @@ static struct patch_mode patch_mode_checkout_nothead = {
.prompt_mode = {
N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
+ N_("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@@ -186,6 +193,7 @@ static struct patch_mode patch_mode_worktree_head = {
.prompt_mode = {
N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
+ N_("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@@ -207,6 +215,7 @@ static struct patch_mode patch_mode_worktree_nothead = {
.prompt_mode = {
N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
+ N_("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@@ -248,7 +257,7 @@ struct add_p_state {
struct hunk head;
struct hunk *hunk;
size_t hunk_nr, hunk_alloc;
- unsigned deleted:1, mode_change:1,binary:1;
+ unsigned deleted:1, added:1, mode_change:1,binary:1;
} *file_diff;
size_t file_diff_nr;
@@ -442,7 +451,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
pend = p + plain->len;
while (p != pend) {
char *eol = memchr(p, '\n', pend - p);
- const char *deleted = NULL, *mode_change = NULL;
+ const char *deleted = NULL, *added = NULL, *mode_change = NULL;
if (!eol)
eol = pend;
@@ -465,7 +474,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
; /* keep the rest of the file in a single "hunk" */
else if (starts_with(p, "@@ ") ||
(hunk == &file_diff->head &&
- skip_prefix(p, "deleted file", &deleted))) {
+ (skip_prefix(p, "deleted file", &deleted) ||
+ skip_prefix(p, "new file", &added)))) {
if (marker == '-' || marker == '+')
/*
* Should not happen; previous hunk did not end
@@ -485,6 +495,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
if (deleted)
file_diff->deleted = 1;
+ else if (added)
+ file_diff->added = 1;
else if (parse_hunk_header(s, hunk) < 0)
return -1;
@@ -537,8 +549,10 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
starts_with(p, "Binary files "))
file_diff->binary = 1;
- if (file_diff->deleted && file_diff->mode_change)
- BUG("diff contains delete *and* a mode change?!?\n%.*s",
+ if (!!file_diff->deleted + !!file_diff->added +
+ !!file_diff->mode_change > 1)
+ BUG("diff can only contain delete *or* add *or* a "
+ "mode change?!?\n%.*s",
(int)(eol - (plain->buf + file_diff->head.start)),
plain->buf + file_diff->head.start);
@@ -1392,11 +1406,13 @@ static int patch_update_file(struct add_p_state *s,
if (hunk->splittable_into > 1)
strbuf_addstr(&s->buf, ",s");
if (hunk_index + 1 > file_diff->mode_change &&
- !file_diff->deleted)
+ !file_diff->deleted && !file_diff->added)
strbuf_addstr(&s->buf, ",e");
if (file_diff->deleted)
prompt_mode_type = PROMPT_DELETION;
+ else if (file_diff->added)
+ prompt_mode_type = PROMPT_ADDITION;
else if (file_diff->mode_change && !hunk_index)
prompt_mode_type = PROMPT_MODE_CHANGE;
else
next prev parent reply other threads:[~2020-05-27 18:23 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-05-25 20:11 Question about behaviour of git-checkout --patch option Merlin Büge
2020-05-27 7:56 ` Jeff King
2020-05-27 1:49 ` Johannes Schindelin [this message]
2020-05-27 8:00 ` Jeff King
2020-05-27 10:32 ` Merlin Büge
2020-05-27 4:48 ` Johannes Schindelin
2020-05-27 21:27 ` Merlin Büge
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=nycvar.QRO.7.76.6.2005270348240.56@tvgsbejvaqbjf.bet \
--to=johannes.schindelin@gmx.de \
--cc=git@vger.kernel.org \
--cc=peff@peff.net \
--cc=toni@bluenox07.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).