Skip to content

Commit 81f6e8c

Browse files
committed
Extract GitAliases.Extras to dedicated repo as submodule
1 parent 614ad68 commit 81f6e8c

File tree

6 files changed

+269
-1
lines changed

6 files changed

+269
-1
lines changed

.gitmodules

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[submodule "modules/GitAliases.Extras"]
2+
path = modules/GitAliases.Extras
3+
url = git@github.com:PhysShell/GitAliases.Extras.git
4+
branch = main

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,30 @@ Checks are skipped when:
4646
- commit message contains `[skip precommit hook]` or `[skip pch]`
4747
- there are no working tree changes (for example, `git commit --allow-empty ...`)
4848

49+
## Extract GitAliases.Extras
50+
51+
Recommended approach: keep this as dotfiles repo and move `modules/GitAliases.Extras` to its own repository, then consume it via git submodule at the same path.
52+
53+
Safe migration flow:
54+
55+
1. Run a dry run:
56+
57+
```powershell
58+
.\tools\migrate-gitaliases-extras.ps1 -SubmoduleUrl 'git@github.com:<you>/GitAliases.Extras.git' -SubmoduleBranch main -PushSplit
59+
```
60+
61+
2. Apply the migration:
62+
63+
```powershell
64+
.\tools\migrate-gitaliases-extras.ps1 -SubmoduleUrl 'git@github.com:<you>/GitAliases.Extras.git' -SubmoduleBranch main -PushSplit -Apply
65+
```
66+
67+
3. Commit resulting changes in this repo (`.gitmodules` + submodule pointer).
68+
69+
Notes:
70+
- `bootstrap.ps1` now initializes submodules automatically if `.gitmodules` exists.
71+
- `profile.ps1` warns with the exact `git submodule update --init --recursive` command if submodules were not initialized.
72+
4973
## What CI checks
5074

5175
- `PSScriptAnalyzer` linting with `PSScriptAnalyzerSettings.psd1`

bootstrap.ps1

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,23 @@ function Ensure-GitGlobalConfig([string]$Key, [string]$Value) {
1010
}
1111
}
1212

13+
function Ensure-GitSubmodules([string]$RepoRoot) {
14+
$gitmodulesPath = Join-Path $RepoRoot '.gitmodules'
15+
if (-not (Test-Path -LiteralPath $gitmodulesPath)) {
16+
return
17+
}
18+
19+
$null = git -C $RepoRoot submodule sync --recursive
20+
if ($LASTEXITCODE -ne 0) {
21+
throw "Failed to sync git submodules in '$RepoRoot'."
22+
}
23+
24+
$null = git -C $RepoRoot submodule update --init --recursive
25+
if ($LASTEXITCODE -ne 0) {
26+
throw "Failed to initialize/update git submodules in '$RepoRoot'."
27+
}
28+
}
29+
1330
# PSReadLine is required for Windows PowerShell 5.1; it's already built into PowerShell Core (pwsh)
1431
if ($PSVersionTable.PSEdition -ne 'Core') { Ensure-Module PSReadLine ([Version]'2.2.6') }
1532

@@ -23,5 +40,8 @@ Ensure-GitGlobalConfig 'push.autoSetupRemote' 'true'
2340
Ensure-GitGlobalConfig 'push.default' 'current'
2441
Ensure-GitGlobalConfig 'remote.pushDefault' 'origin'
2542

43+
$repoRoot = Resolve-Path -LiteralPath $PSScriptRoot | Select-Object -ExpandProperty Path -First 1
44+
Ensure-GitSubmodules -RepoRoot $repoRoot
45+
2646
Write-Host "Bootstrap done. Restart PowerShell."
2747

profile.ps1

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,15 @@ Set-Alias -Name g -Value git -Force
6666

