threads / patch / 15435

patch, 2 partsgit-rebase-interactive: do not squash commits on abort

Subject: [PATCH 1/2] git-rebase-interactive: do not squash commits on abort

## tl;dr

12 messages between Sep 8, 2008 and Sep 10, 2008. Diffs are folded; open one to read it.

replies: 11people: 4as markdown or json

Dmitry Potapov· Sep 8, 2008, 20:42 UTC · lore
If git rebase interactive is stopped by "edit" command and then the user
said "git rebase --continue" while having some stage changes, git rebase
interactive is trying to amend the last commit by doing:
  git --soft reset && git commit

However, the user can abort commit for some reason by providing an empty log message, and that would leave the last commit undone, while the user being completely unaware about what happened. Now if the user tries to continue, by issuing "git rebase --continue" that squashes two previous commits.

Signed-off-by: Dmitry Potapov <dpotapov@gmail.com>
---
 git-rebase--interactive.sh |    7 +++++--
 1 files changed, 5 insertions(+), 2 deletions(-)
Show changes to git-rebase--interactive.sh +5 −2
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 929d681..5b2b1e5 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -429,12 +429,15 @@ do
 				die "Cannot find the author identity"
 			if test -f "$DOTEST"/amend
 			then
+				amend=$(git rev-parse --verify HEAD)
 				git reset --soft HEAD^ ||
 				die "Cannot rewind the HEAD"
 			fi
 			export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE &&
-			git commit --no-verify -F "$DOTEST"/message -e ||
-			die "Could not commit staged changes."
+			git commit --no-verify -F "$DOTEST"/message -e || {
+				test -n "$amend" && git reset --soft $amend
+				die "Could not commit staged changes."
+			}
 		fi
 
 		require_clean_work_tree
-- 
1.6.0
Dmitry Potapov· Sep 8, 2008, 20:42 UTC · re: Dmitry Potapov · lore

[PATCH 2/2] git-rebase--interactive: auto amend only edited commit

"git rebase --continue" issued after git rebase being stop by "edit" command is trying to amend the last commit using stage changes. However, if the last commit is not the commit that was marked as "edit" then it can produce unexpected results.

For instance, after being stop by "edit", I have made some changes to commit message using "git commit --amend". After that I realized that I forgot to add some changes to some file. So, I said "git add file" and the "git rebase --continue". Unfortunately, it caused that the new commit message was lost.

Another problem is that after being stopped at "edit", the user adds new commits. In this case, automatic amend behavior of git rebase triggered by some stage changes causes that not only that the log message of the last commit is lost but that it will contain also wrong Author and Date information.

Therefore, this patch restrict automatic amend only to the situation where HEAD is the commit at which git rebase stop by "edit" command.

Signed-off-by: Dmitry Potapov <dpotapov@gmail.com>
---
 git-rebase--interactive.sh |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)
