1# git-gui branch (create/delete) support 2# Copyright (C) 2006, 2007 Shawn Pearce 3 4proc load_all_heads {} { 5global all_heads 6 7set all_heads [list] 8set fd [open"| git for-each-ref --format=%(refname) refs/heads" r] 9while{[gets$fd line] >0} { 10if{[is_tracking_branch $line]}continue 11if{![regsub ^refs/heads/$line{} name]}continue 12lappend all_heads $name 13} 14close$fd 15 16set all_heads [lsort$all_heads] 17} 18 19proc load_all_tags {} { 20set all_tags [list] 21set fd [open"| git for-each-ref --format=%(refname) refs/tags" r] 22while{[gets$fd line] >0} { 23if{![regsub ^refs/tags/$line{} name]}continue 24lappend all_tags $name 25} 26close$fd 27 28return[lsort$all_tags] 29} 30 31proc populate_branch_menu {} { 32global all_heads disable_on_lock 33 34set m .mbar.branch 35set last [$m index last] 36for{set i 0} {$i<=$last} {incr i} { 37if{[$m type $i] eq {separator}} { 38$m delete $i last 39set new_dol [list] 40foreach a $disable_on_lock{ 41if{[lindex$a0] ne $m || [lindex$a2] <$i} { 42lappend new_dol $a 43} 44} 45set disable_on_lock $new_dol 46break 47} 48} 49 50if{$all_heads ne {}} { 51$m add separator 52} 53foreach b $all_heads{ 54$m add radiobutton\ 55-label$b\ 56-command[list switch_branch $b] \ 57-variable current_branch \ 58-value$b 59lappend disable_on_lock \ 60[list$m entryconf [$m index last]-state] 61} 62} 63 64proc radio_selector {varname value args} { 65upvar#0 $varname var 66set var $value 67} 68 69proc switch_branch {new_branch} { 70global HEAD commit_type current_branch repo_config 71 72if{![lock_index switch]}return 73 74# -- Our in memory state should match the repository. 75# 76 repository_state curType curHEAD curMERGE_HEAD 77if{[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 83Another Git program has modified this repository since the last scan. A rescan must be performed before the current branch can be changed. 84 85The rescan will be automatically started now. 86} 87 unlock_index 88 rescan {set ui_status_value {Ready.}} 89return 90} 91 92# -- Don't do a pointless switch. 93# 94if{$current_branch eq $new_branch} { 95 unlock_index 96return 97} 98 99if{$repo_config(gui.trustmtime) eq {true}} { 100 switch_branch_stage2 {}$new_branch 101}else{ 102set ui_status_value {Refreshing file status...} 103set cmd [list git update-index] 104lappend cmd -q 105lappend cmd --unmerged 106lappend cmd --ignore-missing 107lappend cmd --refresh 108set fd_rf [open"|$cmd" r] 109fconfigure$fd_rf-blocking0-translationbinary 110fileevent$fd_rf readable \ 111[list switch_branch_stage2 $fd_rf $new_branch] 112} 113} 114 115proc switch_branch_stage2 {fd_rf new_branch} { 116global ui_status_value HEAD 117 118if{$fd_rf ne {}} { 119read$fd_rf 120if{![eof$fd_rf]}return 121close$fd_rf 122} 123 124set ui_status_value "Updating working directory to '$new_branch'..." 125set cmd [list git read-tree] 126lappend cmd -m 127lappend cmd -u 128lappend cmd --exclude-per-directory=.gitignore 129lappend cmd $HEAD 130lappend cmd $new_branch 131set fd_rt [open"|$cmd" r] 132fconfigure$fd_rt-blocking0-translationbinary 133fileevent$fd_rt readable \ 134[list switch_branch_readtree_wait $fd_rt $new_branch] 135} 136 137proc switch_branch_readtree_wait {fd_rt new_branch} { 138global selected_commit_type commit_type HEAD MERGE_HEAD PARENT 139global current_branch 140global ui_comm ui_status_value 141 142# -- We never get interesting output on stdout; only stderr. 143# 144read$fd_rt 145fconfigure$fd_rt-blocking1 146if{![eof$fd_rt]} { 147fconfigure$fd_rt-blocking0 148return 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# 155if{[catch{close$fd_rt} err]} { 156regsub{^fatal: }$err{} err 157 warn_popup "File level merge required. 158 159$err 160 161Staying on branch '$current_branch'." 162set ui_status_value "Aborted checkout of '$new_branch' (file level merging is required)." 163 unlock_index 164return 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# 171if{[catch{git symbolic-ref HEAD "refs/heads/$new_branch"} err]} { 172 error_popup "Failed to set current branch. 173 174This working directory is only partially switched. We successfully updated your files, but failed to update an internal Git file. 175 176This should not have occurred. [appname] will now close and give up. 177 178$err" 179 do_quit 180return 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 189set selected_commit_type new 190if{[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 197set PARENT $HEAD 198set ui_status_value "Checked out branch '$current_branch'." 199} 200}