375f575eaf4884d735e21bcb34ab36a75d28f512
   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 both -expand 1 -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 commit_or_die]}]} {
 174                return
 175        }
 176
 177        set ref refs/heads/$newbranch
 178        if {[catch {set cur [git rev-parse --verify "$ref^0"]}]} {
 179                # Assume it does not exist, and that is what the error was.
 180                #
 181                set reflog_msg "branch: Created from [$w_rev get]"
 182                set cur $null_sha1
 183        } elseif {$opt_merge eq {no}} {
 184                tk_messageBox \
 185                        -icon error \
 186                        -type ok \
 187                        -title [wm title $w] \
 188                        -parent $w \
 189                        -message "Branch '$newbranch' already exists."
 190                focus $w_name
 191                return
 192        } else {
 193                set mrb {}
 194                catch {set mrb [git merge-base $new $cur]}
 195                switch -- $opt_merge {
 196                ff {
 197                        if {$mrb eq $new} {
 198                                # The current branch is actually newer.
 199                                #
 200                                set new $cur
 201                        } elseif {$mrb eq $cur} {
 202                                # The current branch is older.
 203                                #
 204                                set reflog_msg "merge [$w_rev get]: Fast-forward"
 205                        } else {
 206                                tk_messageBox \
 207                                        -icon error \
 208                                        -type ok \
 209                                        -title [wm title $w] \
 210                                        -parent $w \
 211                                        -message "Branch '$newbranch' already exists.\n\nIt cannot fast-forward to [$w_rev get].\nA merge is required."
 212                                focus $w_name
 213                                return
 214                        }
 215                }
 216                reset {
 217                        if {$mrb eq $cur} {
 218                                # The current branch is older.
 219                                #
 220                                set reflog_msg "merge [$w_rev get]: Fast-forward"
 221                        } else {
 222                                # The current branch will lose things.
 223                                #
 224                                if {[_confirm_reset $this $newbranch $cur $new]} {
 225                                        set reflog_msg "reset [$w_rev get]"
 226                                } else {
 227                                        return
 228                                }
 229                        }
 230                }
 231                default {
 232                        tk_messageBox \
 233                                -icon error \
 234                                -type ok \
 235                                -title [wm title $w] \
 236                                -parent $w \
 237                                -message "Branch '$newbranch' already exists."
 238                        focus $w_name
 239                        return
 240                }
 241                }
 242        }
 243
 244        if {$new ne $cur} {
 245                if {[catch {
 246                                git update-ref -m $reflog_msg $ref $new $cur
 247                        } err]} {
 248                        tk_messageBox \
 249                                -icon error \
 250                                -type ok \
 251                                -title [wm title $w] \
 252                                -parent $w \
 253                                -message "Failed to create '$newbranch'.\n\n$err"
 254                        return
 255                }
 256        }
 257
 258        if {$cur eq $null_sha1} {
 259                lappend all_heads $newbranch
 260                set all_heads [lsort -uniq $all_heads]
 261                populate_branch_menu
 262        }
 263
 264        destroy $w
 265        if {$opt_checkout} {
 266                switch_branch $newbranch
 267        }
 268}
 269
 270method _confirm_reset {newbranch cur new} {
 271        set reset_ok 0
 272        set gitk [list do_gitk [list $cur ^$new]]
 273
 274        set c $w.confirm_reset
 275        toplevel $c
 276        wm title $c "Confirm Branch Reset"
 277        wm geometry $c "+[winfo rootx $w]+[winfo rooty $w]"
 278
 279        pack [label $c.msg1 \
 280                -anchor w \
 281                -justify left \
 282                -text "Resetting '$newbranch' to [$w_rev get] will lose the following commits:" \
 283                ] -anchor w
 284
 285        set list $c.list.l
 286        frame $c.list
 287        text $list \
 288                -font font_diff \
 289                -width 80 \
 290                -height 10 \
 291                -wrap none \
 292                -xscrollcommand [list $c.list.sbx set] \
 293                -yscrollcommand [list $c.list.sby set]
 294        scrollbar $c.list.sbx -orient h -command [list $list xview]
 295        scrollbar $c.list.sby -orient v -command [list $list yview]
 296        pack $c.list.sbx -fill x -side bottom
 297        pack $c.list.sby -fill y -side right
 298        pack $list -fill both -expand 1
 299        pack $c.list -fill both -expand 1 -padx 5 -pady 5
 300
 301        pack [label $c.msg2 \
 302                -anchor w \
 303                -justify left \
 304                -text "Recovering lost commits may not be easy." \
 305                ]
 306        pack [label $c.msg3 \
 307                -anchor w \
 308                -justify left \
 309                -text "Reset '$newbranch'?" \
 310                ]
 311
 312        frame $c.buttons
 313        button $c.buttons.visualize \
 314                -text Visualize \
 315                -command $gitk
 316        pack $c.buttons.visualize -side left
 317        button $c.buttons.reset \
 318                -text Reset \
 319                -command "
 320                        set @reset_ok 1
 321                        destroy $c
 322                "
 323        pack $c.buttons.reset -side right
 324        button $c.buttons.cancel \
 325                -default active \
 326                -text Cancel \
 327                -command [list destroy $c]
 328        pack $c.buttons.cancel -side right -padx 5
 329        pack $c.buttons -side bottom -fill x -pady 10 -padx 10
 330
 331        set fd [open "| git rev-list --pretty=oneline $cur ^$new" r]
 332        while {[gets $fd line] > 0} {
 333                set abbr [string range $line 0 7]
 334                set subj [string range $line 41 end]
 335                $list insert end "$abbr  $subj\n"
 336        }
 337        close $fd
 338        $list configure -state disabled
 339
 340        bind $c    <Key-v> $gitk
 341
 342        bind $c <Visibility> "
 343                grab $c
 344                focus $c.buttons.cancel
 345        "
 346        bind $c <Key-Return> [list destroy $c]
 347        bind $c <Key-Escape> [list destroy $c]
 348        tkwait window $c
 349        return $reset_ok
 350}
 351
 352method _validate {d S} {
 353        if {$d == 1} {
 354                if {[regexp {[~^:?*\[\0- ]} $S]} {
 355                        return 0
 356                }
 357                if {[string length $S] > 0} {
 358                        set name_type user
 359                }
 360        }
 361        return 1
 362}
 363
 364}