Re: [PATCH v1 09/19] rebase -i: commit only once when rewriting picks

2014-08-06 Thread Fabian Ruch
Hi Jeff,

Jeff King writes:
 On Tue, Jul 29, 2014 at 01:18:09AM +0200, Fabian Ruch wrote:
 The options passed to `do_pick` determine whether the picked commit
 will be rewritten or not. If the commit gets rewritten, because the
 user requested to edit the commit message for instance, let
 `pick_one` merely apply the changes introduced by the commit and do
 not commit the resulting tree yet. If the commit is replayed as is,
 leave it to `pick_one` to recreate the commit (possibly by
 fast-forwarding the head). This makes it easier to combine git-commit
 options like `--edit` and `--amend` in `do_pick` because
 git-cherry-pick does not support `--amend`.

 In the case of `--edit`, do not `exit_with_patch` but assign
 `rewrite` to pick the changes with `-n`. If the pick conflicts, no
 commit is created which we would have to amend when continuing the
 rebase. To complete the pick after the conflicts are resolved the
 user just resumes with `git rebase --continue`.
 
 Hmm. So does this mean the user will actually see a different state
 during such a conflict?
 
 E.g., if I have instructions like:
 
   pick A
   squash B
   squash C
 
 and there is a conflict picking C, then what state do I see? Right now I
 see a commit with the A+B squash prepared. But your description sounds
 to me like we would avoid the squash for B, and the user would see a
 different state.

The squash state will not be different. squash sequences are still taken
care of one line after the other: committing A, committing A+B,
committing A+B+C. If there is a conflict picking C, HEAD will point to
A+B and the index will record the conflicting changes.

 However, I couldn't trigger that behavior in a few experiments. Am I
 misunderstanding, or is there some case where the user-visible state
 will be different?

The user-visible state will be different for rewords. For instance,
let's consider

pick A
reword B.

The verbose log used to show two commits for B (with ff disabled), one
after picking and one after editing. Now the log will show a single
commit in connection with 'reword B' which might be less confusing.

Thanks for raising your eyebrows. I noticed now that the last paragraph
is plainly wrong. The described amend situation did not arise if the
pick conflicted but if the edited commit did not verify. There will
be no after the conflicts are resolved but the user can either commit
manually and circumvent log message verification if she knows what she's
doing, or provide a corrected log message in the editor launched by 'git
rebase --continue'. The _incomplete_ 'git commit --amend' tip which used
to be displayed after a failed verification hook could become
unnecessary and this would possibly spare us including correct GPG sign
options for instance.

However, this patch is mostly motivated by the unification of how
commits are rewritten. Before, rewords and squashes went about this
differently, now both fail with an uncommitted index if there are
conflicts or the log message is ill-formatted.

The log message must be corrected and the following bug, which was
noticed after PATCH v2, must be fixed.

cat .git/hooks/commit-msg -EOF
#!/bin/sh
exit 1
EOF
chmod +x .git/hooks/commit-msg
test_must_fail env FAKE_LINES=reword 1 git rebase -i
test_must_fail git rebase --continue
# the last command succeeds because --continue does not verify

   Fabian
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [PATCH v1 09/19] rebase -i: commit only once when rewriting picks

2014-08-01 Thread Jeff King
On Tue, Jul 29, 2014 at 01:18:09AM +0200, Fabian Ruch wrote:

 The options passed to `do_pick` determine whether the picked commit
 will be rewritten or not. If the commit gets rewritten, because the
 user requested to edit the commit message for instance, let
 `pick_one` merely apply the changes introduced by the commit and do
 not commit the resulting tree yet. If the commit is replayed as is,
 leave it to `pick_one` to recreate the commit (possibly by
 fast-forwarding the head). This makes it easier to combine git-commit
 options like `--edit` and `--amend` in `do_pick` because
 git-cherry-pick does not support `--amend`.
 
 In the case of `--edit`, do not `exit_with_patch` but assign
 `rewrite` to pick the changes with `-n`. If the pick conflicts, no
 commit is created which we would have to amend when continuing the
 rebase. To complete the pick after the conflicts are resolved the
 user just resumes with `git rebase --continue`.

Hmm. So does this mean the user will actually see a different state
during such a conflict?

E.g., if I have instructions like:

  pick A
  squash B
  squash C

and there is a conflict picking C, then what state do I see? Right now I
see a commit with the A+B squash prepared. But your description sounds
to me like we would avoid the squash for B, and the user would see a
different state.

