Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit cd80a55

Browse files
refactor: file-system watcher
- Use `System.Threading.Interlocked` to read/write data - Use `SourceGit.Models.Watcher.LockContext` to release watcher's lock - New way to detect file changes in submodules - Manually update stashes after `git stash` command complete (#1760) Signed-off-by: leo <longshuang@msn.cn>
1 parent 2c96a13 commit cd80a55

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+212
-249
lines changed

‎src/Models/Watcher.cs

Lines changed: 114 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,26 @@ namespace SourceGit.Models
77
{
88
public class Watcher : IDisposable
99
{
10+
public class LockContext : IDisposable
11+
{
12+
public LockContext(Watcher target)
13+
{
14+
_target = target;
15+
Interlocked.Increment(ref _target._lockCount);
16+
}
17+
18+
public void Dispose()
19+
{
20+
Interlocked.Decrement(ref _target._lockCount);
21+
}
22+
23+
private Watcher _target;
24+
}
25+
1026
public Watcher(IRepository repo, string fullpath, string gitDir)
1127
{
1228
_repo = repo;
29+
_root = new DirectoryInfo(fullpath).FullName;
1330

1431
var testGitDir = new DirectoryInfo(Path.Combine(fullpath, ".git")).FullName;
1532
var desiredDir = new DirectoryInfo(gitDir).FullName;
@@ -59,42 +76,29 @@ public Watcher(IRepository repo, string fullpath, string gitDir)
5976
_timer = new Timer(Tick, null, 100, 100);
6077
}
6178

62-
public void SetEnabled(bool enabled)
63-
{
64-
if (enabled)
65-
{
66-
if (_lockCount > 0)
67-
_lockCount--;
68-
}
69-
else
70-
{
71-
_lockCount++;
72-
}
73-
}
74-
75-
public void SetSubmodules(List<Submodule> submodules)
79+
public IDisposable Lock()
7680
{
77-
lock (_lockSubmodule)
78-
{
79-
_submodules.Clear();
80-
foreach (var submodule in submodules)
81-
_submodules.Add(submodule.Path);
82-
}
81+
return new LockContext(this);
8382
}
8483

8584
public void MarkBranchUpdated()
8685
{
87-
_updateBranch=0;
86+
Interlocked.Exchange(ref_updateBranch,0);
8887
}
8988

9089
public void MarkTagUpdated()
9190
{
92-
_updateTags=0;
91+
Interlocked.Exchange(ref_updateTags,0);
9392
}
9493

9594
public void MarkWorkingCopyUpdated()
9695
{
97-
_updateWC = 0;
96+
Interlocked.Exchange(ref _updateWC, 0);
97+
}
98+
99+
public void MarkStashUpdated()
100+
{
101+
Interlocked.Exchange(ref _updateStashes, 0);
98102
}
99103

100104
public void Dispose()
@@ -112,57 +116,81 @@ public void Dispose()
112116

113117
private void Tick(object sender)
114118
{
115-
if (_lockCount > 0)
119+
if (Interlocked.Read(ref_lockCount) > 0)
116120
return;
117121

118122
var now = DateTime.Now.ToFileTime();
119-
if (_updateBranch > 0 && now > _updateBranch)
120-
{
121-
_updateBranch = 0;
122-
_updateWC = 0;
123+
var refreshCommits = false;
124+
var refreshSubmodules = false;
123125

124-
if (_updateTags > 0)
126+
var oldUpdateBranch = Interlocked.Exchange(ref _updateBranch, -1);
127+
if (oldUpdateBranch > 0)
128+
{
129+
if (now > oldUpdateBranch)
125130
{
126-
_updateTags = 0;
127-
_repo.RefreshTags();
128-
}
131+
refreshCommits = true;
132+
refreshSubmodules = _repo.MayHaveSubmodules();
129133

130-
if (_updateSubmodules > 0 || _repo.MayHaveSubmodules())
134+
_repo.RefreshBranches();
135+
_repo.RefreshWorktrees();
136+
}
137+
else
131138
{
132-
_updateSubmodules = 0;
133-
_repo.RefreshSubmodules();
139+
Interlocked.CompareExchange(ref _updateBranch, oldUpdateBranch, -1);
134140
}
135-
136-
_repo.RefreshBranches();
137-
_repo.RefreshCommits();
138-
_repo.RefreshWorkingCopyChanges();
139-
_repo.RefreshWorktrees();
140141
}
141142

142-
if (_updateWC > 0 && now > _updateWC)
143+
var oldUpdateWC = Interlocked.Exchange(ref _updateWC, -1);
144+
if (oldUpdateWC > 0)
143145
{
144-
_updateWC = 0;
145-
_repo.RefreshWorkingCopyChanges();
146+
if (now > oldUpdateWC)
147+
_repo.RefreshWorkingCopyChanges();
148+
else
149+
Interlocked.CompareExchange(ref _updateWC, oldUpdateWC, -1);
146150
}
147151

148-
if (_updateSubmodules>0&&now>_updateSubmodules)
152+
if (refreshSubmodules)
149153
{
150-
_updateSubmodules=0;
154+
Interlocked.Exchange(ref_updateSubmodules,-1);
151155
_repo.RefreshSubmodules();
152156
}
157+
else
158+
{
159+
var oldUpdateSubmodule = Interlocked.Exchange(ref _updateSubmodules, -1);
160+
if (oldUpdateSubmodule > 0)
161+
{
162+
if (now > oldUpdateSubmodule)
163+
_repo.RefreshSubmodules();
164+
else
165+
Interlocked.CompareExchange(ref _updateSubmodules, oldUpdateSubmodule, -1);
166+
}
167+
}
153168

154-
if (_updateStashes > 0 && now > _updateStashes)
169+
var oldUpdateStashes = Interlocked.Exchange(ref _updateStashes, -1);
170+
if (oldUpdateStashes > 0)
155171
{
156-
_updateStashes = 0;
157-
_repo.RefreshStashes();
172+
if (now > oldUpdateStashes)
173+
_repo.RefreshStashes();
174+
else
175+
Interlocked.CompareExchange(ref _updateStashes, oldUpdateStashes, -1);
158176
}
159177

160-
if (_updateTags > 0 && now > _updateTags)
178+
var oldUpdateTags = Interlocked.Exchange(ref _updateTags, -1);
179+
if (oldUpdateTags > 0)
161180
{
162-
_updateTags = 0;
163-
_repo.RefreshTags();
164-
_repo.RefreshCommits();
181+
if (now > oldUpdateTags)
182+
{
183+
refreshCommits = true;
184+
_repo.RefreshTags();
185+
}
186+
else
187+
{
188+
Interlocked.CompareExchange(ref _updateTags, oldUpdateTags, -1);
189+
}
165190
}
191+
192+
if (refreshCommits)
193+
_repo.RefreshCommits();
166194
}
167195

168196
private void OnRepositoryChanged(object o, FileSystemEventArgs e)
@@ -177,7 +205,7 @@ private void OnRepositoryChanged(object o, FileSystemEventArgs e)
177205
if (name.StartsWith(".git/", StringComparison.Ordinal))
178206
HandleGitDirFileChanged(name.Substring(5));
179207
else
180-
HandleWorkingCopyFileChanged(name);
208+
HandleWorkingCopyFileChanged(name,e.FullPath);
181209
}
182210

183211
private void OnGitDirChanged(object o, FileSystemEventArgs e)
@@ -200,7 +228,7 @@ private void OnWorkingCopyChanged(object o, FileSystemEventArgs e)
200228
name.EndsWith("/.git", StringComparison.Ordinal))
201229
return;
202230

