threads / discuss / 50180

git rebase: retain original head?

Subject: git rebase: retain original head?

## tl;dr

11 messages between Jan 8, 2019 and Feb 28, 2019.

replies: 10people: 5as markdown or json

Markus Wiederkehr· Jan 8, 2019, 15:06 UTC · lore
Hello,

I frequently run an interactive rebase to change the order of recent commits, apply fixups, etc. When merge conflicts occur I often want to compare the result with the original head prior to starting the rebase.

In the past I used "git diff ORIG_HEAD". This used to work as long as I did not manually invoke "git reset" during the operation.

Since git version 2.20.1 (not sure, maybe 2.20.0?) this no longer works because ORIG_HEAD seems to get modified by fixup commits during rebase. Now I have to search through reflog to find the commit I want to compare the current head to.

During the rebase operation the original head seems to get stored in 'rebase-merge/orig-head'. Unfortunately this references gets removed after the rebase operation completes.

Would it be possible to retain this information? Could you set ORIG_HEAD back to rebase-merge/orig-head after rebase completes? Alternatively, could something like REBASE_HEAD be added for this purpose?

Undoing a rebase would be related to this and is a very popular question on SO [1]. Users recommend using "git reset --hard ORIG_HEAD" which now no longer works.

Regards, Markus

[1] https://stackoverflow.com/questions/134882
Andreas Schwab· Jan 8, 2019, 17:43 UTC · re: Markus Wiederkehr · lore

Re: git rebase: retain original head?

On Jan 08 2019, Markus Wiederkehr <markus.wiederkehr@gmail.com> wrote:
Show 5 quoted lines
> During the rebase operation the original head seems to get stored in
> 'rebase-merge/orig-head'. Unfortunately this references gets removed
> after the rebase operation completes.
>
> Would it be possible to retain this information?

You could use the reflog of the current branch, where it is the second entry.

Andreas.
-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 7578 EB47 D4E5 4D69 2510  2552 DF73 E780 A9DA AEC1
"And now for something completely different."
Markus Wiederkehr· Jan 8, 2019, 20:38 UTC · re: Andreas Schwab · lore

Re: git rebase: retain original head?

On Tue, Jan 8, 2019 at 6:43 PM Andreas Schwab <schwab@linux-m68k.org> wrote:
Show 7 quoted lines
>
> On Jan 08 2019, Markus Wiederkehr <markus.wiederkehr@gmail.com> wrote:
>
> > Would it be possible to retain this information?
>
> You could use the reflog of the current branch, where it is the second
> entry.

It is not, depending on what happens in the rebase it could be any entry. That's why I always have to search for the right one, which is tedious.

Example:

$ git rebase -i root-tag ... (apply one fixup) $ git reflog 906caf1c (HEAD -> master) HEAD@{0}: rebase -i (finish): returning to refs/heads/master 4906caf1c (HEAD -> master) HEAD@{1}: rebase -i (pick): qux 85dab37b4 HEAD@{2}: rebase -i (pick): baz 7de7420d2 HEAD@{3}: rebase -i (fixup): bar 9bc0461c0 HEAD@{4}: rebase -i (start): checkout root-tag a150b73ca HEAD@{5}: commit: foo

Here I have to use HEAD@{5}.
Markus
On Tue, Jan 8, 2019 at 6:43 PM Andreas Schwab <schwab@linux-m68k.org> wrote:
Show 18 quoted lines
>
> On Jan 08 2019, Markus Wiederkehr <markus.wiederkehr@gmail.com> wrote:
>
> > During the rebase operation the original head seems to get stored in
> > 'rebase-merge/orig-head'. Unfortunately this references gets removed
> > after the rebase operation completes.
> >
> > Would it be possible to retain this information?
>
> You could use the reflog of the current branch, where it is the second
> entry.
>
> Andreas.
>
> --
> Andreas Schwab, schwab@linux-m68k.org
> GPG Key fingerprint = 7578 EB47 D4E5 4D69 2510  2552 DF73 E780 A9DA AEC1
> "And now for something completely different."
Andreas Schwab· Jan 8, 2019, 20:51 UTC · re: Markus Wiederkehr · lore

Re: git rebase: retain original head?

On Jan 08 2019, Markus Wiederkehr <markus.wiederkehr@gmail.com> wrote:
Show 11 quoted lines
> On Tue, Jan 8, 2019 at 6:43 PM Andreas Schwab <schwab@linux-m68k.org> wrote:
>>
>> On Jan 08 2019, Markus Wiederkehr <markus.wiederkehr@gmail.com> wrote:
>>
>> > Would it be possible to retain this information?
>>
>> You could use the reflog of the current branch, where it is the second
>> entry.
>
> It is not, depending on what happens in the rebase it could be any
> entry.
Don't look at the HEAD reflog, use the branch reflog, ie. @{1}.
Andreas.
-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 7578 EB47 D4E5 4D69 2510  2552 DF73 E780 A9DA AEC1
"And now for something completely different."
Markus Wiederkehr· Jan 9, 2019, 13:48 UTC · re: Andreas Schwab · lore