However, I couldn't trigger that behavior in a few experiments. Am I
misunderstanding, or is there some case where the user-visible state
will be different?

-Peff
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[PATCH v1 09/19] rebase -i: commit only once when rewriting picks

2014-07-28 Thread Fabian Ruch
The options passed to `do_pick` determine whether the picked commit
will be rewritten or not. If the commit gets rewritten, because the
user requested to edit the commit message for instance, let
`pick_one` merely apply the changes introduced by the commit and do
not commit the resulting tree yet. If the commit is replayed as is,
leave it to `pick_one` to recreate the commit (possibly by
fast-forwarding the head). This makes it easier to combine git-commit
options like `--edit` and `--amend` in `do_pick` because
git-cherry-pick does not support `--amend`.

In the case of `--edit`, do not `exit_with_patch` but assign
`rewrite` to pick the changes with `-n`. If the pick conflicts, no
commit is created which we would have to amend when continuing the
rebase. To complete the pick after the conflicts are resolved the
user just resumes with `git rebase --continue`.

git-commit lets the user edit the commit log message by default. We
do not want that for the rewriting git-commit command line because
the default behaviour of git-rebase is exactly the opposite. Pass
`--no-edit` when rewriting a picked commit. An explicit `--edit`
passed to `do_pick` (for instance, when reword is executed) enables
the editor launch again. Similarly, pass `--allow-empty-message`
unless the log message is edited.

If `rebase--interactive` is used to rebase a complete branch onto
some head, `rebase` creates a sentinel commit that requires special
treatment by `do_pick`. Do not finalize the pick here either because
its commit message can be altered as for any other pick. Since the
orphaned root commit gets a temporary parent, it is always rewritten.
Safely use the rewrite infrastructure of `do_pick` to create the
final commit.

Signed-off-by: Fabian Ruch baf...@gmail.com
---
 git-rebase--interactive.sh | 58 --
 1 file changed, 36 insertions(+), 22 deletions(-)

diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 46f436f..96c24e8 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -63,7 +63,8 @@ msgnum=$state_dir/msgnum
 author_script=$state_dir/author-script
 
 # When an edit rebase command is being processed, the SHA1 of the
-# commit to be edited is recorded in this file.  When git rebase
+# commit to be edited is recorded in this file.  The same happens when
+# rewriting a commit fails, for instance reword.  When git rebase
 # --continue is executed, if there are any staged changes then they
 # will be amended to the HEAD commit, but only provided the HEAD
 # commit is still the commit to be edited.  When any other rebase
@@ -479,12 +480,17 @@ record_in_rewritten() {
 # The commit message title of commit. Used for information
 # purposes only.
 do_pick () {
-   edit=
+   allow_empty_message=y
+   rewrite=
+   rewrite_amend=
+   rewrite_edit=
while test $# -gt 0
do
case $1 in
-e|--edit)
-   edit=y
+   rewrite=y
+   rewrite_edit=y
+   allow_empty_message=
;;
-*)
die do_pick: unrecognized option -- $1
@@ -499,6 +505,10 @@ do_pick () {
 
if test $(git rev-parse HEAD) = $squash_onto
then
+   rewrite=y
+   rewrite_amend=y
+   git rev-parse --verify HEAD $amend
+
# Set the correct commit message and author info on the
# sentinel root before cherry-picking the original changes
# without committing (-n).  Finally, update the sentinel again
@@ -509,31 +519,35 @@ do_pick () {
# rebase --continue.
git commit --allow-empty --allow-empty-message --amend \
   --no-post-rewrite -n -q -C $1 
-   pick_one -n $1 
-   git commit --allow-empty --allow-empty-message \
-  --amend --no-post-rewrite -n --no-edit \
-  ${gpg_sign_opt:+$gpg_sign_opt} ||
+   pick_one -n $1 ||
die_with_patch $1 Could not apply $1... $2
else
-   pick_one $1 ||
+   pick_one ${rewrite:+-n} $1 ||
die_with_patch $1 Could not apply $1... $2
fi
 
-   if test -n $edit
+   if test -n $rewrite
then
-   # TODO: Work around the fact that git-commit lets us
-   # disable either both the pre-commit and the commit-msg
-   # hook or none. Disable the pre-commit hook because the
-   # tree is left unchanged but run the commit-msg hook
-   # from here because the log message is altered.
-   git commit --allow-empty --amend --no-post-rewrite -n 
${gpg_sign_opt:+$gpg_sign_opt} 
-   if test -x $GIT_DIR/hooks/commit-msg
-