203-
HandleWorkingCopyFileChanged(name);
231+
HandleWorkingCopyFileChanged(name,e.FullPath);
204232
}
205233

206234
private void HandleGitDirFileChanged(string name)
@@ -215,76 +243,84 @@ private void HandleGitDirFileChanged(string name)
215243
if (name.EndsWith("/HEAD", StringComparison.Ordinal) ||
216244
name.EndsWith("/ORIG_HEAD", StringComparison.Ordinal))
217245
{
218-
_updateSubmodules = DateTime.Now.AddSeconds(1).ToFileTime();
219-
_updateWC = DateTime.Now.AddSeconds(1).ToFileTime();
246+
var desired = DateTime.Now.AddSeconds(1).ToFileTime();
247+
Interlocked.Exchange(ref _updateSubmodules, desired);
248+
Interlocked.Exchange(ref _updateWC, desired);
220249
}
221250
}
222251
else if (name.Equals("MERGE_HEAD", StringComparison.Ordinal) ||
223252
name.Equals("AUTO_MERGE", StringComparison.Ordinal))
224253
{
225254
if (_repo.MayHaveSubmodules())
226-
_updateSubmodules=DateTime.Now.AddSeconds(1).ToFileTime();
255+
Interlocked.Exchange(ref_updateSubmodules,DateTime.Now.AddSeconds(1).ToFileTime());
227256
}
228257
else if (name.StartsWith("refs/tags", StringComparison.Ordinal))
229258
{
230-
_updateTags=DateTime.Now.AddSeconds(.5).ToFileTime();
259+
Interlocked.Exchange(ref_updateTags,DateTime.Now.AddSeconds(.5).ToFileTime());
231260
}
232261
else if (name.StartsWith("refs/stash", StringComparison.Ordinal))
233262
{
234-
_updateStashes=DateTime.Now.AddSeconds(.5).ToFileTime();
263+
Interlocked.Exchange(ref_updateStashes,DateTime.Now.AddSeconds(.5).ToFileTime());
235264
}
236265
else if (name.Equals("HEAD", StringComparison.Ordinal) ||
237266
name.Equals("BISECT_START", StringComparison.Ordinal) ||
238267
name.StartsWith("refs/heads/", StringComparison.Ordinal) ||
239268
name.StartsWith("refs/remotes/", StringComparison.Ordinal) ||
240269
(name.StartsWith("worktrees/", StringComparison.Ordinal) && name.EndsWith("/HEAD", StringComparison.Ordinal)))
241270
{
242-
_updateBranch=DateTime.Now.AddSeconds(.5).ToFileTime();
271+
Interlocked.Exchange(ref_updateBranch,DateTime.Now.AddSeconds(.5).ToFileTime());
243272
}
244273
else if (name.StartsWith("objects/", StringComparison.Ordinal) || name.Equals("index", StringComparison.Ordinal))
245274
{
246-
_updateWC=DateTime.Now.AddSeconds(1).ToFileTime();
275+
Interlocked.Exchange(ref_updateWC,DateTime.Now.AddSeconds(1).ToFileTime());
247276
}
248277
}
249278

