Code

git-gui: Sort tags descending by tagger date
[git.git] / lib / branch_create.tcl
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 .]"
24         }
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         set name $repo_config(gui.newbranchtemplate)
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
108 method _create {} {
109         global null_sha1 repo_config
110         global all_heads current_branch
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         }
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         }
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         }
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         }
173         if {[catch {set new [$w_rev commit_or_die]}]} {
174                 return
175         }
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         }
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         }
258         if {$cur eq $null_sha1} {
259                 lappend all_heads $newbranch
260                 set all_heads [lsort -uniq $all_heads]
261                 populate_branch_menu
262         }
264         destroy $w
265         if {$opt_checkout} {
266                 switch_branch $newbranch
267         }
270 method _confirm_reset {newbranch cur new} {
271         set reset_ok 0
272         set gitk [list do_gitk [list $cur ^$new]]
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]"
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
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
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                 ]
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
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
340         bind $c    <Key-v> $gitk
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
352 method _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