Skip to content

Commit

Permalink
Merge pull request #19 from corytodd/exclude-repos
Browse files Browse the repository at this point in the history
add exclude specific  repos feature
  • Loading branch information
corytodd authored Apr 26, 2024
2 parents 67193c2 + c71b64c commit 50e4a4e
Show file tree
Hide file tree
Showing 12 changed files with 137 additions and 28 deletions.
40 changes: 25 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,45 @@ 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:

```
{
"DirectoryScanDepth": 3,
"RepositoryRoots": [
"H:\\code",
"H:\\code"
],
"AuthorAliases": {
"illyum": [
"[email protected]",
"[email protected]"
]
}
"illyum": [
"[email protected]",
"[email protected]"
]
},
"IgnorePaths": [
"H:\\code\\llvm"
]
}
```

Expand All @@ -48,7 +59,7 @@ Show the active configuration
```
walrusc show config
```

Print a list of all repositories
```
walrusc show repos
Expand All @@ -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
```
Expand Down Expand Up @@ -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
2 changes: 1 addition & 1 deletion Walrus.CLI/Walrus.CLI.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<SemVer>0.2.0</SemVer>
<SemVer>0.3.0</SemVer>
<Suffix></Suffix>
</PropertyGroup>

Expand Down
12 changes: 12 additions & 0 deletions Walrus.Core.Tests/MockRepoProvider.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace Walrus.Core.Tests
{
using System;
using System.Collections.Generic;
using Repository;

Expand Down Expand Up @@ -93,5 +94,16 @@ public IEnumerable<WalrusRepository> GetRepositories(string rootDirectory, int s
})
};
}

public IEnumerable<WalrusRepository> GetRepositories(string rootDirectory, int scanDepth, bool allBranches, Predicate<string> excludeFilter = null)
{
foreach(var repo in GetRepositories(rootDirectory, scanDepth, allBranches))
{
if (!excludeFilter(repo.RepositoryPath))
{
yield return repo;
}
}
}
}
}
4 changes: 2 additions & 2 deletions Walrus.Core.Tests/PathHelperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public IEnumerator<object[]> 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
Expand All @@ -32,7 +32,7 @@ public IEnumerator<object[]> 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" };
}
}
Expand Down
29 changes: 29 additions & 0 deletions Walrus.Core.Tests/WalrusConfigTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ namespace Walrus.Core.Tests
using System.Linq;
using System.Collections.Generic;
using Xunit;
using Newtonsoft.Json;

/// <summary>
/// WalrusConfig tests
Expand Down Expand Up @@ -49,5 +50,33 @@ public void EmptyPathRootTest()
// Execute
Assert.Throws<WalrusConfigurationException>(() => config.ValidateOrThrow());
}

/// <summary>
/// Allow existing configurations to work without
/// specifying a repo ignore list.
/// </summary>
[Fact]
public void MissingIgnoredRepos()
{
// Setup
var json = """
{
"DirectoryScanDepth": 3,
"RepositoryRoots": [
"H:\\code"
],
"AuthorAliases": {
"illyum": [
"[email protected]",
"[email protected]"
]
}
}
""";

// Execute
var config = JsonConvert.DeserializeObject<WalrusConfig>(json);
Assert.Empty(config.IgnoredRepos);
}
}
}
6 changes: 6 additions & 0 deletions Walrus.Core/IWalrusConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ public interface IWalrusConfig
/// </summary>
IDictionary<string, IList<string>>? AuthorAliases { get; set; }

/// <summary>
/// List of repository names to ignore
/// This is the full path of the repo to ignore
/// </summary>
IList<string> IgnoredRepos { get; set; }

/// <summary>
/// Validates self or throws WalrusConfigurationException
/// </summary>
Expand Down
30 changes: 28 additions & 2 deletions Walrus.Core/PathHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ namespace Walrus.Core
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Extensions.Logging;

/// <summary>
/// Path helper utilities
Expand All @@ -14,8 +16,8 @@ public static class PathHelper
/// Automatically resolve all environmental variables and
/// the home tilda (~/) in path.
/// </summary>
/// <param name="path">Path to resovle</param>
/// <return>Resolved path</return>
/// <param name="path">Path to resolve</param>
/// <return>Absolute resolved path</return>
public static string ResolvePath(string path)
{
ArgumentNullException.ThrowIfNull(path);
Expand All @@ -27,11 +29,35 @@ public static string ResolvePath(string path)
}

