git-gui: Improve handling of merge commits.
authorShawn O. Pearce <spearce@spearce.org>
Tue, 21 Nov 2006 02:27:22 +0000 (21:27 -0500)
committerShawn O. Pearce <spearce@spearce.org>
Tue, 21 Nov 2006 05:22:33 +0000 (00:22 -0500)
Its useful to be able to amend the last commit even if it was a merge
commit, so we really should support that in the gui.  We now do so by
making PARENT a list.  We always diff against the first parent but we
create a commit consisting of the parent(s) listed in this list, in
order.

We also should recheck the repository state during an amend.  Earlier
I was bitten by this exact bug when I switched branches through a
command prompt and then did not do a rescan in git-gui.  When I hit
"Amend Last Commit" I was surprised to see information from the prior
branch appear.  This was due to git-gui caching the data from the last
rescan and using that data form the amend data load request, rather than
the data of the current branch.

Improved error text in the dialogs used to tell the user why an amend is
being refused by git-gui.  In general this is only during an initial
commit (nothing prior to amend) and during a merge commit (it is simply
too confusing to amend the last commit while also trying to complete a
merge).

Fixed a couple of minor bugs in the pull logic.  Since this code isn't
really useful nobody has recently tested it and noticed the breakage.
It really needs to be rewritten anyway.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
git-gui

