lib / branch_create.tclon commit git-gui: Fast-forward existing branch in branch create dialog (774173a)
   1# git-gui branch create support
   2# Copyright (C) 2006, 2007 Shawn Pearce
   3
   4class branch_create {
   5
   6field w              ; # widget path
   7field w_rev          ; # mega-widget to pick the initial revision
   8field w_name         ; # new branch name widget
   9
  10field name         {}; # name of the branch the user has chosen
  11field name_type  user; # type of branch name to use
  12
  13field opt_merge    ff; # type of merge to apply to existing branch
  14field opt_checkout  1; # automatically checkout the new branch?
  15field reset_ok      0; # did the user agree to reset?
  16
  17constructor dialog {} {
  18        global repo_config
  19
  20        make_toplevel top w
  21        wm title $top "[appname] ([reponame]): Create Branch"
  22        if {$top ne {.}} {
  23                wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
  24        }
  25
  26        label $w.header -text {Create New Branch} -font font_uibold
  27        pack $w.header -side top -fill x
  28
  29        frame $w.buttons
  30        button $w.buttons.create -text Create \
  31                -default active \
  32                -command [cb _create]
  33        pack $w.buttons.create -side right
  34        button $w.buttons.cancel -text {Cancel} \
  35                -command [list destroy $w]
  36        pack $w.buttons.cancel -side right -padx 5
  37        pack $w.buttons -side bottom -fill x -pady 10 -padx 10
  38
  39        labelframe $w.desc -text {Branch Name}
  40        radiobutton $w.desc.name_r \
  41                -anchor w \
  42                -text {Name:} \
  43                -value user \
  44                -variable @name_type
  45        set w_name $w.desc.name_t
  46        entry $w_name \
  47                -borderwidth 1 \
  48                -relief sunken \
  49                -width 40 \
  50                -textvariable @name \
  51                -validate key \
  52                -validatecommand [cb _validate %d %S]
  53        grid $w.desc.name_r $w_name -sticky we -padx {0 5}
  54
  55        radiobutton $w.desc.match_r \
  56                -anchor w \
  57                -text {Match Tracking Branch Name} \
  58                -value match \
  59                -variable @name_type
  60        grid $w.desc.match_r -sticky we -padx {0 5} -columnspan 2
  61
  62        grid columnconfigure $w.desc 1 -weight 1
  63        pack $w.desc -anchor nw -fill x -pady 5 -padx 5
  64
  65        set w_rev [::choose_rev::new $w.rev {Starting Revision}]
  66        pack $w.rev -anchor nw -fill x -pady 5 -padx 5
  67
  68        labelframe $w.options -text {Options}
  69
  70        frame $w.options.merge
  71        label $w.options.merge.l -text {Update Existing Branch:}
  72        pack $w.options.merge.l -side left
  73        radiobutton $w.options.merge.no \
  74                -text No \
  75                -value no \
  76                -variable @opt_merge
  77        pack $w.options.merge.no -side left
  78        radiobutton $w.options.merge.ff \
  79                -text {Fast Forward Only} \
  80                -value ff \
  81                -variable @opt_merge
  82        pack $w.options.merge.ff -side left
  83        radiobutton $w.options.merge.reset \
  84                -text {Reset} \
  85                -value reset \
  86                -variable @opt_merge
  87        pack $w.options.merge.reset -side left
  88        pack $w.options.merge -anchor nw
  89
  90        checkbutton $w.options.checkout \
  91                -text {Checkout After Creation} \
  92                -variable @opt_checkout
  93        pack $w.options.checkout -anchor nw
  94        pack $w.options -anchor nw -fill x -pady 5 -padx 5
  95
  96        set name $repo_config(gui.newbranchtemplate)
  97
  98        bind $w <Visibility> "
  99                grab $w
 100                $w_name icursor end
 101                focus $w_name
 102        "
 103        bind $w <Key-Escape> [list destroy $w]
 104        bind $w <Key-Return> [cb _create]\;break
 105        tkwait window $w
 106}
 107
 108method _create {} {
 109        global null_sha1 repo_config
 110        global all_heads current_branch
 111
 112        switch -- $name_type {
 113        user {
 114                set newbranch $name
 115        }
 116        match {
 117                set spec [$w_rev get_tracking_branch]
 118                if {$spec eq {}} {
 119                        tk_messageBox \
 120                                -icon error \
 121                                -type ok \
 122                                -title [wm title $w] \
 123                                -parent $w \
 124                                -message "Please select a tracking branch."
 125                        return
 126                }
 127                if {![regsub ^refs/heads/ [lindex $spec 2] {} newbranch]} {
 128                        tk_messageBox \
 129                                -icon error \
 130                                -type ok \
 131                                -title [wm title $w] \
 132                                -parent $w \
 133                                -message "Tracking branch [$w get] is not a branch in the remote repository."
 134                        return
 135                }
 136        }
 137        }
 138
 139        if {$newbranch eq {}
 140                || $newbranch eq $repo_config(gui.newbranchtemplate)} {
 141                tk_messageBox \
 142                        -icon error \
 143                        -type ok \
 144                        -title [wm title $w] \
 145                        -parent $w \
 146                        -message "Please supply a branch name."
 147                focus $w_name
 148                return
 149        }
 150
 151        if {$newbranch eq $current_branch} {
 152                tk_messageBox \
 153                        -icon error \
 154                        -type ok \
 155                        -title [wm title $w] \
 156                        -parent $w \
 157                        -message "'$newbranch' already exists and is the current branch."
 158                focus $w_name
 159                return
 160        }
 161
 162        if {[catch {git check-ref-format "heads/$newbranch"}]} {
 163                tk_messageBox \
 164                        -icon error \
 165                        -type ok \
 166                        -title [wm title $w] \
 167                        -parent $w \
 168                        -message "'$newbranch' is not an acceptable branch name."
 169                focus $w_name
 170                return
 171        }
 172
 173        if {[catch {set new [$w_rev get_commit]}]} {
 174                tk_messageBox \
 175                        -icon error \
 176                        -type ok \
 177                        -title [wm title $w] \
 178                        -parent $w \
 179                        -message "Invalid revision: [$w_rev get]"
 180                return
 181        }
 182
 183        set ref refs/heads/$newbranch
 184        if {[catch {set cur [git rev-parse --verify "$ref^0"]}]} {
 185                # Assume it does not exist, and that is what the error was.
 186                #
 187                set reflog_msg "branch: Created from [$w_rev get]"
 188                set cur $null_sha1
 189        } elseif {$opt_merge eq {no}} {
 190                tk_messageBox \
 191                        -icon error \
 192                        -type ok \
 193                        -title [wm title $w] \
 194                        -parent $w \
 195                        -message "Branch '$newbranch' already exists."
 196                focus $w_name
 197                return
 198        } else {
 199                set mrb {}
 200                catch {set mrb [git merge-base $new $cur]}
 201                switch -- $opt_merge {
 202                ff {
 203                        if {$mrb eq $new} {
 204                                # The current branch is actually newer.
 205                                #
 206                                set new $cur
 207                        } elseif {$mrb eq $cur} {
 208                                # The current branch is older.
 209                                #
 210                                set reflog_msg "merge [$w_rev get]: Fast-forward"
 211                        } else {
 212                                tk_messageBox \
 213                                        -icon error \
 214                                        -type ok \
 215                                        -title [wm title $w] \
 216                                        -parent $w \
 217                                        -message "Branch '$newbranch' already exists.\n\nIt cannot fast-forward to [$w_rev get].\nA merge is required."
 218                                focus $w_name
 219                                return
 220                        }
 221                }
 222                reset {
 223                        if {$mrb eq $cur} {
 224                                # The current branch is older.
 225                                #
 226                                set reflog_msg "merge [$w_rev get]: Fast-forward"
 227                        } else {
 228                                # The current branch will lose things.
 229                                #
 230                                if {[_confirm_reset $this $newbranch $cur $new]} {
 231                                        set reflog_msg "reset [$w_rev get]"
 232                                } else {
 233                                        return
 234                                }
 235                        }
 236                }
 237                default {
 238                        tk_messageBox \
 239                                -icon error \
 240                                -type ok \
 241                                -title [wm title $w] \
 242                                -parent $w \
 243                                -message "Branch '$newbranch' already exists."
 244                        focus $w_name
 245                        return
 246                }
 247                }
 248        }
 249
 250        if {$new ne $cur} {
 251                if {[catch {
 252                                git update-ref -m $reflog_msg $ref $new $cur
 253                        } err]} {
 254                        tk_messageBox \
 255                                -icon error \
 256                                -type ok \
 257                                -title [wm title $w] \
 258                                -parent $w \
 259                                -message "Failed to create '$newbranch'.\n\n$err"
 260                        return
 261                }
 262        }
 263
 264        if {$cur eq $null_sha1} {
 265                lappend all_heads $newbranch
 266                set all_heads [lsort -uniq $all_heads]
 267                populate_branch_menu
 268        }
 269
 270        destroy $w
 271        if {$opt_checkout} {
 272                switch_branch $newbranch
 273        }
 274}
 275
 276method _confirm_reset {newbranch cur new} {
 277        set reset_ok 0
 278        set gitk [list do_gitk [list $cur ^$new]]
 279
 280        set c $w.confirm_reset
 281        toplevel $c
 282        wm title $c "Confirm Branch Reset"
 283        wm geometry $c "+[winfo rootx $w]+[winfo rooty $w]"
 284
 285        pack [label $c.msg1 \
 286                -anchor w \
 287                -justify left \
 288                -text "Resetting '$newbranch' to [$w_rev get] will lose the following commits:" \
 289                ] -anchor w
 290
 291        set list $c.list.l
 292        frame $c.list
 293        text $list \
 294                -font font_diff \
 295                -width 80 \
 296                -height 10 \
 297                -wrap none \
 298                -xscrollcommand [list $c.list.sbx set] \
 299                -yscrollcommand [list $c.list.sby set]
 300        scrollbar $c.list.sbx -orient h -command [list $list xview]
 301        scrollbar $c.list.sby -orient v -command [list $list yview]
 302        pack $c.list.sbx -fill x -side bottom
 303        pack $c.list.sby -fill y -side right
 304        pack $list -fill both -expand 1
 305        pack $c.list -fill both -expand 1 -padx 5 -pady 5
 306
 307        pack [label $c.msg2 \
 308                -anchor w \
 309                -justify left \
 310                -text "Recovering lost commits may not be easy." \
 311                ]
 312        pack [label $c.msg3 \
 313                -anchor w \
 314                -justify left \
 315                -text "Reset '$newbranch'?" \
 316                ]
 317
 318        frame $c.buttons
 319        button $c.buttons.visualize \
 320                -text Visualize \
 321                -command $gitk
 322        pack $c.buttons.visualize -side left
 323        button $c.buttons.reset \
 324                -text Reset \
 325                -command "
 326                        set @reset_ok 1
 327                        destroy $c
 328                "
 329        pack $c.buttons.reset -side right
 330        button $c.buttons.cancel \
 331                -default active \
 332                -text Cancel \
 333                -command [list destroy $c]
 334        pack $c.buttons.cancel -side right -padx 5
 335        pack $c.buttons -side bottom -fill x -pady 10 -padx 10
 336
 337        set fd [open "| git rev-list --pretty=oneline $cur ^$new" r]
 338        while {[gets $fd line] > 0} {
 339                set abbr [string range $line 0 7]
 340                set subj [string range $line 41 end]
 341                $list insert end "$abbr  $subj\n"
 342        }
 343        close $fd
 344        $list configure -state disabled
 345
 346        bind $c    <Key-v> $gitk
 347
 348        bind $c <Visibility> "
 349                grab $c
 350                focus $c.buttons.cancel
 351        "
 352        bind $c <Key-Return> [list destroy $c]
 353        bind $c <Key-Escape> [list destroy $c]
 354        tkwait window $c
 355        return $reset_ok
 356}
 357
 358method _validate {d S} {
 359        if {$d == 1} {
 360                if {[regexp {[~^:?*\[\0- ]} $S]} {
 361                        return 0
 362                }
 363                if {[string length $S] > 0} {
 364                        set name_type user
 365                }
 366        }
 367        return 1
 368}
 369
 370}