はじめに
MacのFinderにはファイルやフォルダに好きな名前のタグを付ける機能があります。
今回は、Git管理しているフォルダにGitタグを付けるコードを紹介します。
目次
Codeの説明
git-tag-sync.shはzsh用のシェルスクリプトです。
このスクリプトでは次のことを実行します。
-
.gitというディレクトリの親ディレクトリを収集し(隠しフォルダを除く)、Git管理フォルダとする -
Gitタグが付いているディレクトリを収集する -
Gitタグが付いているGit管理フォルダのタグを保持する -
Gitタグが付いている非Git管理フォルダのGitタグを外す -
Gitタグが付いていないGit管理フォルダにGitタグを付ける -
Gitタグの付与・維持・取消の数をそれぞれ表示
#!/usr/bin/env zsh
set -euo pipefail
ROOT="$HOME"
TAGNAME="Git"
MDLS_BIN="/usr/bin/mdls"
AWK_BIN="/usr/bin/awk"
# has_tag: mdls でタグ検知(スペース/特殊文字/パス形式差異に強い)
has_tag() {
local path="$1"
local tag="$2"
"$MDLS_BIN" -raw -name kMDItemUserTags "$path" 2>/dev/null \
| "$AWK_BIN" -v t="$tag" '
BEGIN { found=0 }
/\(null\)/ { next }
{
gsub(/[()]/, "");
n=split($0, a, ",");
for (i=1; i<=n; i++) {
s=a[i];
gsub(/^[[:space:]]+|[[:space:]]+$/, "", s);
gsub(/^"|"$/, "", s);
if (s == t) { found=1 }
}
}
END { exit !found }
'
}
# --- 収集 ----------------------------------------------------------
# 方針:
# - find は -print0 で NUL 区切りにして、スペース/改行を含むパスでも壊れないようにする
# - 隠しフォルダは探索しない。ただし .git 自体は例外で拾う
# 1) .git の親ディレクトリを収集
GIT_DIRS=()
{
# zsh の read で NUL 区切りを読む
while IFS= read -r -d '' gitdir; do
# "/path/to/repo/.git" -> "/path/to/repo"
GIT_DIRS+=("${gitdir:h}")
done < <(
find "$ROOT" \
\( -path '*/.*' -a ! -name .git -type d -prune \) \
-o \( -type d -name .git -print0 \) \
2>/dev/null
)
}
# sort -u 相当(zsh 組み込み)
# ※ path の順序は安定化させつつ重複排除
GIT_DIRS=(${(ou)GIT_DIRS})
# 2) いま TAGNAME が付いているフォルダ一覧
TAGGED_DIRS=()
{
# tag --find の出力は改行区切り想定
# 空でも安全に配列化
local out
out="$(tag --find "$TAGNAME" "$ROOT" 2>/dev/null || true)"
if [[ -n "$out" ]]; then
TAGGED_DIRS=(${(f)out})
TAGGED_DIRS=(${(ou)TAGGED_DIRS})
fi
}
# 高速化: Git 管理フォルダ集合を作る
typeset -A GIT_SET
for d in "${GIT_DIRS[@]}"; do
GIT_SET[$d]=1
done
# カウント
typeset -i COUNT_KEEP=0
typeset -i COUNT_ADD=0
typeset -i COUNT_REMOVE=0
# --- 反映 ----------------------------------------------------------
# 3) 付ける/保持する(Git管理されているフォルダ)
for d in "${GIT_DIRS[@]}"; do
# mdls ベースのタグ検知(iCloudなどスペース含むパスでも安全)
if has_tag "$d" "$TAGNAME"; then
print -r -- "KEEP : $d"
COUNT_KEEP+=1
else
tag --add "$TAGNAME" "$d"
print -r -- "ADD : $d"
COUNT_ADD+=1
fi
done
# 4) 外す(Git管理されてないのにタグが付いてるもの)
# ※「Git」タグを他用途に使ってない前提。使ってるなら条件を絞る必要あり。
for d in "${TAGGED_DIRS[@]}"; do
if [[ -z "${GIT_SET[$d]-}" ]]; then
tag --remove "$TAGNAME" "$d"
print -r -- "REMOVE: $d"
COUNT_REMOVE+=1
fi
done
# --- 集計 ----------------------------------------------------------
print -r -- "SUMMARY -> KEEP: $COUNT_KEEP, ADD: $COUNT_ADD, REMOVE: $COUNT_REMOVE"
実装方法
git-tag-sync.shはtagコマンドを使用するので、Homebrewでインストールします。
brew install tag
git-tag-sync.shという名前で上記のコードをローカルに保存してください。以下では自作スクリプト置き場の定番である~/.local/binに保存することを前提に進めます。
mkdir -p `~/.local/bin`
mv git-tag-sync.sh ~/.local/bin/
次に実行権限を付与します。(しなればpermission deniedとなります。)
chmod +x ~/.local/bin/git-tag-sync.sh
~/.local/binをPATHに追加し、反映します。(./を付けずに実行が可能になります。)
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
使い方
ターミナルで以下を実行してください。MacではFinderのGUIでタグには色をつけられます。
Finderの設定(cmd+,)で、タグの色に応じてフォルダに色を付ける設定にすることもできます。
git-tag-sync.sh
おわりに
様々なアプリやその拡張機能に.gitフォルダが入っていることがあるので、隠しフォルダは探索から除外しています。
iCloud / Google Drive / One Drive / Dropbox のようなクラウドにあるフォルダも、デスクトップアプリでFiderから探索できてpathを有する場合はGitタグ管理ができるようにしています。
スペース・改行・特殊文字が原因で上手く反映しない可能性もありますが、その場合は適宜コードの編集をお願いします。
今回紹介したコードはMacOSのzshを想定していますが、普段bashを使用されている方は適宜書き換えて使ってみてください。