Re: git rebase: retain original head?

On Tue, Jan 8, 2019 at 10:29 PM Andreas Schwab <schwab@linux-m68k.org> wrote:
Show 7 quoted lines
>
> On Jan 08 2019, Markus Wiederkehr <markus.wiederkehr@gmail.com> wrote:
>
> > It is not, depending on what happens in the rebase it could be any
> > entry.
>
> Don't look at the HEAD reflog, use the branch reflog, ie. @{1}.
That's exactly what I was looking for, thanks!
Jacob Keller· Jan 8, 2019, 21:22 UTC · re: Markus Wiederkehr · lore

Re: git rebase: retain original head?

On Tue, Jan 8, 2019 at 12:47 PM Markus Wiederkehr <markus.wiederkehr@gmail.com> wrote:

Show 29 quoted lines
>
> On Tue, Jan 8, 2019 at 6:43 PM Andreas Schwab <schwab@linux-m68k.org> wrote:
> >
> > On Jan 08 2019, Markus Wiederkehr <markus.wiederkehr@gmail.com> wrote:
> >
> > > Would it be possible to retain this information?
> >
> > You could use the reflog of the current branch, where it is the second
> > entry.
>
> It is not, depending on what happens in the rebase it could be any
> entry. That's why I always have to search for the right one, which is
> tedious.
>
> Example:
>
> $ git rebase -i root-tag
> ... (apply one fixup)
> $ git reflog
> 906caf1c (HEAD -> master) HEAD@{0}: rebase -i (finish): returning to
> refs/heads/master
> 4906caf1c (HEAD -> master) HEAD@{1}: rebase -i (pick): qux
> 85dab37b4 HEAD@{2}: rebase -i (pick): baz
> 7de7420d2 HEAD@{3}: rebase -i (fixup): bar
> 9bc0461c0 HEAD@{4}: rebase -i (start): checkout root-tag
> a150b73ca HEAD@{5}: commit: foo
>
> Here I have to use HEAD@{5}.
>
He meant using:
$ git reflog master

which only shows changes directly to the master ref, not to HEAD. During a git rebase operations don't modify the master ref until after the rebase finishes. This, of course, assumes you're on a branch.

If so, you should even be able to use master@{1} and be safe in assuming it always refers to what the branch was before the rebase started.

Thanks, Jake

Johannes Schindelin· Jan 9, 2019, 14:05 UTC · re: Jacob Keller · lore

Re: git rebase: retain original head?

Hi Jake,
On Tue, 8 Jan 2019, Jacob Keller wrote:
Show 35 quoted lines
> On Tue, Jan 8, 2019 at 12:47 PM Markus Wiederkehr
> <markus.wiederkehr@gmail.com> wrote:
> >
> > On Tue, Jan 8, 2019 at 6:43 PM Andreas Schwab <schwab@linux-m68k.org> wrote:
> > >
> > > On Jan 08 2019, Markus Wiederkehr <markus.wiederkehr@gmail.com> wrote:
> > >
> > > > Would it be possible to retain this information?
> > >
> > > You could use the reflog of the current branch, where it is the second
> > > entry.
> >
> > It is not, depending on what happens in the rebase it could be any
> > entry. That's why I always have to search for the right one, which is
> > tedious.
> >
> > Example:
> >
> > $ git rebase -i root-tag
> > ... (apply one fixup)
> > $ git reflog
> > 906caf1c (HEAD -> master) HEAD@{0}: rebase -i (finish): returning to
> > refs/heads/master
> > 4906caf1c (HEAD -> master) HEAD@{1}: rebase -i (pick): qux
> > 85dab37b4 HEAD@{2}: rebase -i (pick): baz
> > 7de7420d2 HEAD@{3}: rebase -i (fixup): bar
> > 9bc0461c0 HEAD@{4}: rebase -i (start): checkout root-tag
> > a150b73ca HEAD@{5}: commit: foo
> >
> > Here I have to use HEAD@{5}.
> >
> 
> He meant using:
> 
> $ git reflog master

Indeed. That's why terse answers are often worse than no answers at all: more often than not, they leave things unclearer than before, in the worst case they are misleading, as was the very terse first reply. Just don't. Too short is not beautiful.

Show 7 quoted lines
> which only shows changes directly to the master ref, not to HEAD.
> During a git rebase operations don't modify the master ref until after
> the rebase finishes. This, of course, assumes you're on a branch.
> 
> If so, you should even be able to use master@{1} and be safe in
> assuming it always refers to what the branch was before the rebase
> started.

