diff --git a/README.md b/README.md index 7620fe7..025d86f 100644 --- a/README.md +++ b/README.md @@ -6,19 +6,27 @@ A multi-repo Git query tool ## Purpose -I spend a fair amount of time tracking down the what and when of my daily activities. Usually this searching spans many (10+) repositories which means the task is boring and tedious. Walrus -tool aims to automate the discovery of git commits based on date, author, and other criteria. +I spend a fair amount of time tracking down the what and when of my daily activities. Usually this searching spans many (10+) repositories which means the task is boring and tedious. Walrus +tool aims to automate the discovery of git commits based on date, author, and other criteria. ## Goals We don't want to reinvent libgit2sharp, we just want a simple query interface specific to commit messages. We don't care about diffs, code content, or even the change sets. -For this reason, Gitbase was found to be entirely overkill even though it is pretty rad. It is a fantastic tool and I do make use of it but managing it is kind of a pain. For this reason, the +For this reason, Gitbase was found to be entirely overkill even though it is pretty rad. It is a fantastic tool and I do make use of it but managing it is kind of a pain. For this reason, the overarching goal of this project is simplicity with the ability to opt-in to complexity when needed. ## Getting Started This utility should be used as a dotnet tool. See `tools/install_as_tool.ps1` for how to build and install Walrus on your system. +``` +# Windows +.\tools\install_as_tool.ps1 + +# Not Windows +bash tools/install_as_tool.ps1 +``` + To query multiple roots or query from any location, you will need to setup a simple JSON config file. This can be specified as an env `WALRUS_CONFIG_FILE` or a file named `walrus.json` placed in your current working directory. The contents should look something like this is: @@ -26,14 +34,17 @@ named `walrus.json` placed in your current working directory. The contents shoul { "DirectoryScanDepth": 3, "RepositoryRoots": [ - "H:\\code", + "H:\\code" ], "AuthorAliases": { - "illyum": [ - "illy@home.com", - "illy@work.com" - ] - } + "illyum": [ + "illy@home.com", + "illy@work.com" + ] + }, + "IgnorePaths": [ + "H:\\code\\llvm" + ] } ``` @@ -48,7 +59,7 @@ Show the active configuration ``` walrusc show config ``` - + Print a list of all repositories ``` walrusc show repos @@ -58,12 +69,12 @@ Use the default scan depth and search for repositories relative to your current ``` walrusc query --current-directory ``` - + Count commits between March 2 and Jun 2 of 2021. This includes all authors on the currently checked out branch in each repository ``` walrusc query --after "Mar 2 2021" --before "Jun 2 2021" ``` - + Show all commits on all branches since last week (default if --after is not specified) and restrict to a single author ``` @@ -122,8 +133,7 @@ walrusc query --author-alias illyum - [x] Basic date/author query interface - [x] Unit tests -- [x] Simple CLI +- [x] Simple CLI - [x] Table style CLI output - [x] Create dotnet tool -- [x] CI tests -- [ ] Calendar style GUI +- [x] CI tests diff --git a/Walrus.CLI/Walrus.CLI.csproj b/Walrus.CLI/Walrus.CLI.csproj index 68539ca..d4d0cab 100644 --- a/Walrus.CLI/Walrus.CLI.csproj +++ b/Walrus.CLI/Walrus.CLI.csproj @@ -1,7 +1,7 @@ - 0.2.0 + 0.3.0 diff --git a/Walrus.Core.Tests/MockRepoProvider.cs b/Walrus.Core.Tests/MockRepoProvider.cs index 971301c..78af6d0 100644 --- a/Walrus.Core.Tests/MockRepoProvider.cs +++ b/Walrus.Core.Tests/MockRepoProvider.cs @@ -1,5 +1,6 @@ namespace Walrus.Core.Tests { + using System; using System.Collections.Generic; using Repository; @@ -93,5 +94,16 @@ public IEnumerable GetRepositories(string rootDirectory, int s }) }; } + + public IEnumerable GetRepositories(string rootDirectory, int scanDepth, bool allBranches, Predicate excludeFilter = null) + { + foreach(var repo in GetRepositories(rootDirectory, scanDepth, allBranches)) + { + if (!excludeFilter(repo.RepositoryPath)) + { + yield return repo; + } + } + } } } \ No newline at end of file diff --git a/Walrus.Core.Tests/PathHelperTests.cs b/Walrus.Core.Tests/PathHelperTests.cs index 365c36a..b4a74dc 100644 --- a/Walrus.Core.Tests/PathHelperTests.cs +++ b/Walrus.Core.Tests/PathHelperTests.cs @@ -23,7 +23,7 @@ public IEnumerator GetEnumerator() yield return new object[] { "relative", "relative" }; yield return new object[] { @"relative\path\to\boot", @"relative\path\to\boot" }; yield return new object[] { @"relative\path\to\..\boot", @"relative\path\to\..\boot" }; - yield return new object[] { @"~/", @"C:\Users\user\" }; + yield return new object[] { @"~/", @"C:\Users\user" }; yield return new object[] { @"~/%FOO%", @"C:\Users\user\foo" }; } else @@ -32,7 +32,7 @@ public IEnumerator GetEnumerator() yield return new object[] { "relative", "relative" }; yield return new object[] { "relative/path/to/boot", "relative/path/to/boot" }; yield return new object[] { "relative/path/to/../boot", "relative/path/to/../boot" }; - yield return new object[] { "~/", "/home/user/" }; + yield return new object[] { "~/", "/home/user" }; yield return new object[] { "~/$FOO", "/home/user/foo" }; } } diff --git a/Walrus.Core.Tests/WalrusConfigTests.cs b/Walrus.Core.Tests/WalrusConfigTests.cs index 737ff84..99f6392 100644 --- a/Walrus.Core.Tests/WalrusConfigTests.cs +++ b/Walrus.Core.Tests/WalrusConfigTests.cs @@ -3,6 +3,7 @@ namespace Walrus.Core.Tests using System.Linq; using System.Collections.Generic; using Xunit; + using Newtonsoft.Json; /// /// WalrusConfig tests @@ -49,5 +50,33 @@ public void EmptyPathRootTest() // Execute Assert.Throws(() => config.ValidateOrThrow()); } + + /// + /// Allow existing configurations to work without + /// specifying a repo ignore list. + /// + [Fact] + public void MissingIgnoredRepos() + { + // Setup + var json = """ + { + "DirectoryScanDepth": 3, + "RepositoryRoots": [ + "H:\\code" + ], + "AuthorAliases": { + "illyum": [ + "illy@home.com", + "illy@work.com" + ] + } + } + """; + + // Execute + var config = JsonConvert.DeserializeObject(json); + Assert.Empty(config.IgnoredRepos); + } } } \ No newline at end of file diff --git a/Walrus.Core/IWalrusConfig.cs b/Walrus.Core/IWalrusConfig.cs index e269574..b0bb4f3 100644 --- a/Walrus.Core/IWalrusConfig.cs +++ b/Walrus.Core/IWalrusConfig.cs @@ -26,6 +26,12 @@ public interface IWalrusConfig /// IDictionary>? AuthorAliases { get; set; } + /// + /// List of repository names to ignore + /// This is the full path of the repo to ignore + /// + IList IgnoredRepos { get; set; } + /// /// Validates self or throws WalrusConfigurationException /// diff --git a/Walrus.Core/PathHelper.cs b/Walrus.Core/PathHelper.cs index 65a34eb..aea7130 100644 --- a/Walrus.Core/PathHelper.cs +++ b/Walrus.Core/PathHelper.cs @@ -3,6 +3,8 @@ namespace Walrus.Core using System; using System.Collections.Generic; using System.IO; + using System.Linq; + using Microsoft.Extensions.Logging; /// /// Path helper utilities @@ -14,8 +16,8 @@ public static class PathHelper /// Automatically resolve all environmental variables and /// the home tilda (~/) in path. /// - /// Path to resovle - /// Resolved path + /// Path to resolve + /// Absolute resolved path public static string ResolvePath(string path) { ArgumentNullException.ThrowIfNull(path); @@ -27,11 +29,35 @@ public static string ResolvePath(string path) } path = ResolveAllEnvironmentVariables(path); + path = path.TrimEnd(Path.DirectorySeparatorChar); return path; } + /// + /// Returns true if contains . + /// + /// List of paths to test against + /// + /// + internal static bool ContainsPath(IList pathList, string repositoryPath) + { + var found = false; + var normalizedPath = Path.GetFullPath(ResolvePath(repositoryPath)); + foreach(var ignoredRepo in pathList) + { + var normalizedIgnoredRepo = Path.GetFullPath(ResolvePath(ignoredRepo)); + if (normalizedIgnoredRepo == normalizedPath) + { + WalrusLog.Logger.LogDebug("Ignoring repository {RepositoryPath}", repositoryPath); + found = true; + break; + } + } + return found; + } + /// /// Split path into components and resolve them if they're environmental /// variables. It turns out Environment.ExpandEnvironmentVariables does diff --git a/Walrus.Core/Repository/IRepositoryProvider.cs b/Walrus.Core/Repository/IRepositoryProvider.cs index 782cde2..c0c483e 100644 --- a/Walrus.Core/Repository/IRepositoryProvider.cs +++ b/Walrus.Core/Repository/IRepositoryProvider.cs @@ -14,8 +14,9 @@ public interface IRepositoryProvider /// Directory to search for Git repositories /// Recursively scan to this depth. Must be greater than or equal to zero. /// If true, include all branches in commit collection for each repository. + /// Optional filter returns false for repo paths that should be excluded. /// List of repositories /// is less than 0 - IEnumerable GetRepositories(string rootDirectory, int scanDepth, bool allBranches); + IEnumerable GetRepositories(string rootDirectory, int scanDepth, bool allBranches, Predicate? excludeFilter =null); } } \ No newline at end of file diff --git a/Walrus.Core/Repository/RepositoryProvider.cs b/Walrus.Core/Repository/RepositoryProvider.cs index 08dc46c..e3b5a09 100644 --- a/Walrus.Core/Repository/RepositoryProvider.cs +++ b/Walrus.Core/Repository/RepositoryProvider.cs @@ -13,11 +13,14 @@ public class RepositoryProvider : IRepositoryProvider { /// - public IEnumerable GetRepositories(string rootDirectory, int scanDepth, bool allBranches) + public IEnumerable GetRepositories(string rootDirectory, int scanDepth, bool allBranches, Predicate? excludeFilter =null) { Ensure.IsValid(nameof(rootDirectory), !string.IsNullOrEmpty(rootDirectory)); Ensure.IsValid(nameof(scanDepth), scanDepth >= 0); + // Accept everything by default + excludeFilter ??= (string s) => false; + var directories = Utilities.EnumerateGitDirectoriesToDepth(rootDirectory, scanDepth); foreach (var directory in directories) @@ -31,6 +34,11 @@ public IEnumerable GetRepositories(string rootDirectory, int s // If neither the path or the working directory are set Ensure.IsValid(nameof(repoPath), !string.IsNullOrEmpty(repoPath), "Expected a valid repo path to be set. This is a bug."); + if (excludeFilter(repoPath)) + { + continue; + } + var commits = GetCommits(repo, allBranches); var repository = new WalrusRepository(repoPath, commits); diff --git a/Walrus.Core/WalrusConfig.cs b/Walrus.Core/WalrusConfig.cs index a4be69d..a228b65 100644 --- a/Walrus.Core/WalrusConfig.cs +++ b/Walrus.Core/WalrusConfig.cs @@ -9,6 +9,9 @@ /// public sealed class WalrusConfig : IWalrusConfig { + + private readonly List _ignoredRepos = new(); + /// /// Generate a default configuration /// @@ -26,6 +29,13 @@ public sealed class WalrusConfig : IWalrusConfig /// public IDictionary>? AuthorAliases { get; set; } + /// + public IList IgnoredRepos + { + get => _ignoredRepos.Select(PathHelper.ResolvePath).ToList(); + set => _ignoredRepos.AddRange(value); + } + /// public void ValidateOrThrow() { diff --git a/Walrus.Core/WalrusService.cs b/Walrus.Core/WalrusService.cs index 4234cb0..3871bdc 100644 --- a/Walrus.Core/WalrusService.cs +++ b/Walrus.Core/WalrusService.cs @@ -14,6 +14,7 @@ public class WalrusService : IWalrusService { private readonly ILogger _logger; private readonly IRepositoryProvider _repositoryProvider; + private readonly ISet _ignoredPaths; /// /// Create a new WalrusService @@ -31,6 +32,7 @@ public WalrusService(ILogger logger, IRepositoryProvider reposito _logger = logger; _repositoryProvider = repositoryProvider; Config = config; + _ignoredPaths = new HashSet(Config.IgnoredRepos); } /// @@ -103,7 +105,7 @@ private IEnumerable QueryRepositories(PreparedWalrusQuery quer _logger.LogDebug("Searching {Path}", root); var repositories = _repositoryProvider - .GetRepositories(root, Config.DirectoryScanDepth, query.AllBranches) + .GetRepositories(root, Config.DirectoryScanDepth, query.AllBranches, ExcludeReposFilter) .Where(query.IsMatch); foreach (var repo in repositories) @@ -112,5 +114,7 @@ private IEnumerable QueryRepositories(PreparedWalrusQuery quer } } } + + private bool ExcludeReposFilter(string repoPath) => PathHelper.ContainsPath(Config.IgnoredRepos, repoPath); } } \ No newline at end of file diff --git a/samples/walrus.json b/samples/walrus.json index 8c27f13..a515c89 100644 --- a/samples/walrus.json +++ b/samples/walrus.json @@ -4,9 +4,12 @@ "H:\\code" ], "AuthorAliases": { - "illyum": [ - "illy@home.com", - "illy@work.com" - ] - } + "illyum": [ + "illy@home.com", + "illy@work.com" + ] + }, + "IgnorePaths": [ + "H:\\code\\llvm" + ] }