private void FindConflictsImpl(string basePath, SlimObservable<ConflictSet> subject, CancellationToken cancellationToken) { // We may find may conflict files for each conflict, and we need to group them. // We can't relay on the order returns by EnumerateFiles either, so it's hard to tell when we've spotted // all conflict files. Therefore we need to do this directory by directory, and flush out the cache // or conflicts after each directory. logger.Debug("Looking for conflicts in {0}", basePath); var conflictLookup = new Dictionary<string, List<ParsedConflictFileInfo>>(); var stack = new Stack<SearchDirectory>(); stack.Push(new SearchDirectory(basePath, 0)); while (stack.Count > 0) { cancellationToken.ThrowIfCancellationRequested(); conflictLookup.Clear(); var searchDirectory = stack.Pop(); var directory = searchDirectory.Directory; this.TryFilesystemOperation(() => { foreach (var filePath in this.filesystemProvider.EnumerateFiles(directory, conflictPattern, System.IO.SearchOption.TopDirectoryOnly)) { if (this.IsFileIgnored(filePath)) continue; ParsedConflictFileInfo conflictFileInfo; // We may not be able to parse it properly (conflictPattern is pretty basic), or it might not exist, or... if (!this.TryFindBaseFileForConflictFile(filePath, out conflictFileInfo)) continue; List<ParsedConflictFileInfo> existingConflicts; if (!conflictLookup.TryGetValue(conflictFileInfo.OriginalPath, out existingConflicts)) { existingConflicts = new List<ParsedConflictFileInfo>(); conflictLookup.Add(conflictFileInfo.OriginalPath, existingConflicts); } existingConflicts.Add(conflictFileInfo); cancellationToken.ThrowIfCancellationRequested(); } }, directory, "directories"); foreach (var kvp in conflictLookup) { var file = new ConflictFile(kvp.Key, this.filesystemProvider.GetLastWriteTime(kvp.Key), this.filesystemProvider.GetFileSize(kvp.Key)); var conflicts = kvp.Value.Select(x => new ConflictOption(x.FilePath, this.filesystemProvider.GetLastWriteTime(x.FilePath), x.Created, this.filesystemProvider.GetFileSize(x.FilePath))).ToList(); subject.Next(new ConflictSet(file, conflicts)); } if (searchDirectory.Depth < maxSearchDepth) { this.TryFilesystemOperation(() => { foreach (var subDirectory in this.filesystemProvider.EnumerateDirectories(directory, "*", System.IO.SearchOption.TopDirectoryOnly)) { if (IsPathIgnored(subDirectory)) continue; stack.Push(new SearchDirectory(subDirectory, searchDirectory.Depth + 1)); cancellationToken.ThrowIfCancellationRequested(); } }, directory, "files"); } else { logger.Warn($"Max search depth of {maxSearchDepth} exceeded with path {directory}. Not proceeding further."); } } }
public IObservable<ConflictSet> FindConflicts(string basePath, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var subject = new SlimObservable<ConflictSet>(); Task.Run(() => { try { this.FindConflictsImpl(basePath, subject, cancellationToken); subject.Complete(); } catch (Exception e) { subject.Error(e); } }); return subject; }