There are more tricks up Git's sleeves: @{1} is a shorthand for the previous state of the current branch (if any). So after you rebase a branch, @{1} will refer to what previously was ORIG_HEAD.

Having said that, it is an unintended regression in the built-in rebase. Markus, could you come up with a minimal test case, preferably in the form of a patch to t/t3415-rebase-autosquash.sh?

Thanks, Johannes

Markus Wiederkehr· Jan 9, 2019, 15:08 UTC · re: Johannes Schindelin · lore

Re: git rebase: retain original head?

On Wed, Jan 9, 2019 at 3:05 PM Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:

>
> Having said that, it is an unintended regression in the built-in rebase.
> Markus, could you come up with a minimal test case, preferably in the form
> of a patch to t/t3415-rebase-autosquash.sh?

I don't think I'm familiar enough with the test code to be able to provide a good patch but the following code should illustrate the regression.

git init testrepo cd testrepo echo 1 > file && git add file && git commit -m "initial" echo 1 >> file && git commit -am "commit 1" # rev_commit_1=$(git rev-parse HEAD) echo 1 >> file && git commit -am "fixup! $(git rev-parse --short HEAD)" echo 1 >> file && git commit -am "commit 3" rev_orig_head=$(git rev-parse HEAD) GIT_EDITOR=: git rebase --autosquash -i HEAD~3 test $(git rev-parse ORIG_HEAD) = $rev_orig_head

In older versions of git this test succeeded, i.e. ORIG_HEAD pointed to the previous original head, $rev_orig_head. In git version 2.20.1 ORIG_HEAD now points to the commit that got fixuped instead, $rev_commit_1.

In previous versions ORIG_HEAD only pointed somewhere else if "git reset" was invoked manually during the rebase operation. I'm not sure if this is desirable, maybe ORIG_HEAD should always point to the previous head after rebase completes, no matter what operations were run in between. What do you think?

Markus
Johannes Schindelin· Jan 11, 2019, 10:42 UTC · re: Markus Wiederkehr · lore

Re: git rebase: retain original head?

Hi Markus,
On Wed, 9 Jan 2019, Markus Wiederkehr wrote:
Show 10 quoted lines
> On Wed, Jan 9, 2019 at 3:05 PM Johannes Schindelin
> <Johannes.Schindelin@gmx.de> wrote:
> >
> > Having said that, it is an unintended regression in the built-in rebase.
> > Markus, could you come up with a minimal test case, preferably in the form
> > of a patch to t/t3415-rebase-autosquash.sh?
> 
> I don't think I'm familiar enough with the test code to be able to
> provide a good patch but the following code should illustrate the
> regression.

I think that you think you're too unfamiliar with the test code only because you have not had a look.

> git init testrepo
> cd testrepo
> echo 1 > file && git add file && git commit -m "initial"
test_commit initial
> echo 1 >> file && git commit -am "commit 1"
test_commit 1 initial.t
> # rev_commit_1=$(git rev-parse HEAD)
> echo 1 >> file && git commit -am "fixup! $(git rev-parse --short HEAD)"
test_commit "fixup! 1" initial.t
> echo 1 >> file && git commit -am "commit 3"
test_commit 3 initial.t
> rev_orig_head=$(git rev-parse HEAD)
> GIT_EDITOR=: git rebase --autosquash -i HEAD~3
> test $(git rev-parse ORIG_HEAD) = $rev_orig_head
test_cmp_rev $rev_orig_head ORIG_HEAD
I leave it to you to make the actual patch.

Ciao, Johannes

Show 14 quoted lines
> 
> In older versions of git this test succeeded, i.e. ORIG_HEAD pointed
> to the previous original head, $rev_orig_head. In git version 2.20.1
> ORIG_HEAD now points to the commit that got fixuped instead,
> $rev_commit_1.
> 
> In previous versions ORIG_HEAD only pointed somewhere else if "git
> reset" was invoked manually during the rebase operation. I'm not sure
> if this is desirable, maybe ORIG_HEAD should always point to the
> previous head after rebase completes, no matter what operations were
> run in between. What do you think?
> 
> Markus
> 
Nazri Ramliy· Feb 27, 2019, 05:45 UTC · re: Johannes Schindelin · lore

Re: git rebase: retain original head?

On Wed, Jan 9, 2019 at 10:08 PM Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:

> Having said that, it is an unintended regression in the built-in rebase.
> Markus, could you come up with a minimal test case, preferably in the form
> of a patch to t/t3415-rebase-autosquash.sh?

Something like this, perhaps? ("gmail converts tabs to spaces" caveat applies to the diff formatting):

--8<--
diff --git t/t3400-rebase.sh t/t3400-rebase.sh
index 3e73f7584c..cb55597a8b 100755
--- t/t3400-rebase.sh
+++ t/t3400-rebase.sh
@@ -59,6 +59,13 @@ test_expect_success 'rebase against master' '
  git rebase master
 '

