* Question about behaviour of git-checkout --patch option
@ 2020-05-25 20:11 Merlin Büge
2020-05-27 7:56 ` Jeff King
0 siblings, 1 reply; 7+ messages in thread
From: Merlin Büge @ 2020-05-25 20:11 UTC (permalink / raw)
To: git
Hi all!
I have a question about the behaviour of the git-checkout --patch/-p
option.
I have a small local git repository containing empty and non-empty
files. I wanted to replicate it in another directory and did the
following:
cd <target_dir>
git init
git remote add origin <origin_dir>
git fetch
git branch --track master origin/master
git checkout master .
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?
Thanks!
--
Merlin Büge
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Question about behaviour of git-checkout --patch option
2020-05-27 7:56 ` Jeff King
@ 2020-05-27 1:49 ` Johannes Schindelin
2020-05-27 8:00 ` Jeff King
1 sibling, 0 replies; 7+ messages in thread
From: Johannes Schindelin @ 2020-05-27 1:49 UTC (permalink / raw)
To: Jeff King; +Cc: Merlin Büge, git
[-- 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
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: Question about behaviour of git-checkout --patch option
2020-05-27 10:32 ` Merlin Büge
@ 2020-05-27 4:48 ` Johannes Schindelin
2020-05-27 21:27 ` Merlin Büge
0 siblings, 1 reply; 7+ messages in thread
From: Johannes Schindelin @ 2020-05-27 4:48 UTC (permalink / raw)
To: Merlin Büge; +Cc: Jeff King, git
[-- Attachment #1: Type: text/plain, Size: 1089 bytes --]
Hi Merlin,
On Wed, 27 May 2020, Merlin Büge wrote:
> On Wed, 27 May 2020 04:00:09 -0400
> Jeff King <peff@peff.net> wrote:
>
> > Trying your case with:
> >
> > git -c add.interactive.usebuiltin=true checkout -p master .
> >
> > shows that it does not list the added file at all. I suspect the form of
> > the fix will be similar, but it may have to plumb through the file
> > addition from the diff layer, as well.
>
> Thanks a lot for your explanation! Good to know what's going on there.
>
> Out of curiosity: How does the git community keep track of open bugs?
> On https://git-scm.com/ I read that bug reports should be just send to
> the mailing list. But if they are not fixed within a few days, wouldn't
> they just get lost...?
There is not really any centralized way to keep track of open bugs.
And yes, sometimes they get lost after a few days.
In your case, however, there is now
https://lore.kernel.org/git/pull.646.git.1590613746507.gitgitgadget@gmail.com/T/#u
which is better than just a ticket: it is already a patch.
Ciao,
Johannes
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Question about behaviour of git-checkout --patch option
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
2020-05-27 8:00 ` Jeff King
0 siblings, 2 replies; 7+ messages in thread
From: Jeff King @ 2020-05-27 7:56 UTC (permalink / raw)
To: Merlin Büge; +Cc: git
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.
-Peff
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: Question about behaviour of git-checkout --patch option
2020-05-27 7:56 ` Jeff King
2020-05-27 1:49 ` Johannes Schindelin
@ 2020-05-27 8:00 ` Jeff King
2020-05-27 10:32 ` Merlin Büge
1 sibling, 1 reply; 7+ messages in thread
From: Jeff King @ 2020-05-27 8:00 UTC (permalink / raw)
To: Merlin Büge; +Cc: git
On Wed, May 27, 2020 at 03:56:48AM -0400, Jeff King wrote:
> 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).
Trying your case with:
git -c add.interactive.usebuiltin=true checkout -p master .
shows that it does not list the added file at all. I suspect the form of
the fix will be similar, but it may have to plumb through the file
addition from the diff layer, as well.
-Peff
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Question about behaviour of git-checkout --patch option
2020-05-27 8:00 ` Jeff King
@ 2020-05-27 10:32 ` Merlin Büge
2020-05-27 4:48 ` Johannes Schindelin
0 siblings, 1 reply; 7+ messages in thread
From: Merlin Büge @ 2020-05-27 10:32 UTC (permalink / raw)
To: Jeff King; +Cc: git
On Wed, 27 May 2020 04:00:09 -0400
Jeff King <peff@peff.net> wrote:
> Trying your case with:
>
> git -c add.interactive.usebuiltin=true checkout -p master .
>
> shows that it does not list the added file at all. I suspect the form of
> the fix will be similar, but it may have to plumb through the file
> addition from the diff layer, as well.
Thanks a lot for your explanation! Good to know what's going on there.
Out of curiosity: How does the git community keep track of open bugs?
On https://git-scm.com/ I read that bug reports should be just send to
the mailing list. But if they are not fixed within a few days, wouldn't
they just get lost...?
Thanks.
--
Merlin Büge
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: Question about behaviour of git-checkout --patch option
2020-05-27 4:48 ` Johannes Schindelin
@ 2020-05-27 21:27 ` Merlin Büge
0 siblings, 0 replies; 7+ messages in thread
From: Merlin Büge @ 2020-05-27 21:27 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Jeff King, git
On Wed, 27 May 2020 06:48:28 +0200 (CEST), Johannes Schindelin wrote:
> There is not really any centralized way to keep track of open bugs.
>
> And yes, sometimes they get lost after a few days.
>
> In your case, however, there is now
> https://lore.kernel.org/git/pull.646.git.1590613746507.gitgitgadget@gmail.com/T/#u
> which is better than just a ticket: it is already a patch.
Wow, that was fast!
Thanks a lot!
--
Merlin Büge
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2020-05-27 21:27 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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
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
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.