Show changes to git-rebase--interactive.sh +3 −1
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 5b2b1e5..84721c9 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -284,7 +284,7 @@ do_next () {
 		pick_one $sha1 ||
 			die_with_patch $sha1 "Could not apply $sha1... $rest"
 		make_patch $sha1
-		: > "$DOTEST"/amend
+		echo $sha1 > "$DOTEST"/amend
 		warn "Stopped at $sha1... $rest"
 		warn "You can amend the commit now, with"
 		warn
@@ -430,6 +430,8 @@ do
 			if test -f "$DOTEST"/amend
 			then
 				amend=$(git rev-parse --verify HEAD)
+				test "$amend" = $(cat "$DOTEST"/amend) ||
+				die "You have uncommitted changes"
 				git reset --soft HEAD^ ||
 				die "Cannot rewind the HEAD"
 			fi
-- 
1.6.0
Junio C Hamano· Sep 9, 2008, 00:33 UTC · re: Dmitry Potapov · lore

Re: [PATCH 2/2] git-rebase--interactive: auto amend only edited commit

I may not be reading your patch correctly; I am confused by what you are trying to do.

Show 10 quoted lines
> diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
> index 5b2b1e5..84721c9 100755
> --- a/git-rebase--interactive.sh
> +++ b/git-rebase--interactive.sh
> @@ -284,7 +284,7 @@ do_next () {
>  		pick_one $sha1 ||
>  			die_with_patch $sha1 "Could not apply $sha1... $rest"
>  		make_patch $sha1
> -		: > "$DOTEST"/amend
> +		echo $sha1 > "$DOTEST"/amend

You record the $sha1 from the TODO file. If you are editing the first one in the insn sequence, that is also the same as HEAD (i.e. the commit you are telling the user to modify with "commit --amend"). If you already have edited earlier ones, it is not the value of HEAD at this point.

Show 9 quoted lines
>  		warn "Stopped at $sha1... $rest"
>  		warn "You can amend the commit now, with"
>  		warn
> @@ -430,6 +430,8 @@ do
>  			if test -f "$DOTEST"/amend
>  			then
>  				amend=$(git rev-parse --verify HEAD)
> +				test "$amend" = $(cat "$DOTEST"/amend) ||
> +				die "You have uncommitted changes"

And then you complain if HEAD, after running "commit --amend", is not the value you recorded above.

>  				git reset --soft HEAD^ ||
>  				die "Cannot rewind the HEAD"
>  			fi

If the first commit was marked as "edit", then the value you recorded in "$DOTEST/amend" was the HEAD, the commit the user was told to modify with "commit --amend". $DOTEST/amend being the same as HEAD here would be a sign that the user staged changes but hasn't done "commit --amend".

But if we think about the second and subsequent commits that are marked as "edit" in TODO file, the value recorded in "$DOTEST/amend" would not be the value of HEAD at this point, and whether "commit --amend" has been run or not, the value of HEAD is very likely to be different from that value (unless the user said "reset --hard $that_one"). Wouldn't this test almost always complain for them?

Perhaps you wanted to record the value of the HEAD in the first hunk?

How would this change interact with the workflow of splitting existing commits, described at the end of git-rebase documentation?

Dmitry Potapov· Sep 9, 2008, 07:00 UTC · re: Junio C Hamano · lore

Re: [PATCH 2/2] git-rebase--interactive: auto amend only edited commit

On Mon, Sep 08, 2008 at 05:33:22PM -0700, Junio C Hamano wrote:
Show 15 quoted lines
> I may not be reading your patch correctly; I am confused by what you are
> trying to do.
> 
> > diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
> > index 5b2b1e5..84721c9 100755
> > --- a/git-rebase--interactive.sh
> > +++ b/git-rebase--interactive.sh
> > @@ -284,7 +284,7 @@ do_next () {
> >  		pick_one $sha1 ||
> >  			die_with_patch $sha1 "Could not apply $sha1... $rest"
> >  		make_patch $sha1
> > -		: > "$DOTEST"/amend
> > +		echo $sha1 > "$DOTEST"/amend
> 
> You record the $sha1 from the TODO file.
Oops... I should have gone to bed instead of sending this patch...
> 
> Perhaps you wanted to record the value of the HEAD in the first hunk?
You are asbolutely right. It should be:
	git rev-parse --verify HEAD > "$DOTEST"/amend
> 
> How would this change interact with the workflow of splitting existing
> commits, described at the end of git-rebase documentation?

It is not affected, because accordingly to the documentation, you commit all your changes "until your working tree is clean."

However, if you forgot to commit your changes, without my patch you may lose some of your splitting work. Here is the script that demonstrates the problem

-- >8 -- #!/bin/sh

set -e

rm -rf git-test mkdir -p git-test cd git-test

git init for ((i=0;$i<10;i++)) do echo $i; done > foo git add foo git commit -m 'add foo'

sed -e 's/^0$/first line/;s/^9/last line/' < foo > tmp mv tmp foo git add foo git commit -m 'change foo'

echo bar > bar git add bar git commit -m 'more changes'

git log echo '==='

GIT_EDITOR='sed -e '\''s/pick \(.* change foo\)/edit \1/'\'' < "$1" > tmp && mv tmp' git rebase -i HEAD~2
git reset HEAD^
{ echo y; echo n; } | git add -p
git commit -m 'commit chunk 1'

git add foo # If I commit chunk 2 then everything will be fine with and without # my patch # # git commit -m 'commit chunk 2' # # However, if I forgot to commit chunk 2 then without my patch rebase # will lose my work on splitting these chunks! # # With my patch, it will error out: You have uncommitted changes

GIT_EDITOR=cat git rebase --continue

git log -- >8 --

Dmitry
Dmitry Potapov· Sep 9, 2008, 12:05 UTC · re: Dmitry Potapov · lore

[PATCH v2] git-rebase--interactive: auto amend only edited commit

"git rebase --continue" issued after git rebase being stop by "edit" command is trying to amend the last commit using stage changes. However, if the last commit is not the commit that was marked as "edit" then it can produce unexpected results.

For instance, after being stop by "edit", I have made some changes to commit message using "git commit --amend". After that I realized that I forgot to add some changes to some file. So, I said "git add file" and the "git rebase --continue". Unfortunately, it caused that the new commit message was lost.

Another problem is that after being stopped at "edit", the user adds new commits. In this case, automatic amend behavior of git rebase triggered by some stage changes causes that not only that the log message of the last commit is lost but that it will contain also wrong Author and Date information.

Therefore, this patch restrict automatic amend only to the situation where HEAD is the commit at which git rebase stop by "edit" command.

Signed-off-by: Dmitry Potapov <dpotapov@gmail.com>
---
Changes:
 1. I have correct the obvious bug pointed by Junio.
 2. The error message is more verbose now.
 git-rebase--interactive.sh |    6 +++++-
 1 files changed, 5 insertions(+), 1 deletions(-)
Show changes to git-rebase--interactive.sh +5 −1
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 5b2b1e5..d2aa954 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -284,7 +284,7 @@ do_next () {
 		pick_one $sha1 ||
 			die_with_patch $sha1 "Could not apply $sha1... $rest"
 		make_patch $sha1
-		: > "$DOTEST"/amend
+		git rev-parse --verify HEAD > "$DOTEST"/amend
 		warn "Stopped at $sha1... $rest"
 		warn "You can amend the commit now, with"
 		warn
@@ -430,6 +430,10 @@ do
 			if test -f "$DOTEST"/amend
 			then
 				amend=$(git rev-parse --verify HEAD)
+				test "$amend" = $(cat "$DOTEST"/amend) ||
+				die "\
+You have uncommitted changes in your working tree. Please, commit them
+first and then run 'git rebase --continue' again."
 				git reset --soft HEAD^ ||
 				die "Cannot rewind the HEAD"
 			fi
-- 
1.6.0
Johannes Sixt· Sep 9, 2008, 06:42 UTC · re: Dmitry Potapov · lore

Re: [PATCH 2/2] git-rebase--interactive: auto amend only edited commit

Dmitry Potapov schrieb:
Show 8 quoted lines
> Another problem is that after being stopped at "edit", the user adds new
> commits. In this case, automatic amend behavior of git rebase triggered
> by some stage changes causes that not only that the log message of the
> last commit is lost but that it will contain also wrong Author and Date
> information.
> 
> Therefore, this patch restrict automatic amend only to the situation
> where HEAD is the commit at which git rebase stop by "edit" command.
...
Show 6 quoted lines
> @@ -430,6 +430,8 @@ do
>  			if test -f "$DOTEST"/amend
>  			then
>  				amend=$(git rev-parse --verify HEAD)
> +				test "$amend" = $(cat "$DOTEST"/amend) ||
> +				die "You have uncommitted changes"

Doesn't this terse message carry a bit of a "WTF?" factor? In other situations rebase --continue goes into git-commit just fine, but it does not under these special conditions. How about this:

"Will not auto-commit uncommitted changes after you have already committed something. Please run 'git commit --amend' yourself."

>  				git reset --soft HEAD^ ||
>  				die "Cannot rewind the HEAD"
>  			fi
-- Hannes
Dmitry Potapov· Sep 9, 2008, 07:30 UTC · re: Johannes Sixt · lore

Re: [PATCH 2/2] git-rebase--interactive: auto amend only edited commit

On Tue, Sep 09, 2008 at 08:42:57AM +0200, Johannes Sixt wrote:
Show 18 quoted lines
> Dmitry Potapov schrieb:
> > Another problem is that after being stopped at "edit", the user adds new
> > commits. In this case, automatic amend behavior of git rebase triggered
> > by some stage changes causes that not only that the log message of the
> > last commit is lost but that it will contain also wrong Author and Date
> > information.
> > 
> > Therefore, this patch restrict automatic amend only to the situation
> > where HEAD is the commit at which git rebase stop by "edit" command.
> ...
> > @@ -430,6 +430,8 @@ do
> >  			if test -f "$DOTEST"/amend
> >  			then
> >  				amend=$(git rev-parse --verify HEAD)
> > +				test "$amend" = $(cat "$DOTEST"/amend) ||
> > +				die "You have uncommitted changes"
> 
> Doesn't this terse message carry a bit of a "WTF?" factor?
Agreed. However, the current message in the case when you have some
unstaged changes in your working tree is not much better:
  "Working tree is dirty"
Show 6 quoted lines
> In other
> situations rebase --continue goes into git-commit just fine, but it does
> not under these special conditions. How about this:
> 
> "Will not auto-commit uncommitted changes after you have already committed
> something. Please run 'git commit --amend' yourself."

I don't think this is the right suggestion. In cases that I mentioned above (and in some others), you may want to run 'git commit' *without* --amend. Only user may know how those changes should be committed. Giving him/her a direct instruction to run some specific command will produce the wrong result in half cases. So, how about this:

"You have uncommitted changes in your working tree. Please, commit them first and then run 'git rebase --continue' again."

or if you want to describe the cause why auto-commit does not work:

"Will not auto-commit uncommitted changes after you have already committed something. Please commit them first and then run 'git rebase --continue' again."

Personally, I believe those words about auto-commit is not very helpful. Auto-commit-on-edit feature is undocumented. So, those words may be more confusing than helpful, because the user starts thinking what this auto- commit means, while the real question here is whether changes should be committed with --amend or without it.

Dmitry
Junio C Hamano· Sep 9, 2008, 00:06 UTC · re: Dmitry Potapov · lore

Re: [PATCH 1/2] git-rebase-interactive: do not squash commits on abort

Dmitry Potapov <dpotapov@gmail.com> writes:
Show 36 quoted lines
> If git rebase interactive is stopped by "edit" command and then the user
> said "git rebase --continue" while having some stage changes, git rebase
> interactive is trying to amend the last commit by doing:
>   git --soft reset && git commit
>
> However, the user can abort commit for some reason by providing an empty
> log message, and that would leave the last commit undone, while the user
> being completely unaware about what happened. Now if the user tries to
> continue, by issuing "git rebase --continue" that squashes two previous
> commits.
>
> Signed-off-by: Dmitry Potapov <dpotapov@gmail.com>
> ---
>  git-rebase--interactive.sh |    7 +++++--
>  1 files changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
> index 929d681..5b2b1e5 100755
> --- a/git-rebase--interactive.sh
> +++ b/git-rebase--interactive.sh
> @@ -429,12 +429,15 @@ do
>  				die "Cannot find the author identity"
>  			if test -f "$DOTEST"/amend
>  			then
> +				amend=$(git rev-parse --verify HEAD)
>  				git reset --soft HEAD^ ||
>  				die "Cannot rewind the HEAD"
>  			fi
>  			export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE &&
> -			git commit --no-verify -F "$DOTEST"/message -e ||
> -			die "Could not commit staged changes."
> +			git commit --no-verify -F "$DOTEST"/message -e || {
> +				test -n "$amend" && git reset --soft $amend
> +				die "Could not commit staged changes."
> +			}
>  		fi

Feels obviously correct from a cursory look, although I admit I haven't thought about other possible corner cases that this additional reset may fire when it shouldn't. Dscho?

Show 9 quoted lines
>  
>  		require_clean_work_tree
> -- 
> 1.6.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
Johannes Schindelin· Sep 9, 2008, 14:17 UTC · re: Junio C Hamano · lore

Re: [PATCH 1/2] git-rebase-interactive: do not squash commits on abort

Hi,
On Mon, 8 Sep 2008, Junio C Hamano wrote:
Show 42 quoted lines
> Dmitry Potapov <dpotapov@gmail.com> writes:
> 
> > If git rebase interactive is stopped by "edit" command and then the user
> > said "git rebase --continue" while having some stage changes, git rebase
> > interactive is trying to amend the last commit by doing:
> >   git --soft reset && git commit
> >
> > However, the user can abort commit for some reason by providing an empty
> > log message, and that would leave the last commit undone, while the user
> > being completely unaware about what happened. Now if the user tries to
> > continue, by issuing "git rebase --continue" that squashes two previous
> > commits.
> >
> > Signed-off-by: Dmitry Potapov <dpotapov@gmail.com>
> > ---
> >  git-rebase--interactive.sh |    7 +++++--
> >  1 files changed, 5 insertions(+), 2 deletions(-)
> >
> > diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
> > index 929d681..5b2b1e5 100755
> > --- a/git-rebase--interactive.sh
> > +++ b/git-rebase--interactive.sh
> > @@ -429,12 +429,15 @@ do
> >  				die "Cannot find the author identity"
> >  			if test -f "$DOTEST"/amend
> >  			then
> > +				amend=$(git rev-parse --verify HEAD)
> >  				git reset --soft HEAD^ ||
> >  				die "Cannot rewind the HEAD"
> >  			fi
> >  			export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE &&
> > -			git commit --no-verify -F "$DOTEST"/message -e ||
> > -			die "Could not commit staged changes."
> > +			git commit --no-verify -F "$DOTEST"/message -e || {
> > +				test -n "$amend" && git reset --soft $amend
> > +				die "Could not commit staged changes."
> > +			}
> >  		fi
> 
> Feels obviously correct from a cursory look, although I admit I haven't
> thought about other possible corner cases that this additional reset may
> fire when it shouldn't.  Dscho?

It might be safer to set the variable "amend" to empty before the "if". Other than that, I think that patch makes tons of sense.

Thanks, Dscho

Dmitry Potapov· Sep 9, 2008, 20:50 UTC · re: Johannes Schindelin · lore

[PATCH v2] git-rebase-interactive: do not squash commits on abort

If git rebase interactive is stopped by "edit" command and then the user
said "git rebase --continue" while having some stage changes, git rebase
interactive is trying to amend the last commit by doing:
  git --soft reset && git commit

However, the user can abort commit for some reason by providing an empty log message, and that would leave the last commit undone, while the user being completely unaware about what happened. Now if the user tries to continue, by issuing "git rebase --continue" that squashes two previous commits.

Signed-off-by: Dmitry Potapov <dpotapov@gmail.com>
---
On Tue, Sep 09, 2008 at 04:17:36PM +0200, Johannes Schindelin wrote:
>_
> It might be safer to set the variable "amend" to empty before the "if".
OK. I have added it.
 git-rebase--interactive.sh |    8 ++++++--
 1 files changed, 6 insertions(+), 2 deletions(-)
Show changes to git-rebase--interactive.sh +6 −2
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 929d681..aaca915 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -427,14 +427,18 @@ do
 		else
 			. "$DOTEST"/author-script ||
 				die "Cannot find the author identity"
+			amend=""
 			if test -f "$DOTEST"/amend
 			then
+				amend=$(git rev-parse --verify HEAD)
 				git reset --soft HEAD^ ||
 				die "Cannot rewind the HEAD"
 			fi
 			export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE &&
-			git commit --no-verify -F "$DOTEST"/message -e ||
-			die "Could not commit staged changes."
+			git commit --no-verify -F "$DOTEST"/message -e || {
+				test -n "$amend" && git reset --soft $amend
+				die "Could not commit staged changes."
+			}
 		fi
 
 		require_clean_work_tree
-- 
1.6.0
Johannes Schindelin· Sep 10, 2008, 09:53 UTC · re: Dmitry Potapov · lore

Re: [PATCH v2] git-rebase-interactive: do not squash commits on abort

Hi,
On Wed, 10 Sep 2008, Dmitry Potapov wrote:
Show 18 quoted lines
> On Tue, Sep 09, 2008 at 04:17:36PM +0200, Johannes Schindelin wrote:
> >_
> > It might be safer to set the variable "amend" to empty before the "if".
> 
> OK. I have added it.
> 
>  git-rebase--interactive.sh |    8 ++++++--
>  1 files changed, 6 insertions(+), 2 deletions(-)
> 
> diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
> index 929d681..aaca915 100755
> --- a/git-rebase--interactive.sh
> +++ b/git-rebase--interactive.sh
> @@ -427,14 +427,18 @@ do
>  		else
>  			. "$DOTEST"/author-script ||
>  				die "Cannot find the author identity"
> +			amend=""

Sorry, my mistake... I should have been more explicit. In most (if not all) shell scripts, we prefer to set to the empty string with the expression

			amend=

Sorry to be nitting here, but I think it might be desirable in the long run to have consistent code style, as it makes it a bigger pleasure to work with the code.

Ciao, Dscho

Junio C Hamano· Sep 10, 2008, 22:36 UTC · re: Johannes Schindelin · lore

Re: [PATCH v2] git-rebase-interactive: do not squash commits on abort

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
Show 15 quoted lines
>> diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
>> index 929d681..aaca915 100755
>> --- a/git-rebase--interactive.sh
>> +++ b/git-rebase--interactive.sh
>> @@ -427,14 +427,18 @@ do
>>  		else
>>  			. "$DOTEST"/author-script ||
>>  				die "Cannot find the author identity"
>> +			amend=""
>
> Sorry, my mistake... I should have been more explicit.  In most (if not 
> all) shell scripts, we prefer to set to the empty string with the 
> expression
>
> 			amend=

Don't worry. I would notice and remove "" in my MUA for things like these; especially the patch is this small it is hard to miss.

← back to recent threads