diff --git a/git-gui b/git-gui
index dcbb100feb8abbdba734683ceeae7de0ade8560e..d5738baf109d16eab62e1966e6192a8b35c11a38 100755 (executable)
--- a/git-gui
+++ b/git-gui
@@ -231,25 +231,38 @@ proc unlock_index {} {
 ##
 ## status
 
-proc repository_state {hdvar ctvar} {
+proc repository_state {ctvar hdvar mhvar} {
        global gitdir
-       upvar $hdvar hd $ctvar ct
+       upvar $ctvar ct $hdvar hd $mhvar mh
+
+       set mh [list]
 
        if {[catch {set hd [exec git rev-parse --verify HEAD]}]} {
                set hd {}
                set ct initial
-       } elseif {[file exists [file join $gitdir MERGE_HEAD]]} {
+               return
+       }
+
+       set merge_head [file join $gitdir MERGE_HEAD]
+       if {[file exists $merge_head]} {
                set ct merge
-       } else {
-               set ct normal
+               set fd_mh [open $merge_head r]
+               while {[gets $fd_mh line] >= 0} {
+                       lappend mh $line
+               }
+               close $fd_mh
+               return
        }
+
+       set ct normal
 }
 
 proc PARENT {} {
        global PARENT empty_tree
 
-       if {$PARENT ne {}} {
-               return $PARENT
+       set p [lindex $PARENT 0]
+       if {$p ne {}} {
+               return $p
        }
        if {$empty_tree eq {}} {
                set empty_tree [exec git mktree << {}]
@@ -258,21 +271,22 @@ proc PARENT {} {
 }
 
 proc rescan {after} {
-       global HEAD PARENT commit_type
+       global HEAD PARENT MERGE_HEAD commit_type
        global ui_index ui_other ui_status_value ui_comm
        global rescan_active file_states
        global repo_config
 
        if {$rescan_active > 0 || ![lock_index read]} return
 
-       repository_state new_HEAD new_type
+       repository_state newType newHEAD newMERGE_HEAD
        if {[string match amend* $commit_type]
-               && $new_type eq {normal}
-               && $new_HEAD eq $HEAD} {
+               && $newType eq {normal}
+               && $newHEAD eq $HEAD} {
        } else {
-               set HEAD $new_HEAD
-               set PARENT $new_HEAD
-               set commit_type $new_type
+               set HEAD $newHEAD
+               set PARENT $newHEAD
+               set MERGE_HEAD $newMERGE_HEAD
+               set commit_type $newType
        }
 
        array unset file_states
@@ -686,23 +700,36 @@ proc read_diff {fd} {
 ## commit
 
 proc load_last_commit {} {
-       global HEAD PARENT commit_type ui_comm
+       global HEAD PARENT MERGE_HEAD commit_type ui_comm
 
-       if {[string match amend* $commit_type]} return
-       if {$commit_type ne {normal}} {
-               error_popup "Can't amend a $commit_type commit."
+       if {[llength $PARENT] == 0} {
+               error_popup {There is nothing to amend.
+
+You are about to create the initial commit.
+There is no commit before this to amend.
+}
+               return
+       }
+
+       repository_state curType curHEAD curMERGE_HEAD
+       if {$curType eq {merge}} {
+               error_popup {Cannot amend while merging.
+
+You are currently in the middle of a merge that
+has not been fully completed.  You cannot amend
+the prior commit unless you first abort the
+current merge activity.
+}
                return
        }
 
        set msg {}
-       set parent {}
-       set parent_count 0
+       set parents [list]
        if {[catch {
-                       set fd [open "| git cat-file commit $HEAD" r]
+                       set fd [open "| git cat-file commit $curHEAD" r]
                        while {[gets $fd line] > 0} {
                                if {[string match {parent *} $line]} {
-                                       set parent [string range $line 7 end]
-                                       incr parent_count
+                                       lappend parents [string range $line 7 end]
                                }
                        }
                        set msg [string trim [read $fd]]
@@ -712,17 +739,13 @@ proc load_last_commit {} {
                return
        }
 
-       if {$parent_count > 1} {
-               error_popup {Can't amend a merge commit.}
-               return
-       }
-
-       if {$parent_count == 0} {
-               set commit_type amend-initial
-               set PARENT {}
-       } elseif {$parent_count == 1} {
-               set commit_type amend
-               set PARENT $parent
+       set HEAD $curHEAD
+       set PARENT $parents
+       set MERGE_HEAD [list]
+       switch -- [llength $parents] {
+       0       {set commit_type amend-initial}
+       1       {set commit_type amend}
+       default {set commit_type amend-merge}
        }
 
        $ui_comm delete 0.0 end
@@ -770,11 +793,11 @@ proc commit_tree {} {
 
        # -- Our in memory state should match the repository.
        #
-       repository_state curHEAD cur_type
+       repository_state curType curHEAD curMERGE_HEAD
        if {[string match amend* $commit_type]
-               && $cur_type eq {normal}
+               && $curType eq {normal}
                && $curHEAD eq $HEAD} {
-       } elseif {$commit_type ne $cur_type || $HEAD ne $curHEAD} {
+       } elseif {$commit_type ne $curType || $HEAD ne $curHEAD} {
                info_popup {Last scanned state does not match repository state.
 
 Another Git program has modified this repository
@@ -920,7 +943,8 @@ proc commit_writetree {curHEAD msg} {
 }
 
 proc commit_committree {fd_wt curHEAD msg} {
-       global single_commit gitdir HEAD PARENT commit_type tcl_platform
+       global HEAD PARENT MERGE_HEAD commit_type
+       global single_commit gitdir tcl_platform
        global ui_status_value ui_comm selected_commit_type
        global file_states selected_paths rescan_active
 
@@ -935,24 +959,12 @@ proc commit_committree {fd_wt curHEAD msg} {
        # -- Create the commit.
        #
        set cmd [list git commit-tree $tree_id]
-       if {$PARENT ne {}} {
-               lappend cmd -p $PARENT
-       }
-       if {$commit_type eq {merge}} {
-               if {[catch {
-                               set fd_mh [open [file join $gitdir MERGE_HEAD] r]
-                               while {[gets $fd_mh merge_head] >= 0} {
-                                       lappend cmd -p $merge_head
-                               }
-                               close $fd_mh
-                       } err]} {
-                       error_popup "Loading MERGE_HEAD failed:\n\n$err"
-                       set ui_status_value {Commit failed.}
-                       unlock_index
-                       return
+       set parents [concat $PARENT $MERGE_HEAD]
+       if {[llength $parents] > 0} {
+               foreach p $parents {
+                       lappend cmd -p $p
                }
-       }
-       if {$PARENT eq {}} {
+       } else {
                # git commit-tree writes to stderr during initial commit.
                lappend cmd 2>/dev/null
        }
@@ -1020,10 +1032,11 @@ proc commit_committree {fd_wt curHEAD msg} {
 
        # -- Update in memory status
        #
-       set commit_type normal
        set selected_commit_type new
+       set commit_type normal
        set HEAD $cmt_id
        set PARENT $cmt_id
+       set MERGE_HEAD [list]
 
        foreach path [array names file_states] {
                set s $file_states($path)
@@ -1081,8 +1094,8 @@ proc pull_remote {remote branch} {
 
        # -- Our in memory state should match the repository.
        #
-       repository_state curHEAD cur_type
-       if {$commit_type ne $cur_type || $HEAD ne $curHEAD} {
+       repository_state curType curHEAD curMERGE_HEAD
+       if {$commit_type ne $curType || $HEAD ne $curHEAD} {
                error_popup {Last scanned state does not match repository state.
 
 Its highly likely that another Git program modified the
@@ -1120,18 +1133,18 @@ Commit or throw away all changes before starting a pull operation.
 }
 
 proc post_pull_remote {remote branch success} {
-       global HEAD PARENT commit_type selected_commit_type
+       global HEAD PARENT MERGE_HEAD commit_type selected_commit_type
        global ui_status_value
 
        unlock_index
        if {$success} {
-               repository_state HEAD commit_type
+               repository_state commit_type HEAD MERGE_HEAD
                set PARENT $HEAD
                set selected_commit_type new
-               set $ui_status_value "Pulling $branch from $remote complete."
+               set ui_status_value "Pulling $branch from $remote complete."
        } else {
-               set m "Conflicts detected while pulling $branch from $remote."
-               rescan "set ui_status_value {$m}"
+               rescan [list set ui_status_value \
+                       "Conflicts detected while pulling $branch from $remote."]
        }
 }
 
@@ -2852,6 +2865,7 @@ proc trace_commit_type {varname args} {
        initial       {set txt {Initial Commit Message:}}
        amend         {set txt {Amended Commit Message:}}
        amend-initial {set txt {Amended Initial Commit Message:}}
+       amend-merge   {set txt {Amended Merge Commit Message:}}
        merge         {set txt {Merge Commit Message:}}
        *             {set txt {Commit Message:}}
        }
@@ -3146,6 +3160,7 @@ set file_lists($ui_other) [list]
 
 set HEAD {}
 set PARENT {}
+set MERGE_HEAD [list]
 set commit_type {}
 set empty_tree {}
 set current_diff {}