使用 GitHub CLI 和 clone --mirror

資料備份在現代是一個非常重要的習慣及任務,不要讓重要的資料只存在一個地方是備份的第一步(例如只放在 GitHub 上),本文提供一個方法,來用一個腳本自動完整備份你在 GitHub 上的所有 repo,包含所有的 branch 和 tag。

GitHub CLI

為了要取得我們的所有 repo,需要使用 GitHub CLI

安裝好後要先登入:

1
gh auth login

它應該會開始互動式詢問,只要照著它的提示完成登入即可。通常應該會選擇 GitHub.comLogin with a web browser

完成後,可以使用 gh auth status 驗證登入狀態。你應該會得到類似的回應:

1
2
3
4
5
6
7
8
$ gh auth status

github.com
✓ Logged in to github.com account ziteh (keyring)
- Active account: true
- Git operations protocol: https
- Token: gho_*********************************
- Token scopes: 'gist', 'read:org', 'repo', 'workflow'

再來,你可以用 gh repo list <USERNAME> 列出所有的 repo,<USERNAME> 是目標用戶或組織的 username。如果擁有權限的話,Private repo 也會列出。你可以試著列出自己的 repo,看看是否可以看到自己的 Private repo。例如列出 Vim 的所有 repo:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ gh repo list vim

Showing 8 of 8 repositories in @vim

NAME DESCRIPTION INFO UPDATED
vim/vim The official Vim reposi... public about 1 hour ago
vim/vim-win32-installer Vim Win32 Installer public about 1 day ago
vim/vim-appimage AppImage for gVim public about 1 day ago
vim/website_next_generation The new website public about 1 day ago
vim/colorschemes colorschemes for Vim public about 1 day ago
vim/winget-pkgs The Microsoft community... public, fork about 2 days ago
vim/killersheep Silly game for Vim 8.2 public about 8 months ago
vim/vim-history Very very old history o... public about 1 year ago

clone –mirror

git clone 大家都很熟,但是加上 --mirror 後可能就不是每個人都用過了。使用 git clone --mirror <REPO_URL> 會 Clone 該 repo 的所有 git 內部資訊(包含 branch、tag),也就是可以完整地備份 repo,方便進行完整的轉移。只使用一般的 git clone 的話,還要再另外 fetch/pull 其它 branch。

例如:

1
git clone --mirror https://github.com/neovim/neovim.git

執行完後,應該會建立一個 neovim.git 資料夾,內部就是各種 git 資訊。

值得一提的是,git clone --mirror 只會建立 git 內部資訊(Bare repo),不會包含工作目錄(即實際的檔案),不過我們可以再次使用一般的 git clone 來將工作目錄還原(就類似把它當成在本地的 Remote repo 操作)。例如 clone 我們 clone --mirror 下來的 neovim.git Bare repo 資料夾:

1
git clone neovim.git

你應該會看到一個新的 neovim 資料夾,而且內部完整包含了所有檔案,各個 branch 和 tag 也都在。

可以發現,這個 neovim.git 其實就和我們平常用的 GitHub、Remote repo 很類似,有興趣的話可以查查 Git Bare repo[1]

Bash 腳本

現在我們可以使用 GitHub CLI 取得所有 repo,也知道 git clone --mirror 可以用來完成備份 repo,就可以用個簡單的 Bash 腳本來備份所有 repo[2]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash

# clone all repos from GitHub, using GitHub CLI
# input arg1 for user or org name, arg2 for option
# source: https://stackoverflow.com/questions/19576742/how-to-clone-all-repos-at-once-from-github

if [ -z "$1" ]; then
echo "Usage: bash $0 <USERNAME> [OPTION]"
exit 1
fi

username=$1
option=$2

gh repo list "$username" --limit 1000 | while read -r repo _; do
if [ -d "${repo}.git" ]; then
# Already exists and is not an empty directory, fetch.
echo "Fetch '$repo'"
git -C "${repo}.git" fetch --prune-tags
else
# New repo, clone repo.
echo "Clone '$repo'"
gh repo clone "$repo" "${repo}.git" -- --mirror $option
fi
done

這個腳本會使用 gh repo list 來取得目標用戶或組織的 repo 清單,如果本地目錄下沒有此 repo 的話會 clone --mirror 一份 Bare repo,如果已經 clone 過的話(資料夾已經存在),則會 fetch 此 Bare repo 進行更新。我們將此腳本命名為 clone_github.sh,接著就可以試著執行看看了。例如我要備份 Neovim 底下的所有 repo:

1
bash clone_github.sh neovim

有了這個腳本,我們就不用每個 repo 都手動 clone 了。當然更進一步的話,可以把它包成 Docker,在 NAS 上定期執行。

參考


留言可能不會立即顯示。若過了幾天仍未出現,請 Email 聯繫:)