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 ## status
proc repository_state {hdvar ctvar} { proc repository_state {ctvar hdvar mhvar} {
global gitdir 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]}]} { if {[catch {set hd [exec git rev-parse --verify HEAD]}]} {
set hd {} set hd {}
set ct initial set ct initial
} elseif {[file exists [file join $gitdir MERGE_HEAD]]} { return
set ct merge
} else {
set ct normal
} }
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 {} { proc PARENT {} {
global PARENT empty_tree global PARENT empty_tree
if {$PARENT ne {}} { set p [lindex $PARENT 0]
return $PARENT if {$p ne {}} {
return $p
} }
if {$empty_tree eq {}} { if {$empty_tree eq {}} {
set empty_tree [exec git mktree << {}] set empty_tree [exec git mktree << {}]
@ -258,21 +271,22 @@ proc PARENT {} {
} }
proc rescan {after} { 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 ui_index ui_other ui_status_value ui_comm
global rescan_active file_states global rescan_active file_states
global repo_config global repo_config
if {$rescan_active > 0 || ![lock_index read]} return 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] if {[string match amend* $commit_type]
&& $new_type eq {normal} && $newType eq {normal}
&& $new_HEAD eq $HEAD} { && $newHEAD eq $HEAD} {
} else { } else {
set HEAD $new_HEAD set HEAD $newHEAD
set PARENT $new_HEAD set PARENT $newHEAD
set commit_type $new_type set MERGE_HEAD $newMERGE_HEAD
set commit_type $newType
} }
array unset file_states array unset file_states
@ -686,23 +700,36 @@ proc read_diff {fd} {
## commit ## commit
proc load_last_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 {[llength $PARENT] == 0} {
if {$commit_type ne {normal}} { error_popup {There is nothing to amend.
error_popup "Can't amend a $commit_type commit."
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 return
} }
set msg {} set msg {}
set parent {} set parents [list]
set parent_count 0
if {[catch { 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} { while {[gets $fd line] > 0} {
if {[string match {parent *} $line]} { if {[string match {parent *} $line]} {
set parent [string range $line 7 end] lappend parents [string range $line 7 end]
incr parent_count
} }
} }
set msg [string trim [read $fd]] set msg [string trim [read $fd]]
@ -712,17 +739,13 @@ proc load_last_commit {} {
return return
} }
if {$parent_count > 1} { set HEAD $curHEAD
error_popup {Can't amend a merge commit.} set PARENT $parents
return set MERGE_HEAD [list]
} switch -- [llength $parents] {
0 {set commit_type amend-initial}
if {$parent_count == 0} { 1 {set commit_type amend}
set commit_type amend-initial default {set commit_type amend-merge}
set PARENT {}
} elseif {$parent_count == 1} {
set commit_type amend
set PARENT $parent
} }
$ui_comm delete 0.0 end $ui_comm delete 0.0 end
@ -770,11 +793,11 @@ proc commit_tree {} {
# -- Our in memory state should match the repository. # -- 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] if {[string match amend* $commit_type]
&& $cur_type eq {normal} && $curType eq {normal}
&& $curHEAD eq $HEAD} { && $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. info_popup {Last scanned state does not match repository state.
Another Git program has modified this repository Another Git program has modified this repository
@ -920,7 +943,8 @@ proc commit_writetree {curHEAD msg} {
} }
proc commit_committree {fd_wt 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 ui_status_value ui_comm selected_commit_type
global file_states selected_paths rescan_active global file_states selected_paths rescan_active
@ -935,24 +959,12 @@ proc commit_committree {fd_wt curHEAD msg} {
# -- Create the commit. # -- Create the commit.
# #
set cmd [list git commit-tree $tree_id] set cmd [list git commit-tree $tree_id]
if {$PARENT ne {}} { set parents [concat $PARENT $MERGE_HEAD]
lappend cmd -p $PARENT if {[llength $parents] > 0} {
} foreach p $parents {
if {$commit_type eq {merge}} { lappend cmd -p $p
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
} }
} } else {
if {$PARENT eq {}} {
# git commit-tree writes to stderr during initial commit. # git commit-tree writes to stderr during initial commit.
lappend cmd 2>/dev/null lappend cmd 2>/dev/null
} }
@ -1020,10 +1032,11 @@ proc commit_committree {fd_wt curHEAD msg} {
# -- Update in memory status # -- Update in memory status
# #
set commit_type normal
set selected_commit_type new set selected_commit_type new
set commit_type normal
set HEAD $cmt_id set HEAD $cmt_id
set PARENT $cmt_id set PARENT $cmt_id
set MERGE_HEAD [list]
foreach path [array names file_states] { foreach path [array names file_states] {
set s $file_states($path) set s $file_states($path)
@ -1081,8 +1094,8 @@ proc pull_remote {remote branch} {
# -- Our in memory state should match the repository. # -- Our in memory state should match the repository.
# #
repository_state curHEAD cur_type repository_state curType curHEAD curMERGE_HEAD
if {$commit_type ne $cur_type || $HEAD ne $curHEAD} { if {$commit_type ne $curType || $HEAD ne $curHEAD} {
error_popup {Last scanned state does not match repository state. error_popup {Last scanned state does not match repository state.
Its highly likely that another Git program modified the 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} { 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 global ui_status_value
unlock_index unlock_index
if {$success} { if {$success} {
repository_state HEAD commit_type repository_state commit_type HEAD MERGE_HEAD
set PARENT $HEAD set PARENT $HEAD
set selected_commit_type new set selected_commit_type new
set $ui_status_value "Pulling $branch from $remote complete." set ui_status_value "Pulling $branch from $remote complete."
} else { } else {
set m "Conflicts detected while pulling $branch from $remote." rescan [list set ui_status_value \
rescan "set ui_status_value {$m}" "Conflicts detected while pulling $branch from $remote."]
} }
} }
@ -2852,6 +2865,7 @@ proc trace_commit_type {varname args} {
initial {set txt {Initial Commit Message:}} initial {set txt {Initial Commit Message:}}
amend {set txt {Amended Commit Message:}} amend {set txt {Amended Commit Message:}}
amend-initial {set txt {Amended Initial Commit Message:}} amend-initial {set txt {Amended Initial Commit Message:}}
amend-merge {set txt {Amended Merge Commit Message:}}
merge {set txt {Merge Commit Message:}} merge {set txt {Merge Commit Message:}}
* {set txt {Commit Message:}} * {set txt {Commit Message:}}
} }
@ -3146,6 +3160,7 @@ set file_lists($ui_other) [list]
set HEAD {} set HEAD {}
set PARENT {} set PARENT {}
set MERGE_HEAD [list]
set commit_type {} set commit_type {}
set empty_tree {} set empty_tree {}
set current_diff {} set current_diff {}