Skip to content

Commit 1c00408

Browse files
Add --git option for explicit git mv control (#226)
Issue #84 requested evaluation of git mv benefits and adding it as an option. GitStorageManager (using LibGit2Sharp's `Commands.Move`) already existed but was only available via auto-detection. This adds explicit control via `--git` flag. ## Changes - **New `--git` flag on `update` command**: Forces use of GitStorageManager, errors if not in a git repository - **New constructor**: `ListingManager(DirectoryInfo, bool useGit)` for explicit storage manager selection - **Fixed git detection**: Changed from `Repository.IsValid()` to `Repository.Discover()` to properly detect repos from subdirectories - **Maintained backward compatibility**: Default constructor auto-detects, preferring GitStorageManager when available ## Behavior ```bash # Force git mv (errors if not in repo) ListingManager update Chapter03 --git # Auto-detect (existing behavior, now works from subdirs) ListingManager update Chapter03 ``` | Scenario | Result | |----------|--------| | `--git` in git repo | Uses GitStorageManager | | `--git` outside git repo | Error with clear message | | No `--git` in git repo | Auto-detects, uses GitStorageManager | | No `--git` outside git repo | Auto-detects, uses OSStorageManager | ## Why git mv matters Preserves file rename history for `git log --follow`, blame, and code review. <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Move files using git mv</issue_title> > <issue_description>Evaluate if git mv provides a significant difference in the way moved files are treated by git, and consider adding it as an option. I.E: --git > > This was started in #84 . There may not be, but see if enhancements can be made</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes #4 <!-- START COPILOT CODING AGENT TIPS --> --- ✨ Let Copilot coding agent [set things up for you](https://github.com/IntelliTect/EssentialCSharp.ListingManager/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: BenjaminMichaelis <22186029+BenjaminMichaelis@users.noreply.github.com>
1 parent 92ebd9c commit 1c00408

4 files changed

Lines changed: 148 additions & 20 deletions

File tree

ListingManager.Tests/ListingManagerTests.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,4 +1440,72 @@ public void UpdateAllChapterListingNumbers_ListingsWithinListMissing_ListingsRen
14401440
Assert.Equivalent(expectedFiles, files);
14411441
}
14421442
#endregion UpdateAllChaptersListingNumbers
1443+
1444+
#region Constructor with useGit parameter
1445+
[Fact]
1446+
public void Constructor_WithUseGitTrue_InGitRepo_UsesGitStorageManager()
1447+
{
1448+
// Arrange
1449+
DirectoryInfo tempDir = CreateTempDirectory();
1450+
Repository.Init(tempDir.FullName);
1451+
1452+
// Act
1453+
ListingManager listingManager = new(tempDir, useGit: true);
1454+
1455+
// Assert
1456+
Assert.IsType<GitStorageManager>(listingManager.StorageManager);
1457+
}
1458+
1459+
[Fact]
1460+
public void Constructor_WithUseGitFalse_UsesOSStorageManager()
1461+
{
1462+
// Arrange
1463+
DirectoryInfo tempDir = CreateTempDirectory();
1464+
1465+
// Act
1466+
ListingManager listingManager = new(tempDir, useGit: false);
1467+
1468+
// Assert
1469+
Assert.IsType<OSStorageManager>(listingManager.StorageManager);
1470+
}
1471+
1472+
[Fact]
1473+
public void Constructor_WithUseGitTrue_NotInGitRepo_ThrowsInvalidOperationException()
1474+
{
1475+
// Arrange
1476+
DirectoryInfo tempDir = CreateTempDirectory();
1477+
1478+
// Act & Assert
1479+
var exception = Assert.Throws<InvalidOperationException>(() => new ListingManager(tempDir, useGit: true));
1480+
Assert.Contains("--git option was specified", exception.Message);
1481+
Assert.Contains("not in a git repository", exception.Message);
1482+
}
1483+
1484+
[Fact]
1485+
public void Constructor_Default_InGitRepo_UsesGitStorageManager()
1486+
{
1487+
// Arrange
1488+
DirectoryInfo tempDir = CreateTempDirectory();
1489+
Repository.Init(tempDir.FullName);
1490+
1491+
// Act
1492+
ListingManager listingManager = new(tempDir);
1493+
1494+
// Assert
1495+
Assert.IsType<GitStorageManager>(listingManager.StorageManager);
1496+
}
1497+
1498+
[Fact]
1499+
public void Constructor_Default_NotInGitRepo_UsesOSStorageManager()
1500+
{
1501+
// Arrange
1502+
DirectoryInfo tempDir = CreateTempDirectory();
1503+
1504+
// Act
1505+
ListingManager listingManager = new(tempDir);
1506+
1507+
// Assert
1508+
Assert.IsType<OSStorageManager>(listingManager.StorageManager);
1509+
}
1510+
#endregion Constructor with useGit parameter
14431511
}

