Skip to content

Commit b5b6b43

Browse files
committed
git-gui: Implement basic branch switching through read-tree.
If the user selects a different branch from the Branch menu, or asks us to create a new branch and immediately checkout that branch we now perform the update of the working directory by way of a 2 way read-tree invocation. This emulates the behavior of `git checkout branch` or the behavior of `git checkout -b branch initrev`. We don't however support the -m style behavior, where a switch can occur with file level merging performed by merge-recursive. Support for this is planned for a future update. Signed-off-by: Shawn O. Pearce <[email protected]>
1 parent 0fd49d0 commit b5b6b43

File tree

1 file changed

+101
-15
lines changed

1 file changed

+101
-15
lines changed

git-gui.sh

Lines changed: 101 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2155,16 +2155,11 @@ proc do_delete_branch {} {
21552155
tkwait window $w
21562156
}
21572157

2158-
proc switch_branch {b} {
2159-
global HEAD commit_type file_states current_branch
2160-
global selected_commit_type ui_comm
2158+
proc switch_branch {new_branch} {
2159+
global HEAD commit_type current_branch repo_config
21612160

21622161
if {![lock_index switch]} return
21632162

2164-
# -- Backup the selected branch (repository_state resets it)
2165-
#
2166-
set new_branch $current_branch
2167-
21682163
# -- Our in memory state should match the repository.
21692164
#
21702165
repository_state curType curHEAD curMERGE_HEAD
@@ -2185,19 +2180,110 @@ The rescan will be automatically started now.
21852180
return
21862181
}
21872182

2188-
# -- Toss the message buffer if we are in amend mode.
2183+
if {$repo_config(gui.trustmtime) eq {true}} {
2184+
switch_branch_stage2 {} $new_branch
2185+
} else {
2186+
set ui_status_value {Refreshing file status...}
2187+
set cmd [list git update-index]
2188+
lappend cmd -q
2189+
lappend cmd --unmerged
2190+
lappend cmd --ignore-missing
2191+
lappend cmd --refresh
2192+
set fd_rf [open "| $cmd" r]
2193+
fconfigure $fd_rf -blocking 0 -translation binary
2194+
fileevent $fd_rf readable \
2195+
[list switch_branch_stage2 $fd_rf $new_branch]
2196+
}
2197+
}
2198+
2199+
proc switch_branch_stage2 {fd_rf new_branch} {
2200+
global ui_status_value HEAD
2201+
2202+
if {$fd_rf ne {}} {
2203+
read $fd_rf
2204+
if {![eof $fd_rf]} return
2205+
close $fd_rf
2206+
}
2207+
2208+
set ui_status_value "Updating working directory to '$new_branch'..."
2209+
set cmd [list git read-tree]
2210+
lappend cmd -m
2211+
lappend cmd -u
2212+
lappend cmd --exclude-per-directory=.gitignore
2213+
lappend cmd $HEAD
2214+
lappend cmd $new_branch
2215+
set fd_rt [open "| $cmd" r]
2216+
fconfigure $fd_rt -blocking 0 -translation binary
2217+
fileevent $fd_rt readable \
2218+
[list switch_branch_readtree_wait $fd_rt $new_branch]
2219+
}
2220+
2221+
proc switch_branch_readtree_wait {fd_rt new_branch} {
2222+
global selected_commit_type commit_type HEAD MERGE_HEAD PARENT
2223+
global current_branch
2224+
global ui_comm ui_status_value
2225+
2226+
# -- We never get interesting output on stdout; only stderr.
21892227
#
2190-
if {[string match amend* $curType]} {
2191-
$ui_comm delete 0.0 end
2192-
$ui_comm edit reset
2193-
$ui_comm edit modified false
2228+
read $fd_rt
2229+
fconfigure $fd_rt -blocking 1
2230+
if {![eof $fd_rt]} {
2231+
fconfigure $fd_rt -blocking 0
2232+
return
21942233
}
21952234

2196-
set selected_commit_type new
2197-
set current_branch $new_branch
2235+
# -- The working directory wasn't in sync with the index and
2236+
# we'd have to overwrite something to make the switch. A
2237+
# merge is required.
2238+
#
2239+
if {[catch {close $fd_rt} err]} {
2240+
regsub {^fatal: } $err {} err
2241+
warn_popup "File level merge required.
2242+
2243+
$err
2244+
2245+
Staying on branch '$current_branch'."
2246+
set ui_status_value "Aborted checkout of '$new_branch' (file level merging is required)."
2247+
unlock_index
2248+
return
2249+
}
2250+
2251+
# -- Update the symbolic ref. Core git doesn't even check for failure
2252+
# here, it Just Works(tm). If it doesn't we are in some really ugly
2253+
# state that is difficult to recover from within git-gui.
2254+
#
2255+
if {[catch {exec git symbolic-ref HEAD "refs/heads/$new_branch"} err]} {
2256+
error_popup "Failed to set current branch.
2257+
2258+
This working directory is only partially switched.
2259+
We successfully updated your files, but failed to
2260+
update an internal Git file.
2261+
2262+
This should not have occurred. [appname] will now
2263+
close and give up.
21982264
2265+
$err"
2266+
do_quit
2267+
return
2268+
}
2269+
2270+
# -- Update our repository state. If we were previously in amend mode
2271+
# we need to toss the current buffer and do a full rescan to update
2272+
# our file lists. If we weren't in amend mode our file lists are
2273+
# accurate and we can avoid the rescan.
2274+
#
21992275
unlock_index
2200-
error "NOT FINISHED"
2276+
set selected_commit_type new
2277+
if {[string match amend* $commit_type]} {
2278+
$ui_comm delete 0.0 end
2279+
$ui_comm edit reset
2280+
$ui_comm edit modified false
2281+
rescan {set ui_status_value "Checked out branch '$current_branch'."}
2282+
} else {
2283+
repository_state commit_type HEAD MERGE_HEAD
2284+
set PARENT $HEAD
2285+
set ui_status_value "Checked out branch '$current_branch'."
2286+
}
22012287
}
22022288

22032289
######################################################################

0 commit comments

Comments
 (0)