On Thu, 25 Aug 2011, Jeff King wrote: > On Thu, Aug 25, 2011 at 03:15:42PM +0200, Michal Sojka wrote: > > + # Check whether some other refs contain the commit to be edited > > + if {[exec git rev-list --stdin $id << $otherrefsneg] eq {}} { > > + if { [confirm_popup [mc "The commit you are going to edit is contained in another, possibly non-local, ref (e.g. branch or tag).\ > > + It is a bad idea to change a ref that is possibly used by other people. See git-rebase(1) for details.\n\n\ > > + Do you want to continue?"]]} { > > + return 1 > > Minor micro-optimization: this can be "git rev-list -1". You only care > if it produces the one commit, so that's sufficient. Without "-1", git > will keep reporting commits all the way down to the merge base with some > other ref. Here is a new version with the micro-optimization. Another minor change is that this patch now applies to gitk repo (http://git.kernel.org/pub/scm/gitk/gitk.git) instead of the main git repo. -Michal --8<---------------cut here---------------start------------->8--- I often use gitk to review patches before pushing them out and I would like to have an easy way to edit commits. The current approach with copying the commitid, switching to terminal, invoking git rebase -i, editing the commit and switching back to gitk is a way too complicated just for changing a single letter in the commit message or removing a debug printf(). This patch adds "Edit this commit" item to gitk's context menu which invokes interactive rebase in a non-interactive way :-). git gui is used to actually edit the commit. Besides editing the commit message, splitting of commits, as described in git-rebase(1), is also supported. The user is warned if the commit to be edited is contained in another ref besides the current branch and the stash (e.g. in a remote branch). Additionally, error box is displayed if the user attempts to edit a commit not contained in the current branch. Signed-off-by: Michal Sojka <sojka@xxxxxxxxxxxxxxxxxxxx> --- gitk | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 67 insertions(+), 4 deletions(-) diff --git a/gitk b/gitk index a701e0d..1ca2d00 100755 --- a/gitk +++ b/gitk @@ -2481,6 +2481,7 @@ proc makewindow {} { makemenu $rowctxmenu { {mc "Diff this -> selected" command {diffvssel 0}} {mc "Diff selected -> this" command {diffvssel 1}} + {mc "Edit this commit" command edit_commit} {mc "Make patch" command mkpatch} {mc "Create tag" command mktag} {mc "Write commit to file" command writecommit} @@ -8445,18 +8446,18 @@ proc rowmenu {x y id} { if {$id ne $nullid && $id ne $nullid2} { set menu $rowctxmenu if {$mainhead ne {}} { - $menu entryconfigure 7 -label [mc "Reset %s branch to here" $mainhead] -state normal + $menu entryconfigure 8 -label [mc "Reset %s branch to here" $mainhead] -state normal } else { - $menu entryconfigure 7 -label [mc "Detached head: can't reset" $mainhead] -state disabled + $menu entryconfigure 8 -label [mc "Detached head: can't reset" $mainhead] -state disabled } if {[info exists markedid] && $markedid ne $id} { - $menu entryconfigure 9 -state normal $menu entryconfigure 10 -state normal $menu entryconfigure 11 -state normal + $menu entryconfigure 12 -state normal } else { - $menu entryconfigure 9 -state disabled $menu entryconfigure 10 -state disabled $menu entryconfigure 11 -state disabled + $menu entryconfigure 12 -state disabled } } else { set menu $fakerowmenu @@ -9120,6 +9121,68 @@ proc cherrypick {} { notbusy cherrypick } +proc rebase_ok {id} { + if {[exec git merge-base $id HEAD] ne $id} { + error_popup [mc "You cannot edit commits outside of the current branch."] + return 0 + } + set headref [exec git symbolic-ref HEAD] + set allrefs [split [exec git for-each-ref --format=%(refname)] "\n"] + set otherrefs {} + set otherrefsneg "" + foreach ref $allrefs { + if {$ref eq "refs/stash" || $ref eq $headref} continue + set otherrefsneg "$otherrefsneg^$ref\n" + } + + # Check whether some other refs contain the commit to be edited + if {[exec git rev-list --stdin $id --max-count=1 << $otherrefsneg] eq {}} { + if { [confirm_popup [mc "The commit you are going to edit is contained in another, possibly non-local, ref (e.g. branch or tag).\ + It is a bad idea to change a ref that is possibly used by other people. See git-rebase(1) for details.\n\n\ + Do you want to continue?"]]} { + return 1 + } else { + return 0 + } + } + return 1 +} + +proc edit_commit {} { + global rowmenuid selectedline + + if {![rebase_ok $rowmenuid]} return + + nowbusy edit [mc "Editing commit"] + if {[catch {exec sh -c "(GIT_EDITOR='sed -ie 1s/^pick/edit/' git rebase -p -i --no-autosquash $rowmenuid^ && git gui citool --amend) 2>&1"} err]} { + notbusy edit + error_popup $err + exec git rebase --abort + return + } + set newcommit [exec git rev-parse HEAD] + while {[catch {exec sh -c "git diff-index --quiet --cached HEAD && git diff-files --quiet"} err]} { + if {[confirm_popup [mc "There are uncommited changes in the working tree or in the index.\ + Do you want to create a new commit (OK) or discard them (Cancel)?"]]} { + catch {exec git gui citool} err; + # In case of error (i.e. the user did not commit anything), we just ask him again + } else { + exec git reset --hard + } + } + if {[catch {exec sh -c "git rebase --continue 2>&1"} err]} { + notbusy edit + error_popup $err + exec git rebase --abort + return + } + updatecommits + # XXX How to select the edited commit? This doesn't work. + selbyid $newcommit + notbusy edit +} + + proc resethead {} { global mainhead rowmenuid confirm_ok resettype NS -- 1.7.5.4 --8<---------------cut here---------------end--------------->8--- -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html