about summary refs log tree commit diff
path: root/git-gui/lib/index.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'git-gui/lib/index.tcl')
-rw-r--r--git-gui/lib/index.tcl484
1 files changed, 484 insertions, 0 deletions
diff --git a/git-gui/lib/index.tcl b/git-gui/lib/index.tcl
new file mode 100644
index 000000000000..b588db11d9fc
--- /dev/null
+++ b/git-gui/lib/index.tcl
@@ -0,0 +1,484 @@
+# 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} {
+	global use_ttk NS
+	fconfigure $fd -blocking 1
+	if {[catch {close $fd} err]} {
+		set w .indexfried
+		Dialog $w
+		wm withdraw $w
+		wm title $w [strcat "[appname] ([reponame]): " [mc "Index Error"]]
+		wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
+		set s [mc "Updating the Git index failed.  A rescan will be automatically started to resynchronize git-gui."]
+		text $w.msg -yscrollcommand [list $w.vs set] \
+			-width [string length $s] -relief flat \
+			-borderwidth 0 -highlightthickness 0 \
+			-background [get_bg_color $w]
+		$w.msg tag configure bold -font font_uibold -justify center
+		${NS}::scrollbar $w.vs -command [list $w.msg yview]
+		$w.msg insert end $s bold \n\n$err {}
+		$w.msg configure -state disabled
+
+		${NS}::button $w.continue \
+			-text [mc "Continue"] \
+			-command [list destroy $w]
+		${NS}::button $w.unlock \
+			-text [mc "Unlock Index"] \
+			-command "destroy $w; _delete_indexlock"
+		grid $w.msg - $w.vs -sticky news
+		grid $w.unlock $w.continue - -sticky se -padx 2 -pady 2
+		grid columnconfigure $w 0 -weight 1
+		grid rowconfigure $w 0 -weight 1
+
+		wm protocol $w WM_DELETE_WINDOW update
+		bind $w.continue <Visibility> "
+			grab $w
+			focus %W
+		"
+		wm deiconify $w
+		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}
+		MT -
+		TM -
+		T_ {set new _T}
+		M? {set new _M}
+		TD -
+		D_ {set new _D}
+		D? {set new _?}
+		?? {continue}
+		}
+		set info [lindex $s 2]
+		if {$info eq {}} continue
+
+		puts -nonewline $fd "$info\t[encoding convertto utf-8 $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 -
+		AT -
+		AM {set new A_}
+		TM -
+		MT -
+		_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 utf-8 $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 utf-8 $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 \
+			[mc "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 \
+			[mc "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]
+	set untracked_paths [list]
+	foreach path [array names file_states] {
+		switch -glob -- [lindex $file_states($path) 0] {
+		U? {continue}
+		?M -
+		?T -
+		?D {lappend paths $path}
+		?O {lappend untracked_paths $path}
+		}
+	}
+	if {[llength $untracked_paths]} {
+		set reply 0
+		switch -- [get_config gui.stageuntracked] {
+		no {
+			set reply 0
+		}
+		yes {
+			set reply 1
+		}
+		ask -
+		default {
+			set reply [ask_popup [mc "Stage %d untracked files?" \
+									  [llength $untracked_paths]]]
+		}
+		}
+		if {$reply} {
+			set paths [concat $paths $untracked_paths]
+		}
+	}
+	add_helper [mc "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 different 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
+		}
+	}
+}