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