Skip to content

Commit fa38ab6

Browse files
logiclrdprati0100
authored andcommitted
git-gui: revert untracked files by deleting them
Update the revert_helper proc to check for untracked files as well as changes, and then handle changes to be reverted and untracked files with independent blocks of code. Prompt the user independently for untracked files, since the underlying action is fundamentally different (rm -f). If after deleting untracked files, the directory containing them becomes empty, then remove the directory as well. Migrate unlocking of the index out of _close_updateindex to a responsibility of the caller, to permit paths that don't directly unlock the index, and refactor the error handling added in d4e890e so that callers can make flow control decisions in the event of errors. Update Tcl/Tk dependency from 8.4 to 8.6 in git-gui.sh. A new proc delete_files takes care of actually deleting the files in batches, using the Tcler's Wiki recommended approach for keeping the UI responsive. Since the checkout_index and delete_files calls are both asynchronous and could potentially complete in any order, a "chord" is used to coordinate unlocking the index and returning the UI to a usable state only after both operations are complete. The `SimpleChord` class, based on TclOO (Tcl/Tk 8.6), is added in this commit. Signed-off-by: Jonathan Gilbert <[email protected]> Signed-off-by: Pratyush Yadav <[email protected]>
1 parent d9c6469 commit fa38ab6

File tree

3 files changed

+502
-84
lines changed

3 files changed

+502
-84
lines changed

git-gui.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ along with this program; if not, see <http://www.gnu.org/licenses/>.}]
3030
##
3131
## Tcl/Tk sanity check
3232

