| # 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 | 
 | 		} | 
 | 	} | 
 | } |