250-
private void HandleWorkingCopyFileChanged(string name)
279+
private void HandleWorkingCopyFileChanged(string name,stringfullpath)
251280
{
252281
if (name.StartsWith(".vs/", StringComparison.Ordinal))
253282
return;
254283

255284
if (name.Equals(".gitmodules", StringComparison.Ordinal))
256285
{
257-
_updateSubmodules = DateTime.Now.AddSeconds(1).ToFileTime();
258-
_updateWC = DateTime.Now.AddSeconds(1).ToFileTime();
286+
var desired = DateTime.Now.AddSeconds(1).ToFileTime();
287+
Interlocked.Exchange(ref _updateSubmodules, desired);
288+
Interlocked.Exchange(ref _updateWC, desired);
259289
return;
260290
}
261291

262-
lock (_lockSubmodule)
292+
var dir = Directory.Exists(fullpath) ? fullpath : Path.GetDirectoryName(fullpath);
293+
if (IsInSubmodule(dir))
263294
{
264-
foreach (var submodule in _submodules)
265-
{
266-
if (name.StartsWith(submodule, StringComparison.Ordinal))
267-
{
268-
_updateSubmodules = DateTime.Now.AddSeconds(1).ToFileTime();
269-
return;
270-
}
271-
}
295+
Interlocked.Exchange(ref _updateSubmodules, DateTime.Now.AddSeconds(1).ToFileTime());
296+
return;
272297
}
273298

274-
_updateWC = DateTime.Now.AddSeconds(1).ToFileTime();
299+
Interlocked.Exchange(ref _updateWC, DateTime.Now.AddSeconds(1).ToFileTime());
300+
}
301+
302+
private bool IsInSubmodule(string folder)
303+
{
304+
if (File.Exists($"{folder}/.git"))
305+
return true;
306+
307+
var parent = Path.GetDirectoryName(folder);
308+
if (parent == null || parent.Equals(_root, StringComparison.Ordinal))
309+
return false;
310+
311+
return IsInSubmodule(parent);
275312
}
276313

