git-gui: Fast-forward existing branch in branch create dialog

If the user elects to create a local branch that has the same name
as an existing branch and we can fast-forward the local branch to
the selected revision we might as well do the fast-forward for the
user, rather than making them first switch to the branch then merge
the selected revision into it.  After all, its really just a fast
forward.  No history is lost.  The resulting branch checkout may
also be faster if the branch we are switching from is closer to
the new revision.

Likewise we also now allow the user to reset the local branch if
it already exists but would not fast-forward.  However before we
do the actual reset we tell the user what commits they are going to
lose by showing the oneline subject and abbreviated sha1, and we also
let them inspect the range of commits in gitk.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
This commit is contained in:
Shawn O. Pearce 2007-07-04 04:21:57 -04:00
parent dd87efc8cc
commit 774173aa5f

View File

@ -10,7 +10,9 @@ field w_name ; # new branch name widget
field name {}; # name of the branch the user has chosen field name {}; # name of the branch the user has chosen
field name_type user; # type of branch name to use field name_type user; # type of branch name to use
field opt_merge ff; # type of merge to apply to existing branch
field opt_checkout 1; # automatically checkout the new branch? field opt_checkout 1; # automatically checkout the new branch?
field reset_ok 0; # did the user agree to reset?
constructor dialog {} { constructor dialog {} {
global repo_config global repo_config
@ -63,12 +65,33 @@ constructor dialog {} {
set w_rev [::choose_rev::new $w.rev {Starting Revision}] set w_rev [::choose_rev::new $w.rev {Starting Revision}]
pack $w.rev -anchor nw -fill x -pady 5 -padx 5 pack $w.rev -anchor nw -fill x -pady 5 -padx 5
labelframe $w.postActions -text {Post Creation Actions} labelframe $w.options -text {Options}
checkbutton $w.postActions.checkout \
-text {Checkout after creation} \ frame $w.options.merge
label $w.options.merge.l -text {Update Existing Branch:}
pack $w.options.merge.l -side left
radiobutton $w.options.merge.no \
-text No \
-value no \
-variable @opt_merge
pack $w.options.merge.no -side left
radiobutton $w.options.merge.ff \
-text {Fast Forward Only} \
-value ff \
-variable @opt_merge
pack $w.options.merge.ff -side left
radiobutton $w.options.merge.reset \
-text {Reset} \
-value reset \
-variable @opt_merge
pack $w.options.merge.reset -side left
pack $w.options.merge -anchor nw
checkbutton $w.options.checkout \
-text {Checkout After Creation} \
-variable @opt_checkout -variable @opt_checkout
pack $w.postActions.checkout -anchor nw pack $w.options.checkout -anchor nw
pack $w.postActions -anchor nw -fill x -pady 5 -padx 5 pack $w.options -anchor nw -fill x -pady 5 -padx 5
set name $repo_config(gui.newbranchtemplate) set name $repo_config(gui.newbranchtemplate)
@ -84,7 +107,7 @@ constructor dialog {} {
method _create {} { method _create {} {
global null_sha1 repo_config global null_sha1 repo_config
global all_heads global all_heads current_branch
switch -- $name_type { switch -- $name_type {
user { user {
@ -124,7 +147,94 @@ method _create {} {
focus $w_name focus $w_name
return return
} }
if {![catch {git show-ref --verify -- "refs/heads/$newbranch"}]} {
if {$newbranch eq $current_branch} {
tk_messageBox \
-icon error \
-type ok \
-title [wm title $w] \
-parent $w \
-message "'$newbranch' already exists and is the current branch."
focus $w_name
return
}
if {[catch {git check-ref-format "heads/$newbranch"}]} {
tk_messageBox \
-icon error \
-type ok \
-title [wm title $w] \
-parent $w \
-message "'$newbranch' is not an acceptable branch name."
focus $w_name
return
}
if {[catch {set new [$w_rev get_commit]}]} {
tk_messageBox \
-icon error \
-type ok \
-title [wm title $w] \
-parent $w \
-message "Invalid revision: [$w_rev get]"
return
}
set ref refs/heads/$newbranch
if {[catch {set cur [git rev-parse --verify "$ref^0"]}]} {
# Assume it does not exist, and that is what the error was.
#
set reflog_msg "branch: Created from [$w_rev get]"
set cur $null_sha1
} elseif {$opt_merge eq {no}} {
tk_messageBox \
-icon error \
-type ok \
-title [wm title $w] \
-parent $w \
-message "Branch '$newbranch' already exists."
focus $w_name
return
} else {
set mrb {}
catch {set mrb [git merge-base $new $cur]}
switch -- $opt_merge {
ff {
if {$mrb eq $new} {
# The current branch is actually newer.
#
set new $cur
} elseif {$mrb eq $cur} {
# The current branch is older.
#
set reflog_msg "merge [$w_rev get]: Fast-forward"
} else {
tk_messageBox \
-icon error \
-type ok \
-title [wm title $w] \
-parent $w \
-message "Branch '$newbranch' already exists.\n\nIt cannot fast-forward to [$w_rev get].\nA merge is required."
focus $w_name
return
}
}
reset {
if {$mrb eq $cur} {
# The current branch is older.
#
set reflog_msg "merge [$w_rev get]: Fast-forward"
} else {
# The current branch will lose things.
#
if {[_confirm_reset $this $newbranch $cur $new]} {
set reflog_msg "reset [$w_rev get]"
} else {
return
}
}
}
default {
tk_messageBox \ tk_messageBox \
-icon error \ -icon error \
-type ok \ -type ok \
@ -134,32 +244,12 @@ method _create {} {
focus $w_name focus $w_name
return return
} }
if {[catch {git check-ref-format "heads/$newbranch"}]} { }
tk_messageBox \
-icon error \
-type ok \
-title [wm title $w] \
-parent $w \
-message "We do not like '$newbranch' as a branch name."
focus $w_name
return
} }
if {[catch {set cmt [$w_rev get_commit]}]} { if {$new ne $cur} {
tk_messageBox \
-icon error \
-type ok \
-title [wm title $w] \
-parent $w \
-message "Invalid starting revision: [$w_rev get]"
return
}
if {[catch { if {[catch {
git update-ref \ git update-ref -m $reflog_msg $ref $new $cur
-m "branch: Created from [$w_rev get]" \
"refs/heads/$newbranch" \
$cmt \
$null_sha1
} err]} { } err]} {
tk_messageBox \ tk_messageBox \
-icon error \ -icon error \
@ -169,16 +259,102 @@ method _create {} {
-message "Failed to create '$newbranch'.\n\n$err" -message "Failed to create '$newbranch'.\n\n$err"
return return
} }
}
if {$cur eq $null_sha1} {
lappend all_heads $newbranch lappend all_heads $newbranch
set all_heads [lsort $all_heads] set all_heads [lsort -uniq $all_heads]
populate_branch_menu populate_branch_menu
}
destroy $w destroy $w
if {$opt_checkout} { if {$opt_checkout} {
switch_branch $newbranch switch_branch $newbranch
} }
} }
method _confirm_reset {newbranch cur new} {
set reset_ok 0
set gitk [list do_gitk [list $cur ^$new]]
set c $w.confirm_reset
toplevel $c
wm title $c "Confirm Branch Reset"
wm geometry $c "+[winfo rootx $w]+[winfo rooty $w]"
pack [label $c.msg1 \
-anchor w \
-justify left \
-text "Resetting '$newbranch' to [$w_rev get] will lose the following commits:" \
] -anchor w
set list $c.list.l
frame $c.list
text $list \
-font font_diff \
-width 80 \
-height 10 \
-wrap none \
-xscrollcommand [list $c.list.sbx set] \
-yscrollcommand [list $c.list.sby set]
scrollbar $c.list.sbx -orient h -command [list $list xview]
scrollbar $c.list.sby -orient v -command [list $list yview]
pack $c.list.sbx -fill x -side bottom
pack $c.list.sby -fill y -side right
pack $list -fill both -expand 1
pack $c.list -fill both -expand 1 -padx 5 -pady 5
pack [label $c.msg2 \
-anchor w \
-justify left \
-text "Recovering lost commits may not be easy." \
]
pack [label $c.msg3 \
-anchor w \
-justify left \
-text "Reset '$newbranch'?" \
]
frame $c.buttons
button $c.buttons.visualize \
-text Visualize \
-command $gitk
pack $c.buttons.visualize -side left
button $c.buttons.reset \
-text Reset \
-command "
set @reset_ok 1
destroy $c
"
pack $c.buttons.reset -side right
button $c.buttons.cancel \
-default active \
-text Cancel \
-command [list destroy $c]
pack $c.buttons.cancel -side right -padx 5
pack $c.buttons -side bottom -fill x -pady 10 -padx 10
set fd [open "| git rev-list --pretty=oneline $cur ^$new" r]
while {[gets $fd line] > 0} {
set abbr [string range $line 0 7]
set subj [string range $line 41 end]
$list insert end "$abbr $subj\n"
}
close $fd
$list configure -state disabled
bind $c <Key-v> $gitk
bind $c <Visibility> "
grab $c
focus $c.buttons.cancel
"
bind $c <Key-Return> [list destroy $c]
bind $c <Key-Escape> [list destroy $c]
tkwait window $c
return $reset_ok
}
method _validate {d S} { method _validate {d S} {
if {$d == 1} { if {$d == 1} {
if {[regexp {[~^:?*\[\0- ]} $S]} { if {[regexp {[~^:?*\[\0- ]} $S]} {