path = ResolveAllEnvironmentVariables(path);
path = path.TrimEnd(Path.DirectorySeparatorChar);

return path;

}

/// <summary>
/// Returns true if <paramref name="pathList"/> contains <paramref name="repositoryPath"/>.
/// </summary>
/// <param name="ignoredRepos">List of paths to test against</param>
/// <param name="repositoryPath"></param>
/// <returns></returns>
internal static bool ContainsPath(IList<string> 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;
}

/// <summary>
/// Split path into components and resolve them if they're environmental
/// variables. It turns out Environment.ExpandEnvironmentVariables does
Expand Down
3 changes: 2 additions & 1 deletion Walrus.Core/Repository/IRepositoryProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ public interface IRepositoryProvider
/// <param name="rootDirectory">Directory to search for Git repositories</param>
/// <param name="scanDepth">Recursively scan to this depth. Must be greater than or equal to zero.</param>
/// <param name="allBranches">If true, include all branches in commit collection for each repository.</param>
/// <param name="excludeFilter">Optional filter returns false for repo paths that should be excluded.</param>
/// <returns>List of repositories</returns>
/// <exception cref="ArgumentException"><paramref name="scanDepth" /> is less than 0</exception>
IEnumerable<WalrusRepository> GetRepositories(string rootDirectory, int scanDepth, bool allBranches);
IEnumerable<WalrusRepository> GetRepositories(string rootDirectory, int scanDepth, bool allBranches, Predicate<string>? excludeFilter =null);
}
}
10 changes: 9 additions & 1 deletion Walrus.Core/Repository/RepositoryProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@
public class RepositoryProvider : IRepositoryProvider
{
/// <inheritdoc />
public IEnumerable<WalrusRepository> GetRepositories(string rootDirectory, int scanDepth, bool allBranches)
public IEnumerable<WalrusRepository> GetRepositories(string rootDirectory, int scanDepth, bool allBranches, Predicate<string>? 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)
Expand All @@ -31,6 +34,11 @@ public IEnumerable<WalrusRepository> 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);
Expand Down
10 changes: 10 additions & 0 deletions Walrus.Core/WalrusConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
/// </summary>
public sealed class WalrusConfig : IWalrusConfig
{

private readonly List<string> _ignoredRepos = new();

/// <summary>
/// Generate a default configuration
/// </summary>
Expand All @@ -26,6 +29,13 @@ public sealed class WalrusConfig : IWalrusConfig
/// <inheritdoc />
public IDictionary<string, IList<string>>? AuthorAliases { get; set; }

/// <inheritdoc />
public IList<string> IgnoredRepos
{
get => _ignoredRepos.Select(PathHelper.ResolvePath).ToList();
set => _ignoredRepos.AddRange(value);
}

/// <inheritdoc />
public void ValidateOrThrow()
{
Expand Down
6 changes: 5 additions & 1 deletion Walrus.Core/WalrusService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class WalrusService : IWalrusService
{
private readonly ILogger _logger;
private readonly IRepositoryProvider _repositoryProvider;
private readonly ISet<string> _ignoredPaths;

/// <summary>
/// Create a new WalrusService
Expand All @@ -31,6 +32,7 @@ public WalrusService(ILogger<WalrusService> logger, IRepositoryProvider reposito
_logger = logger;
_repositoryProvider = repositoryProvider;
Config = config;
_ignoredPaths = new HashSet<string>(Config.IgnoredRepos);
}

/// <inheritdoc />
Expand Down Expand Up @@ -103,7 +105,7 @@ private IEnumerable<WalrusRepository> 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)
Expand All @@ -112,5 +114,7 @@ private IEnumerable<WalrusRepository> QueryRepositories(PreparedWalrusQuery quer
}
}
}

private bool ExcludeReposFilter(string repoPath) => PathHelper.ContainsPath(Config.IgnoredRepos, repoPath);
}
}
13 changes: 8 additions & 5 deletions samples/walrus.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
"H:\\code"
],
"AuthorAliases": {
"illyum": [
"[email protected]",
"[email protected]"
]
}
"illyum": [
"[email protected]",
"[email protected]"
]
},
"IgnorePaths": [
"H:\\code\\llvm"
]
}

0 comments on commit 50e4a4e

Please sign in to comment.