From: "Jonathan Gilbert via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Jonathan Gilbert <rcq8n2xf3v@liamekaens.com>,
Pratyush Yadav <me@yadavpratyush.com>,
Jonathan Gilbert <JonathanG@iQmetrix.com>
Subject: [PATCH 2/2] git-gui: revert untracked files by deleting them
Date: Wed, 30 Oct 2019 06:48:43 +0000 [thread overview]
Message-ID: <0190f6f2f978a674a29a1e2013d00bc289851c76.1572418123.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.436.git.1572418123.gitgitgadget@gmail.com>
From: Jonathan Gilbert <JonathanG@iQmetrix.com>
Updates the revert_helper procedure to also detect untracked files. If
files are present, the user is asked if they want them deleted. A new
proc delete_files with helper delete_helper performs the deletion in
batches, to allow the UI to remain responsive.
Signed-off-by: Jonathan Gilbert <JonathanG@iQmetrix.com>
---
lib/index.tcl | 255 +++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 222 insertions(+), 33 deletions(-)
diff --git a/lib/index.tcl b/lib/index.tcl
index 28d4d2a54e..9661ddb556 100644
--- a/lib/index.tcl
+++ b/lib/index.tcl
@@ -393,11 +393,20 @@ proc revert_helper {txt paths} {
if {![lock_index begin-update]} return
+ # The index is now locked. Some of the paths below include calls that
+ # unlock the index (e.g. checked_index). If we reach the end and the
+ # index is still locked, we need to unlock it before returning.
+ set need_unlock_index 1
+
set path_list [list]
+ set untracked_list [list]
set after {}
foreach path $paths {
switch -glob -- [lindex $file_states($path) 0] {
U? {continue}
+ ?O {
+ lappend untracked_list $path
+ }
?M -
?T -
?D {
@@ -409,45 +418,225 @@ proc revert_helper {txt paths} {
}
}
+ set path_cnt [llength $path_list]
+ set untracked_cnt [llength $untracked_list]
- # 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 $path_list]
- if {$n == 0} {
- unlock_index
- return
- } elseif {$n == 1} {
- set query [mc "Revert changes in file %s?" [short_path [lindex $path_list]]]
- } else {
- set query [mc "Revert changes in these %i files?" $n]
- }
+ if {$path_cnt > 0} {
+ # 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.
+ #
+ if {$path_cnt == 1} {
+ set query [mc \
+ "Revert changes in file %s?" \
+ [short_path [lindex $path_list]] \
+ ]
+ } else {
+ set query [mc \
+ "Revert changes in these %i files?" \
+ $path_cnt]
+ }
- set reply [tk_dialog \
- .confirm_revert \
- "[appname] ([reponame])" \
- "$query
+ 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 \
+ question \
+ 1 \
+ [mc "Do Nothing"] \
+ [mc "Revert Changes"] \
+ ]
+
+ if {$reply == 1} {
+ checkout_index \
+ $txt \
+ $path_list \
+ [concat $after [list ui_ready]]
+
+ set need_unlock_index 0
+ }
+ }
+
+ if {$need_unlock_index} { unlock_index }
+
+ if {$untracked_cnt > 0} {
+ # Split question between singular and plural cases, because
+ # such distinction is needed in some languages.
+ #
+ # 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.
+ #
+ if {$untracked_cnt == 1} {
+ set query [mc \
+ "Delete untracked file %s?" \
+ [short_path [lindex $untracked_list]] \
+ ]
+ } else {
+ set query [mc \
+ "Delete these %i untracked files?" \
+ $untracked_cnt \
+ ]
+ }
+
+ set reply [tk_dialog \
+ .confirm_revert \
+ "[appname] ([reponame])" \
+ "$query
+
+[mc "Files will be permanently deleted."]" \
+ question \
+ 1 \
+ [mc "Do Nothing"] \
+ [mc "Delete Files"] \
+ ]
+
+ if {$reply == 1} {
+ delete_files $untracked_list
+ }
+ }
+}
+
+# Delete all of the specified files, performing deletion in batches to allow the
+# UI to remain responsive and updated.
+proc delete_files {path_list} {
+ # Enable progress bar status updates
+ $::main_status start [mc "Deleting"] [mc "files"]
+
+ set path_index 0
+ set deletion_errors [list]
+ set deletion_error_path "not yet captured"
+ set batch_size 50
+
+ delete_helper \
+ $path_list \
+ $path_index \
+ $deletion_errors \
+ $deletion_error_path \
+ $batch_size
+}
+
+# Helper function to delete a list of files in batches. Each call deletes one
+# batch of files, and then schedules a call for the next batch after any UI
+# messages have been processed.
+proc delete_helper \
+ {path_list path_index deletion_errors deletion_error_path batch_size} {
+ global file_states
+
+ set path_cnt [llength $path_list]
+
+ set batch_remaining $batch_size
+
+ while {$batch_remaining > 0} {
+ if {$path_index >= $path_cnt} { break }
+
+ set path [lindex $path_list $path_index]
+
+ set deletion_failed [catch {file delete -- $path} deletion_error]
+
+ if {$deletion_failed} {
+ lappend deletion_errors $deletion_error
+
+ # Optimistically capture the path that failed, in case
+ # there's only one.
+ set deletion_error_path $path
+ } else {
+ remove_empty_directories [file dirname $path]
+
+ # Don't assume the deletion worked. Remove the file from
+ # the UI, but only if it no longer exists.
+ if {![lexists $path]} {
+ unset file_states($path)
+ display_file $path __
+ }
+ }
+
+ incr path_index 1
+ incr batch_remaining -1
+ }
+
+ # Update the progress bar to indicate that this batch has been
+ # completed. The update will be visible when this procedure returns
+ # and allows the UI thread to process messages.
+ $::main_status update $path_index $path_cnt
+
+ if {$path_index < $path_cnt} {
+ # The Tcler's Wiki lists this as the best practice for keeping
+ # a UI active and processing messages during a long-running
+ # operation.
+
+ after idle [list after 0 [list \
+ delete_helper \
$path_list \
- [concat $after [list ui_ready]]
+ $path_index \
+ $deletion_errors \
+ $deletion_error_path \
+ $batch_size \
+ ]]
} else {
- unlock_index
+ # Finish the status bar operation.
+ $::main_status stop
+
+ # Report error, if any, based on how many deletions failed.
+ set deletion_error_cnt [llength $deletion_errors]
+
+ if {$deletion_error_cnt == 1} {
+ error_popup [mc \
+ "File %s could not be deleted: %s" \
+ $deletion_error_path \
+ [lindex $deletion_errors 0] \
+ ]
+ } elseif {$deletion_error_cnt == $path_cnt} {
+ error_popup [mc \
+ "None of the selected files could be deleted." \
+ ]
+ } elseif {$deletion_error_cnt > 1} {
+ error_popup [mc \
+ "%d of the selected files could not be deleted." \
+ $deletion_error_cnt]
+ }
+
+ reshow_diff
+ ui_ready
+ }
+}
+
+# This function is from the TCL documentation:
+#
+# https://wiki.tcl-lang.org/page/file+exists
+#
+# [file exists] returns false if the path does exist but is a symlink to a path
+# that doesn't exist. This proc returns true if the path exists, regardless of
+# whether it is a symlink and whether it is broken.
+proc lexists name {
+ expr {![catch {file lstat $name finfo}]}
+}
+
+# Remove as many empty directories as we can starting at the specified path.
+# If we encounter a directory that is not empty, or if a directory deletion
+# fails, then we stop the operation and return to the caller. Even if this
+# procedure fails to delete any directories at all, it does not report failure.
+proc remove_empty_directories {directory_path} {
+ set parent_path [file dirname $directory_path]
+
+ while {$parent_path != $directory_path} {
+ set contents [glob -nocomplain -dir $directory_path *]
+
+ if {[llength $contents] > 0} { break }
+ if {[catch {file delete -- $directory_path}]} { break }
+
+ set directory_path $parent_path
+ set parent_path [file dirname $directory_path]
}
}
--
gitgitgadget
next prev parent reply other threads:[~2019-10-30 6:48 UTC|newest]
Thread overview: 57+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-10-30 6:48 [PATCH 0/2] git-gui: revert untracked files by deleting them Jonathan Gilbert via GitGitGadget
2019-10-30 6:48 ` [PATCH 1/2] git-gui: consolidate naming conventions Jonathan Gilbert via GitGitGadget
2019-11-03 0:27 ` Pratyush Yadav
2019-10-30 6:48 ` Jonathan Gilbert via GitGitGadget [this message]
2019-11-03 7:44 ` [PATCH 2/2] git-gui: revert untracked files by deleting them Pratyush Yadav
2019-11-04 16:04 ` Jonathan Gilbert
2019-11-04 17:36 ` Jonathan Gilbert
2019-10-30 9:06 ` [PATCH 0/2] " Bert Wesarg
2019-10-30 17:16 ` Jonathan Gilbert
2019-11-03 1:12 ` Pratyush Yadav
2019-11-03 4:41 ` Jonathan Gilbert
2019-11-03 7:54 ` Pratyush Yadav
2019-11-07 7:05 ` [PATCH v2 " Jonathan Gilbert via GitGitGadget
2019-11-07 7:05 ` [PATCH v2 1/2] git-gui: consolidate naming conventions Jonathan Gilbert via GitGitGadget
2019-11-07 7:05 ` [PATCH v2 2/2] git-gui: revert untracked files by deleting them Jonathan Gilbert via GitGitGadget
2019-11-11 19:25 ` Pratyush Yadav
2019-11-11 21:55 ` Jonathan Gilbert
2019-11-11 22:59 ` Philip Oakley
2019-11-12 4:49 ` Jonathan Gilbert
2019-11-12 10:45 ` Philip Oakley
2019-11-12 16:29 ` Jonathan Gilbert
2019-11-26 11:22 ` Philip Oakley
2019-11-12 19:35 ` Pratyush Yadav
2019-11-11 19:35 ` [PATCH v2 0/2] " Pratyush Yadav
2019-11-13 9:56 ` [PATCH v3 " Jonathan Gilbert via GitGitGadget
2019-11-13 9:56 ` [PATCH v3 1/2] git-gui: consolidate naming conventions Jonathan Gilbert via GitGitGadget
2019-11-13 9:56 ` [PATCH v3 2/2] git-gui: revert untracked files by deleting them Jonathan Gilbert via GitGitGadget
2019-11-16 15:11 ` Pratyush Yadav
2019-11-16 21:42 ` Jonathan Gilbert
2019-11-17 6:56 ` [PATCH v4 0/2] " Jonathan Gilbert via GitGitGadget
2019-11-17 6:56 ` [PATCH v4 1/2] git-gui: consolidate naming conventions Jonathan Gilbert via GitGitGadget
2019-11-17 6:56 ` [PATCH v4 2/2] git-gui: revert untracked files by deleting them Jonathan Gilbert via GitGitGadget
2019-11-24 13:09 ` Pratyush Yadav
2019-11-19 15:21 ` [PATCH v4 0/2] " Pratyush Yadav
2019-11-19 16:56 ` Jonathan Gilbert
2019-11-24 20:37 ` [PATCH v5 0/3] " Jonathan Gilbert via GitGitGadget
2019-11-24 20:37 ` [PATCH v5 1/3] git-gui: consolidate naming conventions Jonathan Gilbert via GitGitGadget
2019-11-24 20:37 ` [PATCH v5 2/3] git-gui: update status bar to track operations Jonathan Gilbert via GitGitGadget
2019-11-27 21:55 ` Pratyush Yadav
2019-11-28 7:34 ` Jonathan Gilbert
2019-11-24 20:37 ` [PATCH v5 3/3] git-gui: revert untracked files by deleting them Jonathan Gilbert via GitGitGadget
2019-11-27 22:03 ` Pratyush Yadav
2019-11-28 8:30 ` [PATCH v6 0/3] " Jonathan Gilbert via GitGitGadget
2019-11-28 8:30 ` [PATCH v6 1/3] git-gui: consolidate naming conventions Jonathan Gilbert via GitGitGadget
2019-11-28 8:30 ` [PATCH v6 2/3] git-gui: update status bar to track operations Jonathan Gilbert via GitGitGadget
2019-11-30 23:05 ` Pratyush Yadav
2019-12-01 2:12 ` Jonathan Gilbert
2019-12-01 11:43 ` Philip Oakley
2019-12-01 20:09 ` Jonathan Gilbert
2019-11-28 8:30 ` [PATCH v6 3/3] git-gui: revert untracked files by deleting them Jonathan Gilbert via GitGitGadget
2019-12-01 2:28 ` [PATCH v7 0/3] " Jonathan Gilbert via GitGitGadget
2019-12-01 2:28 ` [PATCH v7 1/3] git-gui: consolidate naming conventions Jonathan Gilbert via GitGitGadget
2019-12-01 2:28 ` [PATCH v7 2/3] git-gui: update status bar to track operations Jonathan Gilbert via GitGitGadget
2020-02-26 8:24 ` Benjamin Poirier
2020-03-02 18:14 ` Pratyush Yadav
2019-12-01 2:28 ` [PATCH v7 3/3] git-gui: revert untracked files by deleting them Jonathan Gilbert via GitGitGadget
2019-12-05 18:54 ` [PATCH v7 0/3] " Pratyush Yadav
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=0190f6f2f978a674a29a1e2013d00bc289851c76.1572418123.git.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=JonathanG@iQmetrix.com \
--cc=git@vger.kernel.org \
--cc=me@yadavpratyush.com \
--cc=rcq8n2xf3v@liamekaens.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).