Code

git-gui: Sort tags descending by tagger date
[git.git] / lib / branch.tcl
1 # git-gui branch (create/delete) support
2 # Copyright (C) 2006, 2007 Shawn Pearce
4 proc load_all_heads {} {
5         global all_heads
6         global some_heads_tracking
8         set rh refs/heads
9         set rh_len [expr {[string length $rh] + 1}]
10         set all_heads [list]
11         set fd [open "| git for-each-ref --format=%(refname) $rh" r]
12         while {[gets $fd line] > 0} {
13                 if {!$some_heads_tracking || ![is_tracking_branch $line]} {
14                         lappend all_heads [string range $line $rh_len end]
15                 }
16         }
17         close $fd
19         set all_heads [lsort $all_heads]
20 }
22 proc load_all_tags {} {
23         set all_tags [list]
24         set fd [open "| git for-each-ref --sort=-taggerdate --format=%(refname) refs/tags" r]
25         while {[gets $fd line] > 0} {
26                 if {![regsub ^refs/tags/ $line {} name]} continue
27                 lappend all_tags $name
28         }
29         close $fd
30         return $all_tags
31 }
33 proc populate_branch_menu {} {
34         global all_heads disable_on_lock
36         set m .mbar.branch
37         set last [$m index last]
38         for {set i 0} {$i <= $last} {incr i} {
39                 if {[$m type $i] eq {separator}} {
40                         $m delete $i last
41                         set new_dol [list]
42                         foreach a $disable_on_lock {
43                                 if {[lindex $a 0] ne $m || [lindex $a 2] < $i} {
44                                         lappend new_dol $a
45                                 }
46                         }
47                         set disable_on_lock $new_dol
48                         break
49                 }
50         }
52         if {$all_heads ne {}} {
53                 $m add separator
54         }
55         foreach b $all_heads {
56                 $m add radiobutton \
57                         -label $b \
58                         -command [list switch_branch $b] \
59                         -variable current_branch \
60                         -value $b
61                 lappend disable_on_lock \
62                         [list $m entryconf [$m index last] -state]
63         }
64 }
66 proc radio_selector {varname value args} {
67         upvar #0 $varname var
68         set var $value
69 }
71 proc switch_branch {new_branch} {
72         global HEAD commit_type current_branch repo_config
74         if {![lock_index switch]} return
76         # -- Our in memory state should match the repository.
77         #
78         repository_state curType curHEAD curMERGE_HEAD
79         if {[string match amend* $commit_type]
80                 && $curType eq {normal}
81                 && $curHEAD eq $HEAD} {
82         } elseif {$commit_type ne $curType || $HEAD ne $curHEAD} {
83                 info_popup {Last scanned state does not match repository state.
85 Another Git program has modified this repository since the last scan.  A rescan must be performed before the current branch can be changed.
87 The rescan will be automatically started now.
88 }
89                 unlock_index
90                 rescan {set ui_status_value {Ready.}}
91                 return
92         }
94         # -- Don't do a pointless switch.
95         #
96         if {$current_branch eq $new_branch} {
97                 unlock_index
98                 return
99         }
101         if {$repo_config(gui.trustmtime) eq {true}} {
102                 switch_branch_stage2 {} $new_branch
103         } else {
104                 set ui_status_value {Refreshing file status...}
105                 set cmd [list git update-index]
106                 lappend cmd -q
107                 lappend cmd --unmerged
108                 lappend cmd --ignore-missing
109                 lappend cmd --refresh
110                 set fd_rf [open "| $cmd" r]
111                 fconfigure $fd_rf -blocking 0 -translation binary
112                 fileevent $fd_rf readable \
113                         [list switch_branch_stage2 $fd_rf $new_branch]
114         }
117 proc switch_branch_stage2 {fd_rf new_branch} {
118         global ui_status_value HEAD
120         if {$fd_rf ne {}} {
121                 read $fd_rf
122                 if {![eof $fd_rf]} return
123                 close $fd_rf
124         }
126         set ui_status_value "Updating working directory to '$new_branch'..."
127         set cmd [list git read-tree]
128         lappend cmd -m
129         lappend cmd -u
130         lappend cmd --exclude-per-directory=.gitignore
131         lappend cmd $HEAD
132         lappend cmd $new_branch
133         set fd_rt [open "| $cmd" r]
134         fconfigure $fd_rt -blocking 0 -translation binary
135         fileevent $fd_rt readable \
136                 [list switch_branch_readtree_wait $fd_rt $new_branch]
139 proc switch_branch_readtree_wait {fd_rt new_branch} {
140         global selected_commit_type commit_type HEAD MERGE_HEAD PARENT
141         global current_branch
142         global ui_comm ui_status_value
144         # -- We never get interesting output on stdout; only stderr.
145         #
146         read $fd_rt
147         fconfigure $fd_rt -blocking 1
148         if {![eof $fd_rt]} {
149                 fconfigure $fd_rt -blocking 0
150                 return
151         }
153         # -- The working directory wasn't in sync with the index and
154         #    we'd have to overwrite something to make the switch. A
155         #    merge is required.
156         #
157         if {[catch {close $fd_rt} err]} {
158                 regsub {^fatal: } $err {} err
159                 warn_popup "File level merge required.
161 $err
163 Staying on branch '$current_branch'."
164                 set ui_status_value "Aborted checkout of '$new_branch' (file level merging is required)."
165                 unlock_index
166                 return
167         }
169         # -- Update the symbolic ref.  Core git doesn't even check for failure
170         #    here, it Just Works(tm).  If it doesn't we are in some really ugly
171         #    state that is difficult to recover from within git-gui.
172         #
173         if {[catch {git symbolic-ref HEAD "refs/heads/$new_branch"} err]} {
174                 error_popup "Failed to set current branch.
176 This working directory is only partially switched.  We successfully updated your files, but failed to update an internal Git file.
178 This should not have occurred.  [appname] will now close and give up.
180 $err"
181                 do_quit
182                 return
183         }
185         # -- Update our repository state.  If we were previously in amend mode
186         #    we need to toss the current buffer and do a full rescan to update
187         #    our file lists.  If we weren't in amend mode our file lists are
188         #    accurate and we can avoid the rescan.
189         #
190         unlock_index
191         set selected_commit_type new
192         if {[string match amend* $commit_type]} {
193                 $ui_comm delete 0.0 end
194                 $ui_comm edit reset
195                 $ui_comm edit modified false
196                 rescan {set ui_status_value "Checked out branch '$current_branch'."}
197         } else {
198                 repository_state commit_type HEAD MERGE_HEAD
199                 set PARENT $HEAD
200                 set ui_status_value "Checked out branch '$current_branch'."
201         }