全ての git リポジトリをバックアップする


概要

シンプルな linux コマンドだけを使って D:\ ドライブの全ての .git リポジトリを E:\ ドライブに git clone します。

動機

自分は内蔵の D:\ ドライブにたくさんのプロジェクトを保存しています。それぞれのプロジェクトは個別の git リポジトリで履歴管理されています。やっかいなことに、git リポジトリは色々なディレクトリに分散していて、何がどこにあるか完全には把握していません。そんな中、E:\ に外付けドライブを接続し、全てのリポジトリを bare リポジトリとしてバックアップしようと思いました。幸い、すでに WSL2 がインストールされて Linux コマンドが使える状態だったのでそれを利用しました。

特徴

  • 1ステップずつシンプルなコマンドで実行しました。
  • ステップごとの実行結果をファイルとして作業用ディレクトリに保存しました。後から確認しやすくなっています。
  • 基本的な Linux コマンドだけで構成しました。

注意

  • リポジトリのパスに空白文字が入っている、記号文字が入っている、日本語のリポジトリ名などの場合は失敗しそうです。アルファベットと数字のみのディレクトリ名で使うのが無難です。リポジトリ内にある多言語ファイル名は大丈夫なはずです。
  • エラーハンドリングなどは最小限です。予期しないエラーで失敗する可能性があります。各ステップで上手く動いたか確認することをおすすめします。

コマンド手順

全14ステップ

簡潔にコマンドだけを示します(詳しい説明は下のほうを参照時間があったら書きます)。

0. 作業用ディレクトリ

mkdir workdirgitbash
cd workdirgitbash

1. 変数の設定(コピー元・コピー先)

$ SRCVAR=バックアップ元
$ DSTVAR=バックアップ先
例:

SRCVAR=/mnt/d/
DSTVAR=/mnt/e/backup202204/

エラーメッセージ用の設定

EAAMSG=$(echo "EAAOA! " | sed "s/A/R/g")

2. 信頼性確認

echo $SRCVAR | grep "[^a-zA-Z0-9/_-]" -v || echo "$EAAMSG: Unsupported Pathname"
echo $DSTVAR | grep "[^a-zA-Z0-9/_-]" -v || echo "$EAAMSG: Unsupported Pathname"
mkdir -p $DSTVAR
[ -z "$(ls -A $DSTVAR)" ] || echo "$EAAMSG: Destination Not Empty"

エラーが表示された場合は適宜、調整してください。

3. ".git" という名前のディレクトリを検索する

find $SRCVAR -type d -name .git > repos.txt

*検索にしばらく時間がかかるかもしれません。

4. コピー元リポジトリのリストを作る

cat repos.txt | sed "s/.git$//" > srclist.txt

5. コピー先のディレクトリ名を合成する

cat srclist.txt | sed "s/^${SRCVAR//'/'/'\/'}/${DSTVAR//'/'/'\/'}\//" > dstlist.txt

6. 信頼性確認

[ -z "$(grep '[^a-zA-Z0-9/_-]' srclist.txt)" ] || echo "$EAAMSG: Unsupported Pathname"
[ -z "$(grep '[^a-zA-Z0-9/_-]' dstlist.txt)" ] || echo "$EAAMSG: Unsupported Pathname"

7. Git リポジトリが最新かどうかを確認する用のコマンドを合成する

cat srclist.txt | sed 's/.*/pushd/' > statuschk0.txt
cat srclist.txt | sed 's/.*/; [ -z "$(git status -s)" ] || echo "$EAATAG : work tree not clean"/' > statuschk2.txt
cat srclist.txt | sed 's/.*/; popd;/' > statuschk3.txt
paste statuschk0.txt srclist.txt statuschk2.txt statuschk3.txt > statuschk.sh

8. Git リポジトリが最新かどうか確認する

chmod u+x statuschk.sh
./statuschk.sh

*エラーが表示されたら適宜調整します。
*Git のセキュリティ強化による仕様変更の影響を受けるようです。

fatal: unsafe repository ('/mnt/d/repo1' is owned by someone else)

のエラーを回避する方法としては、root 権限で実行する手があります:

sudo bash statuschk.sh

9. 空のディレクトリ名を作る用のコマンドを合成する

cat dstlist.txt | sed "s/^/mkdir -p /" > mkdircommand.sh

10. コピー元のリストとコピー先のリストを1行ずつ結合する

paste srclist.txt dstlist.txt > pathes.txt

11. git clone 用のコマンドを合成する

cat pathes.txt | sed "s/^/git clone --bare /" > cloneall.sh

12. 全体的なスクリプトを合成する

paste -d "; " mkdircommand.sh cloneall.sh > totalcommand.sh

13. スクリプトに実行可能フラグを立てる

chmod u+x totalcommand.sh

14. 実行する

./totalcommand.sh

権限が必要な場合は代わりに:

sudo ./totalcommand.sh

詳しい説明

時間があったら書きます。ヒントだけ:
・各ステップでそれぞれのファイルを cat で表示して目の子で確かめます。

cat dstlist.txt

更新履歴

2022-04-16 : ディレクトリ名のタイポを直しました
2022-04-16 : Git のセキュリティ強化による仕様変更に対応しました

最後に

提案や不備な点その他なにかありましたら気軽にコメントください。