33-
if {[catch {package require Tcl 8.4} err]
34-
|| [catch {package require Tk 8.4} err]
33+
if {[catch {package require Tcl 8.6} err]
34+
|| [catch {package require Tk 8.6} err]
3535
} {
3636
catch {wm withdraw .}
3737
tk_messageBox \

lib/chord.tcl

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# Simple Chord for Tcl
2+
#
3+
# A "chord" is a method with more than one entrypoint and only one body, such
4+
# that the body runs only once all the entrypoints have been called by
5+
# different asynchronous tasks. In this implementation, the chord is defined
6+
# dynamically for each invocation. A SimpleChord object is created, supplying
7+
# body script to be run when the chord is completed, and then one or more notes
8+
# are added to the chord. Each note can be called like a proc, and returns
9+
# immediately if the chord isn't yet complete. When the last remaining note is
10+
# called, the body runs before the note returns.
11+
#
12+
# The SimpleChord class has a constructor that takes the body script, and a
13+
# method add_note that returns a note object. Since the body script does not
14+
# run in the context of the procedure that defined it, a mechanism is provided
15+
# for injecting variables into the chord for use by the body script. The
16+
# activation of a note is idempotent; multiple calls have the same effect as
17+
# a simple call.
18+
#
19+
# If you are invoking asynchronous operations with chord notes as completion
20+
# callbacks, and there is a possibility that earlier operations could complete
21+
# before later ones are started, it is a good practice to create a "common"
22+
# note on the chord that prevents it from being complete until you're certain
23+
# you've added all the notes you need.
24+
#
25+
# Example:
26+
#
27+
# # Turn off the UI while running a couple of async operations.
28+
# lock_ui
29+
#
30+
# set chord [SimpleChord new {
31+
# unlock_ui
32+
# # Note: $notice here is not referenced in the calling scope
33+
# if {$notice} { info_popup $notice }
34+
# }
35+
#
36+
# # Configure a note to keep the chord from completing until
37+
# # all operations have been initiated.
38+
# set common_note [$chord add_note]
39+
#
40+
# # Pass notes as 'after' callbacks to other operations
41+
# async_operation $args [$chord add_note]
42+
# other_async_operation $args [$chord add_note]
43+
#
44+
# # Communicate with the chord body
45+
# if {$condition} {
46+
# # This sets $notice in the same context that the chord body runs in.
47+
# $chord eval { set notice "Something interesting" }
48+
# }
49+
#
50+
# # Activate the common note, making the chord eligible to complete
51+
# $common_note
52+
#
53+
# At this point, the chord will complete at some unknown point in the future.
54+
# The common note might have been the first note activated, or the async
55+
# operations might have completed synchronously and the common note is the
56+
# last one, completing the chord before this code finishes, or anything in
57+
# between. The purpose of the chord is to not have to worry about the order.
58+
59+
# SimpleChord class:
60+
# Represents a procedure that conceptually has multiple entrypoints that must
61+
# all be called before the procedure executes. Each entrypoint is called a
62+
# "note". The chord is only "completed" when all the notes are "activated".
63+
oo::class create SimpleChord {
64+
variable notes body is_completed
65+
66+
# Constructor:
67+
# set chord [SimpleChord new {body}]
68+
# Creates a new chord object with the specified body script. The
69+
# body script is evaluated at most once, when a note is activated
70+
# and the chord has no other non-activated notes.
71+
constructor {body} {
72+
set notes [list]
73+
my eval [list set body $body]
74+
set is_completed 0
75+
}
76+
77+
# Method:
78+
# $chord eval {script}
79+
# Runs the specified script in the same context (namespace) in which
80+
# the chord body will be evaluated. This can be used to set variable
81+
# values for the chord body to use.
82+
method eval {script} {
83+
namespace eval [self] $script
84+
}
85+
86+
# Method:
87+
# set note [$chord add_note]
88+
# Adds a new note to the chord, an instance of ChordNote. Raises an
89+
# error if the chord is already completed, otherwise the chord is
90+
# updated so that the new note must also be activated before the
91+
# body is evaluated.
92+
method add_note {} {
93+
if {$is_completed} { error "Cannot add a note to a completed chord" }
94+
95+
set note [ChordNote new [self]]
96+
97+
lappend notes $note
98+
99+
return $note
100+
}
101+
102+
# This method is for internal use only and is intentionally undocumented.
103+
method notify_note_activation {} {
104+
if {!$is_completed} {
105+
foreach note $notes {
106+
if {![$note is_activated]} { return }
107+
}
108+
109+
set is_completed 1
110+
111+
namespace eval [self] $body
112+
namespace delete [self]
113+
}
114+
}
115+
}
116+
117+
# ChordNote class:
118+
# Represents a note within a chord, providing a way to activate it. When the
119+
# final note of the chord is activated (this can be any note in the chord,
120+
# with all other notes already previously activated in any order), the chord's
121+
# body is evaluated.
122+
oo::class create ChordNote {
123+
variable chord is_activated
124+
125+
# Constructor:
126+
# Instances of ChordNote are created internally by calling add_note on
127+
# SimpleChord objects.
128+
constructor {chord} {
129+
my eval set chord $chord
130+
set is_activated 0
131+
}
132+
133+
# Method:
134+
# [$note is_activated]
135+
# Returns true if this note has already been activated.
136+
method is_activated {} {
137+
return $is_activated
138+
}
139+
140+
# Method:
141+
# $note
142+
# Activates the note, if it has not already been activated, and
143+
# completes the chord if there are no other notes awaiting
144+
# activation. Subsequent calls will have no further effect.
145+
#
146+
# NB: In TclOO, if an object is invoked like a method without supplying
147+
# any method name, then this internal method `unknown` is what
148+
# actually runs (with no parameters). It is used in the ChordNote
149+
# class for the purpose of allowing the note object to be called as
150+
# a function (see example above). (The `unknown` method can also be
151+
# used to support dynamic dispatch, but must take parameters to
152+
# identify the "unknown" method to be invoked. In this form, this
153+
# proc serves only to make instances behave directly like methods.)
154+
method unknown {} {
155+
if {!$is_activated} {
156+
set is_activated 1
157+
$chord notify_note_activation
158+
}
159+
}
160+
}

0 commit comments

Comments
 (0)