6767
# 3. GitAliases.Extras: Your custom module that DEPENDS on posh-git.
6868
# It finds all aliases and registers the proxy completer.
69+
$extrasManifest = Join-Path $dotFilesRoot 'modules\GitAliases.Extras\GitAliases.Extras.psd1'
6970
if (-not (Get-Module -Name GitAliases.Extras -ErrorAction SilentlyContinue)) {
70-
Import-Module GitAliases.Extras -ErrorAction Stop
71+
if (Test-Path -LiteralPath $extrasManifest) {
72+
Import-Module $extrasManifest -ErrorAction Stop
73+
} elseif (Test-Path -LiteralPath (Join-Path $dotFilesRoot '.gitmodules')) {
74+
Write-Warning "GitAliases.Extras is missing. Initialize submodules: git -C '$dotFilesRoot' submodule update --init --recursive"
75+
} else {
76+
Write-Warning "GitAliases.Extras module not found at '$extrasManifest'."
77+
}
7178
}
7279

7380
# Write-Host "PowerShell profile loaded. Posh-git and custom alias completion are active." -ForegroundColor Green

tests/MigrationScript.Tests.ps1

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
$ErrorActionPreference = 'Stop'
2+
Set-StrictMode -Version Latest
3+
4+
BeforeAll {
5+
function Script:Invoke-GitForMigrationTests {
6+
[CmdletBinding()]
7+
param(
8+
[Parameter(Mandatory = $true)]
9+
[string]$RepoPath,
10+
[Parameter(Mandatory = $true)]
11+
[string[]]$Arguments,
12+
[switch]$AllowFail
13+
)
14+
15+
$previousPreference = $ErrorActionPreference
16+
$ErrorActionPreference = 'Continue'
17+
try {
18+
$output = & git -C $RepoPath @Arguments 2>&1
19+
} finally {
20+
$ErrorActionPreference = $previousPreference
21+
}
22+
23+
$exitCode = $LASTEXITCODE
24+
$text = ($output | Out-String).Trim()
25+
if (-not $AllowFail -and $exitCode -ne 0) {
26+
throw "git $($Arguments -join ' ') failed in '$RepoPath' (exit=$exitCode): $text"
27+
}
28+
29+
[pscustomobject]@{
30+
ExitCode = $exitCode
31+
Output = $text
32+
}
33+
}
34+
35+
function Script:New-MigrationTestRepository {
36+
[CmdletBinding()]
37+
param(
38+
[Parameter(Mandatory = $true)]
39+
[string]$Prefix
40+
)
41+
42+
$tempRoot = Join-Path ([IO.Path]::GetTempPath()) ($Prefix + '-' + [guid]::NewGuid().Guid)
43+
$repoPath = Join-Path $tempRoot 'repo'
44+
$toolsPath = Join-Path $repoPath 'tools'
45+
$modulePath = Join-Path $repoPath 'modules\GitAliases.Extras'
46+
47+
New-Item -ItemType Directory -Path $toolsPath -Force | Out-Null
48+
New-Item -ItemType Directory -Path $modulePath -Force | Out-Null
49+
50+
Copy-Item -Path $script:MigrationScriptSource -Destination (Join-Path $toolsPath 'migrate-gitaliases-extras.ps1') -Force
51+
52+
Invoke-GitForMigrationTests -RepoPath $repoPath -Arguments @('init') | Out-Null
53+
Invoke-GitForMigrationTests -RepoPath $repoPath -Arguments @('config', 'user.email', 'test@example.com') | Out-Null
54+
Invoke-GitForMigrationTests -RepoPath $repoPath -Arguments @('config', 'user.name', 'Test User') | Out-Null
55+
Invoke-GitForMigrationTests -RepoPath $repoPath -Arguments @('config', 'commit.gpgsign', 'false') | Out-Null
56+
57+
Set-Content -Path (Join-Path $modulePath 'GitAliases.Extras.psm1') -Value "function gsw { 'ok' }" -NoNewline -Encoding ascii
58+
Invoke-GitForMigrationTests -RepoPath $repoPath -Arguments @('add', '.') | Out-Null
59+
Invoke-GitForMigrationTests -RepoPath $repoPath -Arguments @('commit', '-m', 'init module') | Out-Null
60+
61+
[pscustomobject]@{
62+
TempRoot = $tempRoot
63+
RepoPath = $repoPath
64+
}
65+
}
66+
67+
[string]$script:RepoRoot = Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..') |
68+
Select-Object -ExpandProperty Path -First 1
69+
$script:MigrationScriptSource = Join-Path $script:RepoRoot 'tools\migrate-gitaliases-extras.ps1'
70+
}
71+
72+
Describe 'migrate-gitaliases-extras script' {
73+
It 'dry run succeeds and does not modify repository layout' -Skip:(-not (Get-Command git -ErrorAction SilentlyContinue)) {
74+
$context = New-MigrationTestRepository -Prefix 'migration-dry-run'
75+
try {
76+
Push-Location $context.RepoPath
77+
try {
78+
$output = & pwsh -NoProfile -File '.\tools\migrate-gitaliases-extras.ps1' -SubmoduleUrl 'git@github.com:example/GitAliases.Extras.git' 2>&1
79+
$exitCode = $LASTEXITCODE
80+
} finally {
81+
Pop-Location
82+
}
83+
84+
$exitCode | Should -Be 0
85+
(($output | Out-String) -match 'Dry run complete') | Should -BeTrue
86+
(Test-Path -LiteralPath (Join-Path $context.RepoPath 'modules\GitAliases.Extras\GitAliases.Extras.psm1')) | Should -BeTrue
87+
(Test-Path -LiteralPath (Join-Path $context.RepoPath '.gitmodules')) | Should -BeFalse
88+
} finally {
89+
if (Test-Path -LiteralPath $context.TempRoot) {
90+
Remove-Item -Path $context.TempRoot -Recurse -Force -ErrorAction SilentlyContinue
91+
}
92+
}
93+
}
94+
95+
It 'fails on dirty working tree without AllowDirty' -Skip:(-not (Get-Command git -ErrorAction SilentlyContinue)) {
96+
$context = New-MigrationTestRepository -Prefix 'migration-dirty'
97+
try {
98+
Set-Content -Path (Join-Path $context.RepoPath 'dirty.txt') -Value 'dirty' -NoNewline -Encoding ascii
99+
100+
Push-Location $context.RepoPath
101+
try {
102+
$output = & pwsh -NoProfile -File '.\tools\migrate-gitaliases-extras.ps1' -SubmoduleUrl 'git@github.com:example/GitAliases.Extras.git' 2>&1
103+
$exitCode = $LASTEXITCODE
104+
} finally {
105+
Pop-Location
106+
}
107+
108+
$exitCode | Should -Not -Be 0
109+
(($output | Out-String) -match 'Working tree is not clean') | Should -BeTrue
110+
} finally {
111+
if (Test-Path -LiteralPath $context.TempRoot) {
112+
Remove-Item -Path $context.TempRoot -Recurse -Force -ErrorAction SilentlyContinue
113+
}
114+
}
115+
}
116+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
[CmdletBinding()]
2+
param(
3+
[Parameter(Mandatory = $true)]
4+
[string]$SubmoduleUrl,
5+
[string]$SubmoduleBranch = 'main',
6+
[string]$ModulePath = 'modules/GitAliases.Extras',
7+
[switch]$PushSplit,
8+
[switch]$Apply,
9+
[switch]$AllowDirty
10+
)
11+
12+
$ErrorActionPreference = 'Stop'
13+
Set-StrictMode -Version Latest
14+
15+
function Invoke-Git {
16+
[CmdletBinding()]
17+
param(
18+
[Parameter(Mandatory = $true)]
19+
[string]$RepoRoot,
20+
[Parameter(Mandatory = $true)]
21+
[string[]]$Arguments
22+
)
23+
24+
$previousPreference = $ErrorActionPreference
25+
$ErrorActionPreference = 'Continue'
26+
try {
27+
$output = & git -C $RepoRoot @Arguments 2>&1
28+
} finally {
29+
$ErrorActionPreference = $previousPreference
30+
}
31+
32+
if ($LASTEXITCODE -ne 0) {
33+
$text = ($output | Out-String).Trim()
34+
throw "git $($Arguments -join ' ') failed (exit=$LASTEXITCODE): $text"
35+
}
36+
37+
return ($output | Out-String).Trim()
38+
}
39+
40+
[string]$repoRoot = Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..') |
41+
Select-Object -ExpandProperty Path -First 1
42+
[string]$moduleFullPath = Join-Path $repoRoot $ModulePath
43+
44+
if (-not (Test-Path -LiteralPath (Join-Path $repoRoot '.git'))) {
45+
throw "Not a git repository: $repoRoot"
46+
}
47+
48+
if (-not (Test-Path -LiteralPath $moduleFullPath)) {
49+
throw "Module path not found: $moduleFullPath"
50+
}
51+
52+
if (-not $AllowDirty) {
53+
$status = Invoke-Git -RepoRoot $repoRoot -Arguments @('status', '--porcelain')
54+
if ($status) {
55+
throw "Working tree is not clean. Commit or stash changes, or rerun with -AllowDirty."
56+
}
57+
}
58+
59+
Write-Host "Creating split history for '$ModulePath'..."
60+
$splitOutput = Invoke-Git -RepoRoot $repoRoot -Arguments @('subtree', 'split', "--prefix=$ModulePath", 'HEAD')
61+
$splitCommit = ($splitOutput -split "`r?`n" | Where-Object { $_ -match '^[0-9a-f]{40}$' } | Select-Object -Last 1)
62+
if (-not $splitCommit) {
63+
throw "Could not resolve split commit from git subtree output."
64+
}
65+
Write-Host "Split commit: $splitCommit"
66+
67+
if ($PushSplit) {
68+
Write-Host "Pushing split history to '$SubmoduleUrl' branch '$SubmoduleBranch'..."
69+
Invoke-Git -RepoRoot $repoRoot -Arguments @('push', $SubmoduleUrl, "$($splitCommit):refs/heads/$SubmoduleBranch") | Out-Null
70+
}
71+
72+
if (-not $Apply) {
73+
Write-Host ''
74+
Write-Host 'Dry run complete. Next command:'
75+
Write-Host (" .\tools\migrate-gitaliases-extras.ps1 -SubmoduleUrl '{0}' -SubmoduleBranch '{1}' -PushSplit -Apply" -f $SubmoduleUrl, $SubmoduleBranch)
76+
Write-Host ''
77+
Write-Host 'This will:'
78+
Write-Host (" 1) keep history via subtree split for {0}" -f $ModulePath)
79+
Write-Host (" 2) replace {0} with a git submodule" -f $ModulePath)
80+
exit 0
81+
}
82+
83+
if (-not $PushSplit) {
84+
Write-Warning "You used -Apply without -PushSplit. Ensure branch '$SubmoduleBranch' already exists in '$SubmoduleUrl'."
85+
}
86+
87+
Write-Host "Replacing '$ModulePath' with a submodule..."
88+
Invoke-Git -RepoRoot $repoRoot -Arguments @('rm', '-r', '--', $ModulePath) | Out-Null
89+
Invoke-Git -RepoRoot $repoRoot -Arguments @('submodule', 'add', '-b', $SubmoduleBranch, $SubmoduleUrl, $ModulePath) | Out-Null
90+
Invoke-Git -RepoRoot $repoRoot -Arguments @('submodule', 'sync', '--recursive') | Out-Null
91+
Invoke-Git -RepoRoot $repoRoot -Arguments @('submodule', 'update', '--init', '--recursive', '--', $ModulePath) | Out-Null
92+
93+
Write-Host ''
94+
Write-Host 'Migration completed.'
95+
Write-Host 'Review and commit:'
96+
Write-Host ' git status'
97+
Write-Host " git commit -m 'Extract GitAliases.Extras to submodule'"

0 commit comments

Comments
 (0)