From d41b43eb4c73044d0fff2057211fc78e4e7b2094 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 8 Jul 2007 18:40:56 -0400 Subject: git-gui: Refactor branch switch to support detached head This is a major rewrite of the way we perform switching between branches and the subsequent update of the working directory. Like core Git we now use a single code path to perform all changes: our new checkout_op class. We also use it for branch creation/update as it integrates the tracking branch fetch process along with a very basic merge (fast-forward and reset only currently). Because some users have literally hundreds of local branches we use the standard revision picker (with its branch filtering tool) to select the local branch, rather than keeping all of the local branches in the Branch menu. The branch menu listing out all of the available branches is simply not sane for those types of huge repositories. Users can now checkout a detached head by ticking off the option in the checkout dialog. This option is off by default for the obvious reason, but it can be easily enabled for any local branch by simply checking it. We also detach the head if any non local branch was selected, or if a revision expression was entered. Signed-off-by: Shawn O. Pearce --- lib/branch_create.tcl | 247 ++++---------------------------------------------- 1 file changed, 18 insertions(+), 229 deletions(-) (limited to 'lib/branch_create.tcl') diff --git a/lib/branch_create.tcl b/lib/branch_create.tcl index f708957b22..def615d19d 100644 --- a/lib/branch_create.tcl +++ b/lib/branch_create.tcl @@ -73,7 +73,7 @@ constructor dialog {} { pack $w.options.merge.l -side left radiobutton $w.options.merge.no \ -text No \ - -value no \ + -value none \ -variable @opt_merge pack $w.options.merge.no -side left radiobutton $w.options.merge.ff \ @@ -113,7 +113,7 @@ constructor dialog {} { } method _create {} { - global repo_config current_branch + global repo_config global M1B set spec [$w_rev get_tracking_branch] @@ -155,17 +155,6 @@ method _create {} { return } - 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 \ @@ -178,228 +167,28 @@ method _create {} { } if {$spec ne {} && $opt_fetch} { - set l_trck [lindex $spec 0] - set remote [lindex $spec 1] - set r_head [lindex $spec 2] - regsub ^refs/heads/ $r_head {} r_head - - set c $w.fetch_trck - toplevel $c - wm title $c "Refreshing Tracking Branch" - wm geometry $c "+[winfo rootx $w]+[winfo rooty $w]" - - set e [::console::embed \ - $c.console \ - "Fetching $r_head from $remote"] - pack $c.console -fill both -expand 1 - $e exec \ - [list git fetch $remote +$r_head:$l_trck] \ - [cb _finish_fetch $newbranch $c $e] - - bind $c [list grab $c] - bind $c <$M1B-Key-w> break - bind $c <$M1B-Key-W> break - wm protocol $c WM_DELETE_WINDOW [cb _noop] - } else { - _finish_create $this $newbranch - } -} - -method _noop {} {} - -method _finish_fetch {newbranch c e ok} { - wm protocol $c WM_DELETE_WINDOW {} - - if {$ok} { - destroy $c - _finish_create $this $newbranch - } else { - $e done $ok - button $c.close -text Close -command [list destroy $c] - pack $c.close -side bottom -anchor e -padx 10 -pady 10 - } -} - -method _finish_create {newbranch} { - global null_sha1 all_heads - - if {[catch {set new [$w_rev commit_or_die]}]} { + set new {} + } elseif {[catch {set new [$w_rev commit_or_die]}]} { 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 \ - -icon error \ - -type ok \ - -title [wm title $w] \ - -parent $w \ - -message "Branch '$newbranch' already exists." - focus $w_name - return - } - } - } - - if {$new ne $cur} { - if {[catch { - git update-ref -m $reflog_msg $ref $new $cur - } err]} { - tk_messageBox \ - -icon error \ - -type ok \ - -title [wm title $w] \ - -parent $w \ - -message "Failed to create '$newbranch'.\n\n$err" - return - } - } - - if {$cur eq $null_sha1} { - lappend all_heads $newbranch - set all_heads [lsort -uniq $all_heads] - populate_branch_menu - } - - destroy $w - if {$opt_checkout} { - switch_branch $newbranch + set co [::checkout_op::new \ + [$w_rev get] \ + $new \ + refs/heads/$newbranch] + $co parent $w + $co enable_create 1 + $co enable_merge $opt_merge + $co enable_checkout $opt_checkout + if {$spec ne {} && $opt_fetch} { + $co enable_fetch $spec } -} - -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" + if {[$co run]} { + destroy $w + } else { + focus $w_name } - close $fd - $list configure -state disabled - - bind $c $gitk - - bind $c " - grab $c - focus $c.buttons.cancel - " - bind $c [list destroy $c] - bind $c [list destroy $c] - tkwait window $c - return $reset_ok } method _validate {d S} { -- cgit v1.2.3