git-gui: Option to default new branches to match tracking branches
[git/jrn.git] / lib / branch_create.tcl
blobdf3f435e11a13e6d1e812909bc0277ac6e1add2f
1 # git-gui branch create support
2 # Copyright (C) 2006, 2007 Shawn Pearce
4 class branch_create {
6 field w ; # widget path
7 field w_rev ; # mega-widget to pick the initial revision
8 field w_name ; # new branch name widget
10 field name {}; # name of the branch the user has chosen
11 field name_type user; # type of branch name to use
13 field opt_merge ff; # type of merge to apply to existing branch
14 field opt_checkout 1; # automatically checkout the new branch?
15 field reset_ok 0; # did the user agree to reset?
17 constructor dialog {} {
18 global repo_config
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 .]"
26 label $w.header -text {Create New Branch} -font font_uibold
27 pack $w.header -side top -fill x
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
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}
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
62 grid columnconfigure $w.desc 1 -weight 1
63 pack $w.desc -anchor nw -fill x -pady 5 -padx 5
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
68 labelframe $w.options -text {Options}
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
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
96 trace add variable @name_type write [cb _select]
98 set name $repo_config(gui.newbranchtemplate)
99 if {[is_config_true gui.matchtrackingbranch]} {
100 set name_type match
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
109 method _create {} {
110 global null_sha1 repo_config
111 global all_heads current_branch
113 switch -- $name_type {
114 user {
115 set newbranch $name
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
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
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
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
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
174 if {[catch {set new [$w_rev commit_or_die]}]} {
175 return
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.
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.
201 set new $cur
202 } elseif {$mrb eq $cur} {
203 # The current branch is older.
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
217 reset {
218 if {$mrb eq $cur} {
219 # The current branch is older.
221 set reflog_msg "merge [$w_rev get]: Fast-forward"
222 } else {
223 # The current branch will lose things.
225 if {[_confirm_reset $this $newbranch $cur $new]} {
226 set reflog_msg "reset [$w_rev get]"
227 } else {
228 return
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
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
259 if {$cur eq $null_sha1} {
260 lappend all_heads $newbranch
261 set all_heads [lsort -uniq $all_heads]
262 populate_branch_menu
265 destroy $w
266 if {$opt_checkout} {
267 switch_branch $newbranch
271 method _confirm_reset {newbranch cur new} {
272 set reset_ok 0
273 set gitk [list do_gitk [list $cur ^$new]]
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]"
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
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
302 pack [label $c.msg2 \
303 -anchor w \
304 -justify left \
305 -text "Recovering lost commits may not be easy." \
307 pack [label $c.msg3 \
308 -anchor w \
309 -justify left \
310 -text "Reset '$newbranch'?" \
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
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
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"
338 close $fd
339 $list configure -state disabled
341 bind $c <Key-v> $gitk
343 bind $c <Visibility> "
344 grab $c
345 focus $c.buttons.cancel
347 bind $c <Key-Return> [list destroy $c]
348 bind $c <Key-Escape> [list destroy $c]
349 tkwait window $c
350 return $reset_ok
353 method _validate {d S} {
354 if {$d == 1} {
355 if {[regexp {[~^:?*\[\0- ]} $S]} {
356 return 0
358 if {[string length $S] > 0} {
359 set name_type user
362 return 1
365 method _select {args} {
366 if {$name_type eq {match}} {
367 $w_rev pick_tracking_branch
371 method _visible {} {
372 grab $w
373 if {$name_type eq {user}} {
374 $w_name icursor end
375 focus $w_name