ListingManager/ListingManager.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,25 @@ public partial class ListingManager
1313

1414
public ListingManager(DirectoryInfo pathToChapter)
1515
{
16-
StorageManager = Repository.IsValid(pathToChapter.FullName) ? new GitStorageManager(pathToChapter.FullName) : new OSStorageManager();
16+
string? repoPath = Repository.Discover(pathToChapter.FullName);
17+
StorageManager = repoPath is not null ? new GitStorageManager(repoPath) : new OSStorageManager();
18+
}
19+
20+
public ListingManager(DirectoryInfo pathToChapter, bool useGit)
21+
{
22+
if (useGit)
23+
{
24+
string? repoPath = Repository.Discover(pathToChapter.FullName);
25+
if (repoPath is null)
26+
{
27+
throw new InvalidOperationException($"The --git option was specified, but the directory '{pathToChapter.FullName}' is not in a git repository.");
28+
}
29+
StorageManager = new GitStorageManager(repoPath);
30+
}
31+
else
32+
{
33+
StorageManager = new OSStorageManager();
34+
}
1735
}
1836

1937
public ListingManager(DirectoryInfo pathToChapter, IStorageManager storageManager)

ListingManager/Program.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,19 @@ public static RootCommand GetRootCommand()
4747
Description = "All listings are in a single directory and not separated into chapter and chapter test directories.",
4848
};
4949

50+
Option<bool> gitOption = new("--git")
51+
{
52+
Description = "Use git mv for moving files instead of OS file operations. Requires the directory to be a valid git repository.",
53+
};
54+
5055
Command listingUpdating = new("update", "Updates namespaces and filenames for all listings and accompanying tests within a chapter");
5156
listingUpdating.Arguments.Add(directoryInArgument);
5257
listingUpdating.Options.Add(verboseOption);
5358
listingUpdating.Options.Add(previewOption);
5459
listingUpdating.Options.Add(byFolderOption);
5560
listingUpdating.Options.Add(singleDirOption);
5661
listingUpdating.Options.Add(allChaptersOption);
62+
listingUpdating.Options.Add(gitOption);
5763