+test_expect_success 'rebase sets ORIG_HEAD' '
+ echo Add B. > expect &&
+ echo Modify A. >> expect &&
+ git log --oneline --format=%s ORIG_HEAD.. > actual &&
+ test_cmp expect actual
+'
+
 test_expect_success 'rebase, with <onto> and <upstream> specified as
:/quuxery' '
  test_when_finished "git branch -D torebase" &&
  git checkout -b torebase my-topic-branch^ &&
-->8--

Bisect shows that the first bad commit is this one:

commit 21853626eac565dd42572d90724b29863f61eb3b
Author: Johannes Schindelin <johannes.schindelin@gmx.de>
Date:   Fri Jan 18 07:09:27 2019 -0800

    built-in rebase: call `git am` directly

I verified that by undoing the crux of that commit, and that fixes the
failing test:

-->8--
diff --git builtin/rebase.c builtin/rebase.c
index 7c7bc13e91..848f6740a0 100644
--- builtin/rebase.c
+++ builtin/rebase.c
@@ -728,11 +728,6 @@ static int run_specific_rebase(struct rebase_options *opts)
  goto finished_rebase;
  }

- if (opts->type == REBASE_AM) {
- status = run_am(opts);
- goto finished_rebase;
- }
-
  add_var(&script_snippet, "GIT_DIR", absolute_path(get_git_dir()));
  add_var(&script_snippet, "state_dir", opts->state_dir);

--8<--

But something seems off by my bisection in that the "bad" commit
happens about 10 days after this email thread :/

nazri
Johannes Schindelin· Feb 28, 2019, 10:10 UTC · re: Nazri Ramliy · lore

Re: git rebase: retain original head?

Hi Nazri,
On Wed, 27 Feb 2019, Nazri Ramliy wrote:
> On Wed, Jan 9, 2019 at 10:08 PM Johannes Schindelin
> <Johannes.Schindelin@gmx.de> wrote:
Oh wow. Better late than never, eh?
Show 28 quoted lines
> > Having said that, it is an unintended regression in the built-in
> > rebase. Markus, could you come up with a minimal test case, preferably
> > in the form of a patch to t/t3415-rebase-autosquash.sh?
> 
> Something like this, perhaps? ("gmail converts tabs to spaces" caveat
> applies to the diff formatting):
> 
> --8<--
> diff --git t/t3400-rebase.sh t/t3400-rebase.sh
> index 3e73f7584c..cb55597a8b 100755
> --- t/t3400-rebase.sh
> +++ t/t3400-rebase.sh
> @@ -59,6 +59,13 @@ test_expect_success 'rebase against master' '
>   git rebase master
>  '
> 
> +test_expect_success 'rebase sets ORIG_HEAD' '
> + echo Add B. > expect &&
> + echo Modify A. >> expect &&
> + git log --oneline --format=%s ORIG_HEAD.. > actual &&
> + test_cmp expect actual
> +'
> +
>  test_expect_success 'rebase, with <onto> and <upstream> specified as
> :/quuxery' '
>   test_when_finished "git branch -D torebase" &&
>   git checkout -b torebase my-topic-branch^ &&
> -->8--

I made it a little more stand-alone, and can confirm that this is now safely a regression in v2.21.0.

Show 32 quoted lines
> Bisect shows that the first bad commit is this one:
> 
> commit 21853626eac565dd42572d90724b29863f61eb3b
> Author: Johannes Schindelin <johannes.schindelin@gmx.de>
> Date:   Fri Jan 18 07:09:27 2019 -0800
> 
>     built-in rebase: call `git am` directly
> 
> I verified that by undoing the crux of that commit, and that fixes the
> failing test:
> 
> -->8--
> diff --git builtin/rebase.c builtin/rebase.c
> index 7c7bc13e91..848f6740a0 100644
> --- builtin/rebase.c
> +++ builtin/rebase.c
> @@ -728,11 +728,6 @@ static int run_specific_rebase(struct rebase_options *opts)
>   goto finished_rebase;
>   }
> 
> - if (opts->type == REBASE_AM) {
> - status = run_am(opts);
> - goto finished_rebase;
> - }
> -
>   add_var(&script_snippet, "GIT_DIR", absolute_path(get_git_dir()));
>   add_var(&script_snippet, "state_dir", opts->state_dir);
> 
> --8<--
> 
> But something seems off by my bisection in that the "bad" commit
> happens about 10 days after this email thread :/

Yep, I had fixed the issue before this commit, and a regression test would have come in handy to prevent that regression from creeping in *again*.

Anyway, I'm on it. Johannes

P.S.: If you want to follow my progress, I'll push updates to the rebase-am-and-orig-head branch at https://github.com/dscho/git.

← back to recent threads