git/lib/index.tcl
Alexander Gavrilov 0aea2842d9 git-gui: Make Ctrl-T safe to use for conflicting files.
A previous patch added a check for conflict markers, which
is done when the file is about to be staged due to a click
on the icon. However, pressing Ctrl-T still immediately
stages the file without confirmation. This patch fixes it.

The check requires a loaded diff, so staging multiple files
at once won't work if they are unmerged.

Signed-off-by: Alexander Gavrilov <angavrilov@gmail.com>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
2008-09-30 11:47:33 -07:00

453 lines
9.4 KiB
Tcl

# git-gui index (add/remove) support
# Copyright (C) 2006, 2007 Shawn Pearce
proc _delete_indexlock {} {
if {[catch {file delete -- [gitdir index.lock]} err]} {
error_popup [strcat [mc "Unable to unlock the index."] "\n\n$err"]
}
}
proc _close_updateindex {fd after} {
fconfigure $fd -blocking 1
if {[catch {close $fd} err]} {
set w .indexfried
toplevel $w
wm title $w [strcat "[appname] ([reponame]): " [mc "Index Error"]]
wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
pack [label $w.msg \
-justify left \
-anchor w \
-text [strcat \
[mc "Updating the Git index failed. A rescan will be automatically started to resynchronize git-gui."] \
"\n\n$err"] \
] -anchor w
frame $w.buttons
button $w.buttons.continue \
-text [mc "Continue"] \
-command [list destroy $w]
pack $w.buttons.continue -side right -padx 5
button $w.buttons.unlock \
-text [mc "Unlock Index"] \
-command "destroy $w; _delete_indexlock"
pack $w.buttons.unlock -side right
pack $w.buttons -side bottom -fill x -pady 10 -padx 10
wm protocol $w WM_DELETE_WINDOW update
bind $w.buttons.continue <Visibility> "
grab $w
focus $w.buttons.continue
"
tkwait window $w
$::main_status stop
unlock_index
rescan $after 0
return
}
$::main_status stop
unlock_index
uplevel #0 $after
}
proc update_indexinfo {msg pathList after} {
global update_index_cp
if {![lock_index update]} return
set update_index_cp 0
set pathList [lsort $pathList]
set totalCnt [llength $pathList]
set batch [expr {int($totalCnt * .01) + 1}]
if {$batch > 25} {set batch 25}
$::main_status start $msg [mc "files"]
set fd [git_write update-index -z --index-info]
fconfigure $fd \
-blocking 0 \
-buffering full \
-buffersize 512 \
-encoding binary \
-translation binary
fileevent $fd writable [list \
write_update_indexinfo \
$fd \
$pathList \
$totalCnt \
$batch \
$after \
]
}
proc write_update_indexinfo {fd pathList totalCnt batch after} {
global update_index_cp
global file_states current_diff_path
if {$update_index_cp >= $totalCnt} {
_close_updateindex $fd $after
return
}
for {set i $batch} \
{$update_index_cp < $totalCnt && $i > 0} \
{incr i -1} {
set path [lindex $pathList $update_index_cp]
incr update_index_cp
set s $file_states($path)
switch -glob -- [lindex $s 0] {
A? {set new _O}
M? {set new _M}
T_ {set new _T}
D_ {set new _D}
D? {set new _?}
?? {continue}
}
set info [lindex $s 2]
if {$info eq {}} continue
puts -nonewline $fd "$info\t[encoding convertto $path]\0"
display_file $path $new
}
$::main_status update $update_index_cp $totalCnt
}
proc update_index {msg pathList after} {
global update_index_cp
if {![lock_index update]} return
set update_index_cp 0
set pathList [lsort $pathList]
set totalCnt [llength $pathList]
set batch [expr {int($totalCnt * .01) + 1}]
if {$batch > 25} {set batch 25}
$::main_status start $msg [mc "files"]
set fd [git_write update-index --add --remove -z --stdin]
fconfigure $fd \
-blocking 0 \
-buffering full \
-buffersize 512 \
-encoding binary \
-translation binary
fileevent $fd writable [list \
write_update_index \
$fd \
$pathList \
$totalCnt \
$batch \
$after \
]
}
proc write_update_index {fd pathList totalCnt batch after} {
global update_index_cp
global file_states current_diff_path
if {$update_index_cp >= $totalCnt} {
_close_updateindex $fd $after
return
}
for {set i $batch} \
{$update_index_cp < $totalCnt && $i > 0} \
{incr i -1} {
set path [lindex $pathList $update_index_cp]
incr update_index_cp
switch -glob -- [lindex $file_states($path) 0] {
AD {set new __}
?D {set new D_}
_O -
AM {set new A_}
_T {set new T_}
_U -
U? {
if {[file exists $path]} {
set new M_
} else {
set new D_
}
}
?M {set new M_}
?? {continue}
}
puts -nonewline $fd "[encoding convertto $path]\0"
display_file $path $new
}
$::main_status update $update_index_cp $totalCnt
}
proc checkout_index {msg pathList after} {
global update_index_cp
if {![lock_index update]} return
set update_index_cp 0
set pathList [lsort $pathList]
set totalCnt [llength $pathList]
set batch [expr {int($totalCnt * .01) + 1}]
if {$batch > 25} {set batch 25}
$::main_status start $msg [mc "files"]
set fd [git_write checkout-index \
--index \
--quiet \
--force \
-z \
--stdin \
]
fconfigure $fd \
-blocking 0 \
-buffering full \
-buffersize 512 \
-encoding binary \
-translation binary
fileevent $fd writable [list \
write_checkout_index \
$fd \
$pathList \
$totalCnt \
$batch \
$after \
]
}
proc write_checkout_index {fd pathList totalCnt batch after} {
global update_index_cp
global file_states current_diff_path
if {$update_index_cp >= $totalCnt} {
_close_updateindex $fd $after
return
}
for {set i $batch} \
{$update_index_cp < $totalCnt && $i > 0} \
{incr i -1} {
set path [lindex $pathList $update_index_cp]
incr update_index_cp
switch -glob -- [lindex $file_states($path) 0] {
U? {continue}
?M -
?T -
?D {
puts -nonewline $fd "[encoding convertto $path]\0"
display_file $path ?_
}
}
}
$::main_status update $update_index_cp $totalCnt
}
proc unstage_helper {txt paths} {
global file_states current_diff_path
if {![lock_index begin-update]} return
set pathList [list]
set after {}
foreach path $paths {
switch -glob -- [lindex $file_states($path) 0] {
A? -
M? -
T_ -
D? {
lappend pathList $path
if {$path eq $current_diff_path} {
set after {reshow_diff;}
}
}
}
}
if {$pathList eq {}} {
unlock_index
} else {
update_indexinfo \
$txt \
$pathList \
[concat $after [list ui_ready]]
}
}
proc do_unstage_selection {} {
global current_diff_path selected_paths
if {[array size selected_paths] > 0} {
unstage_helper \
{Unstaging selected files from commit} \
[array names selected_paths]
} elseif {$current_diff_path ne {}} {
unstage_helper \
[mc "Unstaging %s from commit" [short_path $current_diff_path]] \
[list $current_diff_path]
}
}
proc add_helper {txt paths} {
global file_states current_diff_path
if {![lock_index begin-update]} return
set pathList [list]
set after {}
foreach path $paths {
switch -glob -- [lindex $file_states($path) 0] {
_U -
U? {
if {$path eq $current_diff_path} {
unlock_index
merge_stage_workdir $path
return
}
}
_O -
?M -
?D -
?T {
lappend pathList $path
if {$path eq $current_diff_path} {
set after {reshow_diff;}
}
}
}
}
if {$pathList eq {}} {
unlock_index
} else {
update_index \
$txt \
$pathList \
[concat $after {ui_status [mc "Ready to commit."]}]
}
}
proc do_add_selection {} {
global current_diff_path selected_paths
if {[array size selected_paths] > 0} {
add_helper \
{Adding selected files} \
[array names selected_paths]
} elseif {$current_diff_path ne {}} {
add_helper \
[mc "Adding %s" [short_path $current_diff_path]] \
[list $current_diff_path]
}
}
proc do_add_all {} {
global file_states
set paths [list]
foreach path [array names file_states] {
switch -glob -- [lindex $file_states($path) 0] {
U? {continue}
?M -
?T -
?D {lappend paths $path}
}
}
add_helper {Adding all changed files} $paths
}
proc revert_helper {txt paths} {
global file_states current_diff_path
if {![lock_index begin-update]} return
set pathList [list]
set after {}
foreach path $paths {
switch -glob -- [lindex $file_states($path) 0] {
U? {continue}
?M -
?T -
?D {
lappend pathList $path
if {$path eq $current_diff_path} {
set after {reshow_diff;}
}
}
}
}
# Split question between singular and plural cases, because
# such distinction is needed in some languages. Previously, the
# code used "Revert changes in" for both, but that can't work
# in languages where 'in' must be combined with word from
# rest of string (in diffrent way for both cases of course).
#
# FIXME: Unfortunately, even that isn't enough in some languages
# as they have quite complex plural-form rules. Unfortunately,
# msgcat doesn't seem to support that kind of string translation.
#
set n [llength $pathList]
if {$n == 0} {
unlock_index
return
} elseif {$n == 1} {
set query [mc "Revert changes in file %s?" [short_path [lindex $pathList]]]
} else {
set query [mc "Revert changes in these %i files?" $n]
}
set reply [tk_dialog \
.confirm_revert \
"[appname] ([reponame])" \
"$query
[mc "Any unstaged changes will be permanently lost by the revert."]" \
question \
1 \
[mc "Do Nothing"] \
[mc "Revert Changes"] \
]
if {$reply == 1} {
checkout_index \
$txt \
$pathList \
[concat $after [list ui_ready]]
} else {
unlock_index
}
}
proc do_revert_selection {} {
global current_diff_path selected_paths
if {[array size selected_paths] > 0} {
revert_helper \
[mc "Reverting selected files"] \
[array names selected_paths]
} elseif {$current_diff_path ne {}} {
revert_helper \
[mc "Reverting %s" [short_path $current_diff_path]] \
[list $current_diff_path]
}
}
proc do_select_commit_type {} {
global commit_type selected_commit_type
if {$selected_commit_type eq {new}
&& [string match amend* $commit_type]} {
create_new_commit
} elseif {$selected_commit_type eq {amend}
&& ![string match amend* $commit_type]} {
load_last_commit
# The amend request was rejected...
#
if {![string match amend* $commit_type]} {
set selected_commit_type new
}
}
}