git-gui: Improve handling of merge commits.

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>
This commit is contained in:
Shawn O. Pearce 2006-11-20 21:27:22 -05:00
parent 375f38828e
commit f18e40a1a6

143
git-gui
View File

@ -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]]} {
set ct merge
} else {
set ct normal
return
}
set merge_head [file join $gitdir MERGE_HEAD]
if {[file exists $merge_head]} {
set ct merge
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 {}