5864
listingUpdating.SetAction((ParseResult parseResult) =>
5965
{
@@ -63,9 +69,12 @@ public static RootCommand GetRootCommand()
6369
bool byFolder = parseResult.GetValue(byFolderOption);
6470
bool singleDir = parseResult.GetValue(singleDirOption);
6571
bool allChapters = parseResult.GetValue(allChaptersOption);
72+
bool useGit = parseResult.GetValue(gitOption);
6673

6774
Console.WriteLine($"Updating listings within: {directoryIn}");
68-
ListingManager listingManager = new(directoryIn);
75+
// When --git is specified, use the explicit constructor to force GitStorageManager.
76+
// When --git is NOT specified, use the parameterless constructor for auto-detection.
77+
ListingManager listingManager = useGit ? new(directoryIn, useGit) : new(directoryIn);
6978
if (allChapters)
7079
{
7180
listingManager.UpdateAllChapterListingNumbers(directoryIn, verbose, preview, byFolder, singleDir);

README.md

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,37 +11,70 @@ Run `dotnet tool update -g IntelliTect.EssentialCSharp.ListingManager`. This wil
1111

1212
# Usage
1313

14-
Any command can be run with these optional parameters.
14+
Run `ListingManager` from the command line.
1515

16-
- `verbose` -> provides more detail into what the command is doing
16+
For available commands run `ListingManager -h`. This will display all the commands available to you.
1717

18-
`ListingUpdating` can be run with the following additional optional parameters.
18+
## Update Command
1919

20-
- `preview` -> leave files in place but still print actions that would take place to console
21-
- `by-folder` -> changes a listing's chapter based on the chapter number in the chapter's path
22-
- `chapter-only` -> changes only the chapter of the listing, leaving the listing number unchanged. Use with `byfolder`
20+
The `update` command updates namespaces and filenames for all listings and accompanying tests within a chapter.
2321

24-
Run `ListingManager` from the command line.
22+
### Basic Usage
2523

26-
For available commands run `ListingManager -h`. This will display all the commands available to you.
24+
```bash
25+
ListingManager update <directoryIn>
26+
```
27+
28+
### Optional Parameters
29+
30+
- `--verbose` -> Displays more detailed messages in the log
31+
- `--preview` -> Displays the changes that will be made without actually making them
32+
- `--by-folder` -> Updates namespaces and filenames for all listings and accompanying tests within a folder
33+
- `--single-dir` -> All listings are in a single directory and not separated into chapter and chapter test directories
34+
- `--all-chapters` -> The passed in path is the parent directory to many chapter directories rather than a single chapter directory
35+
- `--git` -> Use git mv for moving files instead of OS file operations. Requires the directory to be in a git repository. This preserves git history for renamed files.
36+
37+
### Examples
38+
39+
```bash
40+
# Update listings in a chapter with preview
41+
ListingManager update "user/EssentialCSharp/src/Chapter03/" --preview --verbose
2742

28-
To update Listings at a path provide the Chapter's path and specify the `ListingUpdating` mode.
29-
`ListingManager --path "user/EssentialCSharp/src/Chapter03/" --mode ListingUpdating` or
30-
`ListingManager --path "user/EssentialCSharp/src/Chapter03/"`
31-
NOTE: It is highly recommended that you commit and push your changes before running this command. Additionally you should
32-
run this command with `--preview` and `--verbose` specified to ensure there are no adverse affects. Once you are confident
43+
# Update listings using git mv (preserves git history)
44+
ListingManager update "user/EssentialCSharp/src/Chapter03/" --git --preview --verbose
45+
46+
# Update all chapters
47+
ListingManager update "user/EssentialCSharp/src/" --all-chapters --preview --verbose
48+
```
49+
50+
**NOTE:** It is highly recommended that you commit and push your changes before running this command. Additionally you should
51+
run this command with `--preview` and `--verbose` specified to ensure there are no adverse effects. Once you are confident
3352
that the proposed changes are what you want, you can run the command without the `--preview` modifier.
3453

35-
To find potentially mismatched listings in a chapter run,
36-
`ListingManager --path "user/EssentialCSharp/src/Chapter03/" --mode ScanForMismatchedListings`. Potentially mismatched listings
37-
will be printed to the console.
54+
## Scan Commands
3855

39-
To run all chapters in powershell from ListingManager directory,
56+
### Scan for Mismatched Listings
57+
58+
To find potentially mismatched listings in a chapter run:
59+
```bash
60+
ListingManager scan listings <directoryIn>
4061
```
62+
63+
### Scan for Missing Tests
64+
65+
To find missing tests:
66+
```bash
67+
ListingManager scan tests <directoryIn>
68+
```
69+
70+
### Running All Chapters in PowerShell
71+
72+
From the ListingManager directory:
73+
```powershell
4174
Get-ChildItem -Path 'insert.srcPathNameHere' -Directory | Where-Object {
4275
!$_.name.EndsWith("Tests")
4376
} | ForEach-Object {
44-
listingmanager --path $_.FullName --preview --verbose
77+
listingmanager update $_.FullName --preview --verbose
4578
}
4679
```
4780

0 commit comments

Comments
 (0)