277314
private readonly IRepository _repo = null;
315+
private readonly string _root = null;
278316
private List<FileSystemWatcher> _watchers = [];
279317
private Timer _timer = null;
280-
private int _lockCount = 0;
318+
319+
private long _lockCount = 0;
281320
private long _updateWC = 0;
282321
private long _updateBranch = 0;
283322
private long _updateSubmodules = 0;
284323
private long _updateStashes = 0;
285324
private long _updateTags = 0;
286-
287-
private readonly Lock _lockSubmodule = new();
288-
private List<string> _submodules = new List<string>();
289325
}
290326
}

‎src/ViewModels/AddRemote.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public static ValidationResult ValidateSSHKey(string sshkey, ValidationContext c
8989

9090
public override async Task<bool> Sure()
9191
{
92-
_repo.SetWatcherEnabled(false);
92+
usingvarlockWatcher=_repo.LockWatcher();
9393
ProgressDescription = "Adding remote ...";
9494

9595
var log = _repo.CreateLog("Add Remote");
@@ -114,7 +114,6 @@ public override async Task<bool> Sure()
114114

115115
_repo.MarkFetched();
116116
_repo.MarkBranchesDirtyManually();
117-
_repo.SetWatcherEnabled(true);
118117
return succ;
119118
}
120119

‎src/ViewModels/AddSubmodule.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public static ValidationResult ValidateURL(string url, ValidationContext ctx)
4242

4343
public override async Task<bool> Sure()
4444
{
45-
_repo.SetWatcherEnabled(false);
45+
usingvarlockWatcher=_repo.LockWatcher();
4646
ProgressDescription = "Adding submodule...";
4747

4848
var log = _repo.CreateLog("Add Submodule");
@@ -64,7 +64,6 @@ public override async Task<bool> Sure()
6464
.AddAsync(_url, relativePath, Recursive);
6565

6666
log.Complete();
67-
_repo.SetWatcherEnabled(true);
6867
return succ;
6968
}
7069

‎src/ViewModels/AddToIgnore.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public AddToIgnore(Repository repo, string pattern)
2929

3030
public override async Task<bool> Sure()
3131
{
32-
_repo.SetWatcherEnabled(false);
32+
usingvarlockWatcher=_repo.LockWatcher();
3333
ProgressDescription = "Adding Ignored File(s) ...";
3434

3535
var file = StorageFile.GetFullPath(_repo.FullPath, _repo.GitDir);
@@ -47,7 +47,6 @@ public override async Task<bool> Sure()
4747
}
4848

4949
_repo.MarkWorkingCopyDirtyManually();
50-
_repo.SetWatcherEnabled(true);
5150
return true;
5251
}
5352

‎src/ViewModels/AddWorktree.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public static ValidationResult ValidateWorktreePath(string path, ValidationConte
106106

107107
public override async Task<bool> Sure()
108108
{
109-
_repo.SetWatcherEnabled(false);
109+
usingvarlockWatcher=_repo.LockWatcher();
110110
ProgressDescription = "Adding worktree ...";
111111

112112
var branchName = _selectedBranch;
@@ -120,7 +120,6 @@ public override async Task<bool> Sure()
120120
.AddAsync(_path, branchName, _createNewBranch, tracking);
121121

122122
log.Complete();
123-
_repo.SetWatcherEnabled(true);
124123
return succ;
125124
}
126125

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /