git-gui: Improve handling of merge commits.
[gitweb.git] / 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 {}