Skip to content

Instantly share code, notes, and snippets.

@mebiusbox
Last active September 1, 2024 07:27
Show Gist options
  • Save mebiusbox/a90fda22776c9c9b376ffc165dd0988d to your computer and use it in GitHub Desktop.
Save mebiusbox/a90fda22776c9c9b376ffc165dd0988d to your computer and use it in GitHub Desktop.
gistコマンドを自作してテンプレートファイルを管理する

Gistコマンドを自作してテンプレートファイルを管理する

※ここでの「テンプレートファイル」とはコンフィグファイルなど、各プロジェクトとよく使うファイルのことを指します.

目的 (Goal)

  • PowerShellを使って、Gistに登録しているファイルをクローンし、差分を表示したり、パッチを適用する機能を持つgistコマンドを実装する

動機 (Motivation)

  • Gistはgitコマンドやghコマンドでクローンして管理できますが、ローカルリポジトリを作ってしまうので扱いづらい
  • 1~2ファイル程度のテンプレートファイルを管理するのに、プロジェクトを専用に作るのではなく、Gistで管理したい(バージョン履歴を残す).
  • gitコマンドのように手軽に扱いたい
  • PowerShellとコマンドラインツールで対応したい

設計(Design)

Function Description
gist-clone Gistからファイルをダウンロードする
gist-diff Gistの最新版とローカル版を比較する
gist clone, diff, patch, merge の4種類のコマンドが使える
PS > gist-clone -UserId <userid> -Hash <hash> -Name <name> [-OutName <outname>]
PS > gist-diff -UserId <userid> -Hash <hash> -Name <name> [-WorkingName <workingname>] [-Patch]

# clone(オプションは gist-clone に渡される)
PS > gist clone [options]

# diff(オプションは gist-diff に渡される)
PS > gist diff [-Patch] [options]

# patch(オプションは patch.exe に渡される)
# 事前に diff -Patch でパッチファイルを作成する必要がある
PS > gist patch [options]
# patch(適用前のファイルを保存)
PS > gist patch -b
# merge (WinMerge)
PS > gist merge

コードについて

  • Windows11, PowerShell 7.4.5 で動作確認しました
  • 私の環境はWindowsでワーキング環境ではCRLFで作業しています.git側のEOLがLFなので、git diffの内容を出力するとLFとなります.そのため、sdを使って変換しています.
  • Gistからファイルを取得するのに2種類の方法があります.URLで直接アクセスするのと、ghコマンド(GitHub CLI)を使う方法です.URLの場合、最新バージョンを取得するのにラグが発生したので、ghコマンドを使うようにしています.
  • パッチを適用するのにGitに付属しているpatch.exeを利用しています.
  • パッチを適用したあとは、手動でパッチファイルを削除する必要があります.
  • 必要な情報を毎回入力するのは面倒なので、gist関数の中にハッシュテーブルで持つようにして、そのエントリを fzf で選択できるようにしています.
  • プッシュする機能はないので、GistPadなどで更新します.
  • 差分を表示するのにdeltaを使用しています.
  • 基本的に管理しているファイル名と同じ名前でローカルにあることを想定していますが、db内のNameListに特定の名前を追加することができます.
  • Visual Studio Codeの拡張機能GistPadで保存する場合、フォーマッタの問題で保存時に変わってしまうことがある.その場合、コマンドパレットから「File > Save without Formatting」で保存すること.
  • サブコマンドにmergeも追加しました.WinMergeを起動します.
function gist-clone() {
Param(
[Parameter(Mandatory=$true)]
[string]$UserId,
[Parameter(Mandatory=$true)]
[string]$Hash,
[Parameter(Mandatory=$true)]
[string]$Name,
[string]$OutName
)
# $uri = "https://gist.github.com/${UserId}/${Hash}/raw/${Name}"
# if ($OutName -eq "") {
# Invoke-WebRequest -Uri $uri -OutFile $Name
# } else {
# Invoke-WebRequest -Uri $uri -OutFile $OutName
# }
if ($OutName -eq "") {
gh gist view $Hash -f $Name >$Name
} else {
gh gist view $Hash -f $Name >$OutName
}
}
function gist-diff() {
Param(
[Parameter(Mandatory=$true)]
[string]$UserId,
[Parameter(Mandatory=$true)]
[string]$Hash,
[Parameter(Mandatory=$true)]
[string]$Name,
[string]$WorkingName,
[switch]$Patch
)
$tmp = New-TemporaryFile
# $uri = "https://gist.github.com/${UserId}/${Hash}/raw/${Name}"
# Invoke-WebRequest -Uri $uri -OutFile $tmp
gh gist view $Hash -f $Name >$tmp
$src = $WorkingName
if ($src -eq "") {
$src = $Name
}
$hash1 = Get-FileHash -Algorithm SHA1 -Path $tmp
$hash2 = Get-FileHash -Algorithm SHA1 -Path $src
if ($hash1.Hash -eq $hash2.Hash) {
Write-Output "${src} is up-to-date."
} else {
if ($Patch) {
git diff --no-index $src $tmp >"${src}.patch"
sd "\n" "\r\n" "${src}.patch"
}
delta $tmp ".\${src}" --side-by-side
}
}
function gist() {
Param(
[Parameter(Mandatory=$true)]
[ValidateSet("clone", "diff", "patch", "merge")]
[string]$Command,
[Parameter(ValueFromRemainingArguments=$true)]
[string[]]$AdditionalParams
)
$userId = "mebiusbox"
$db = @{
editorconfig = @{
Hash = "c660eee4639989e8f1ebe1a3b80e5ade"
Name = ".editorconfig"
NameList = @(".editorconfig")
}
}
$splatAdditionalParams = @{}
if ($AdditionalParams) {
$key = $null
for ($i = 0; $i -lt $AdditionalParams.Count; $i++) {
if ($AdditionalParams[$i].StartsWith("-")) {
$key = $AdditionalParams[$i].TrimStart('-')
$splatAdditionalParams[$key] = $true
} else {
$splatAdditionalParams[$key] = $AdditionalParams[$i]
}
}
}
$db.Keys | fzf | ForEach-Object {
$key = $_
switch ($Command) {
"clone" {
gist-clone -UserId $userId -Hash $db.($key).Hash -Name $db.($key).Name @splatAdditionalParams
}
"diff" {
foreach ($v in $db.($key).NameList) {
if (Test-Path $v -PathType Leaf) {
gist-diff -UserId $userId -Hash $db.($key).Hash -Name $db.($key).Name -WorkingName $v @splatAdditionalParams
break
}
}
}
"merge" {
foreach ($v in $db.($key).NameList) {
if (Test-Path $v -PathType Leaf) {
$tmp = New-TemporaryFile
gh gist view $db.($key).Hash -f $db.($key).Name >$tmp
& "$env:localappdata\programs\WinMerge\WinMergeU.exe" $tmp $v
break
}
}
}
"patch" {
foreach ($v in $db.($key).NameList) {
if (Test-Path $v -PathType Leaf) {
& "$env:programfiles\git\usr\bin\patch.exe" -i "${v}.patch" --binary $AdditionalParams
break
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment