df3f435e11a13e6d1e812909bc0277ac6e1add2f
   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        trace add variable @name_type write [cb _select]
  97
  98        set name $repo_config(gui.newbranchtemplate)
  99        if {[is_config_true gui.matchtrackingbranch]} {
 100                set name_type match
 101        }
 102
 103        bind $w <Visibility> [cb _visible]
 104        bind $w <Key-Escape> [list destroy $w]
 105        bind $w <Key-Return> [cb _create]\;break
 106        tkwait window $w
 107}
 108
 109method _create {} {
 110        global null_sha1 repo_config
 111        global all_heads current_branch
 112
 113        switch -- $name_type {
 114        user {
 115                set newbranch $name
 116        }
 117        match {
 118                set spec [$w_rev get_tracking_branch]
 119                if {$spec eq {}} {
 120                        tk_messageBox \
 121                                -icon error \
 122                                -type ok \
 123                                -title [wm title $w] \
 124                                -parent $w \
 125                                -message "Please select a tracking branch."
 126                        return
 127                }
 128                if {![regsub ^refs/heads/ [lindex $spec 2] {} newbranch]} {
 129                        tk_messageBox \
 130                                -icon error \
 131                                -type ok \
 132                                -title [wm title $w] \
 133                                -parent $w \
 134                                -message "Tracking branch [$w get] is not a branch in the remote repository."
 135                        return
 136                }
 137        }
 138        }
 139
 140        if {$newbranch eq {}
 141                || $newbranch eq $repo_config(gui.newbranchtemplate)} {
 142                tk_messageBox \
 143                        -icon error \
 144                        -type ok \
 145                        -title [wm title $w] \
 146                        -parent $w \
 147                        -message "Please supply a branch name."
 148                focus $w_name
 149                return
 150        }
 151
 152        if {$newbranch eq $current_branch} {
 153                tk_messageBox \
 154                        -icon error \
 155                        -type ok \
 156                        -title [wm title $w] \
 157                        -parent $w \
 158                        -message "'$newbranch' already exists and is the current branch."
 159                focus $w_name
 160                return
 161        }
 162
 163        if {[catch {git check-ref-format "heads/$newbranch"}]} {
 164                tk_messageBox \
 165                        -icon error \
 166                        -type ok \
 167                        -title [wm title $w] \
 168                        -parent $w \
 169                        -message "'$newbranch' is not an acceptable branch name."
 170                focus $w_name
 171                return
 172        }
 173
 174        if {[catch {set new [$w_rev commit_or_die]}]} {
 175                return
 176        }
 177
 178        set ref refs/heads/$newbranch
 179        if {[catch {set cur [git rev-parse --verify "$ref^0"]}]} {
 180                # Assume it does not exist, and that is what the error was.
 181                #
 182                set reflog_msg "branch: Created from [$w_rev get]"
 183                set cur $null_sha1
 184        } elseif {$opt_merge eq {no}} {
 185                tk_messageBox \
 186                        -icon error \
 187                        -type ok \
 188                        -title [wm title $w] \
 189                        -parent $w \
 190                        -message "Branch '$newbranch' already exists."
 191                focus $w_name
 192                return
 193        } else {
 194                set mrb {}
 195                catch {set mrb [git merge-base $new $cur]}
 196                switch -- $opt_merge {
 197                ff {
 198                        if {$mrb eq $new} {
 199                                # The current branch is actually newer.
 200                                #
 201                                set new $cur
 202                        } elseif {$mrb eq $cur} {
 203                                # The current branch is older.
 204                                #
 205                                set reflog_msg "merge [$w_rev get]: Fast-forward"
 206                        } else {
 207                                tk_messageBox \
 208                                        -icon error \
 209                                        -type ok \
 210                                        -title [wm title $w] \
 211                                        -parent $w \
 212                                        -message "Branch '$newbranch' already exists.\n\nIt cannot fast-forward to [$w_rev get].\nA merge is required."
 213                                focus $w_name
 214                                return
 215                        }
 216                }
 217                reset {
 218                        if {$mrb eq $cur} {
 219                                # The current branch is older.
 220                                #
 221                                set reflog_msg "merge [$w_rev get]: Fast-forward"
 222                        } else {
 223                                # The current branch will lose things.
 224                                #
 225                                if {[_confirm_reset $this $newbranch $cur $new]} {
 226                                        set reflog_msg "reset [$w_rev get]"
 227                                } else {
 228                                        return
 229                                }
 230                        }
 231                }
 232                default {
 233                        tk_messageBox \
 234                                -icon error \
 235                                -type ok \
 236                                -title [wm title $w] \
 237                                -parent $w \
 238                                -message "Branch '$newbranch' already exists."
 239                        focus $w_name
 240                        return
 241                }
 242                }
 243        }
 244
 245        if {$new ne $cur} {
 246                if {[catch {
 247                                git update-ref -m $reflog_msg $ref $new $cur
 248                        } err]} {
 249                        tk_messageBox \
 250                                -icon error \
 251                                -type ok \
 252                                -title [wm title $w] \
 253                                -parent $w \
 254                                -message "Failed to create '$newbranch'.\n\n$err"
 255                        return
 256                }
 257        }
 258
 259        if {$cur eq $null_sha1} {
 260                lappend all_heads $newbranch
 261                set all_heads [lsort -uniq $all_heads]
 262                populate_branch_menu
 263        }
 264
 265        destroy $w
 266        if {$opt_checkout} {
 267                switch_branch $newbranch
 268        }
 269}
 270
 271method _confirm_reset {newbranch cur new} {
 272        set reset_ok 0
 273        set gitk [list do_gitk [list $cur ^$new]]
 274
 275        set c $w.confirm_reset
 276        toplevel $c
 277        wm title $c "Confirm Branch Reset"
 278        wm geometry $c "+[winfo rootx $w]+[winfo rooty $w]"
 279
 280        pack [label $c.msg1 \
 281                -anchor w \
 282                -justify left \
 283                -text "Resetting '$newbranch' to [$w_rev get] will lose the following commits:" \
 284                ] -anchor w
 285
 286        set list $c.list.l
 287        frame $c.list
 288        text $list \
 289                -font font_diff \
 290                -width 80 \
 291                -height 10 \
 292                -wrap none \
 293                -xscrollcommand [list $c.list.sbx set] \
 294                -yscrollcommand [list $c.list.sby set]
 295        scrollbar $c.list.sbx -orient h -command [list $list xview]
 296        scrollbar $c.list.sby -orient v -command [list $list yview]
 297        pack $c.list.sbx -fill x -side bottom
 298        pack $c.list.sby -fill y -side right
 299        pack $list -fill both -expand 1
 300        pack $c.list -fill both -expand 1 -padx 5 -pady 5
 301
 302        pack [label $c.msg2 \
 303                -anchor w \
 304                -justify left \
 305                -text "Recovering lost commits may not be easy." \
 306                ]
 307        pack [label $c.msg3 \
 308                -anchor w \
 309                -justify left \
 310                -text "Reset '$newbranch'?" \
 311                ]
 312
 313        frame $c.buttons
 314        button $c.buttons.visualize \
 315                -text Visualize \
 316                -command $gitk
 317        pack $c.buttons.visualize -side left
 318        button $c.buttons.reset \
 319                -text Reset \
 320                -command "
 321                        set @reset_ok 1
 322                        destroy $c
 323                "
 324        pack $c.buttons.reset -side right
 325        button $c.buttons.cancel \
 326                -default active \
 327                -text Cancel \
 328                -command [list destroy $c]
 329        pack $c.buttons.cancel -side right -padx 5
 330        pack $c.buttons -side bottom -fill x -pady 10 -padx 10
 331
 332        set fd [open "| git rev-list --pretty=oneline $cur ^$new" r]
 333        while {[gets $fd line] > 0} {
 334                set abbr [string range $line 0 7]
 335                set subj [string range $line 41 end]
 336                $list insert end "$abbr  $subj\n"
 337        }
 338        close $fd
 339        $list configure -state disabled
 340
 341        bind $c    <Key-v> $gitk
 342
 343        bind $c <Visibility> "
 344                grab $c
 345                focus $c.buttons.cancel
 346        "
 347        bind $c <Key-Return> [list destroy $c]
 348        bind $c <Key-Escape> [list destroy $c]
 349        tkwait window $c
 350        return $reset_ok
 351}
 352
 353method _validate {d S} {
 354        if {$d == 1} {
 355                if {[regexp {[~^:?*\[\0- ]} $S]} {
 356                        return 0
 357                }
 358                if {[string length $S] > 0} {
 359                        set name_type user
 360                }
 361        }
 362        return 1
 363}
 364
 365method _select {args} {
 366        if {$name_type eq {match}} {
 367                $w_rev pick_tracking_branch
 368        }
 369}
 370
 371method _visible {} {
 372        grab $w
 373        if {$name_type eq {user}} {
 374                $w_name icursor end
 375                focus $w_name
 376        }
 377}
 378
 379}