背景
使用 repo
管理了多個 git
倉庫,有時需要將本地倉庫的tag
同步給其他人,但又不能直接推到遠程(例如權限問題)。
實際場景舉例
- 本地復現了一個問題,需要讓其他人回退到相同環境來排查。
- 本地集成驗證好了一個版本需要發布,打好
tag
卻沒有權限推送,得告知各個倉庫負責人在同樣的commit
上打tag
並推送到遠程倉庫。
倉庫少的話,簡單告知下各個倉庫對應的commit
號就可以了,對方手工找到對應的commit
號進行操作。
涉及的倉庫數量多或者本地 tag
可能發生變更需要多次同步的時候,手工操作就比較麻煩了。
自動化腳本
讓我們來考慮下如何讓同步本地 tag
這個事情變得簡單些。(不看實現過程的話,可直接拉到最后總結部分)
后續演示基於一個簡單的repo
環境
mkdir test-repo;
cd test-repo;
repo init -u https://github.com/zqb-all/zqb-manifest
repo sync
#打上tag方便后續測試
repo forall -c git tag test-v1
基礎命令
tag
名是已知的,要提取的關鍵信息就只有 倉庫
和 commit號
。
repo
可以幫我們遍歷所有倉庫,在每個倉庫下執行git
命令,使用方式是repo forall -c git xxx
。
已知tagname
,要獲取commit
號,可以通過git log tagname
列出對應的commit
,我們只需要一個commit
,於是可以加上-1
參數只列出一個提交。
組合一下就得到了
#!/bin/bash
# v1
tag=$1
repo forall -c git log -1 $tag
試試效果,
$ ./repo_share_tag.sh test-v1
commit e9e78ee6df545fb057eb6baaaf9446327cabdfa7
Author: zhuangqiubin <zhuangqiubin@gmail.com>
Date: Mon Apr 6 00:14:54 2020 +0800
fix typo
commit 059c803e9f1d0df8e5c89aec11340224c1d85f0e
Author: zqb-all <zhuangqiubin@gmail.com>
Date: Fri Aug 30 10:11:51 2019 +0800
fix save name, support vim
以下省略多個commit信息
得到的結果是每個倉庫對應這個tag
的commit
打印。
有幾個問題。
- 沒有打印倉庫名
- 對於沒有打上該
tag
的倉庫,git log
會有報錯信息 - 打印的冗余信息太多,例如時間,
commit
信息等,都無關緊要,只需要唯一的commit
號即可
定制格式
問題1
和2
可以通過為repo forall
加上-p
參數來一並解決。
使用repo forall -p -c git xxx
,會打印出倉庫路徑,並忽略錯誤。
問題3
可以通過定制git log
的格式來解決。
我是想到了平時一直在使用的一個git
的alias
,它可以定制git log
的顯示格式。
$cat ~/.gitconfig | grep "lg"
lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
明顯看出用--pretty=format
就可以指定格式,各個參數的含義對照執行結果也容易猜測出來,不想猜就通過man git log
來確認下。
如果平時沒用過定制的git log
輸出,只要有這個想法,搜索一下也有很多介紹。
既然有git
原生提供的功能,就不要自己費力氣去過濾處理了。
所以問題3
的解決方式是,加上--pretty=format:'%h'
參數。
修改后為
#!/bin/bash
# v2
tag=$1
repo forall -p -c git log -1 $tag --pretty=format:'%h'
此時就沒有冗余信息了,輸出格式清爽多了。
$ ./repo_share_tag.sh test-v1
project python/convertfb/
774ddb6
project rust/cut-trailing-bytes/
e9e78ee
project shell/EasierMinicom/
059c803
project shell/PathMarker/
4b6e219
project shell/pop-up-task-diary/
5d85ed2
project shell/smartbc/
9d7bc06
生成腳本
導出輕松了,但接收方還是得手工打tag
。
能不能直接生成一個腳本,給到接收方運行,自動打tag
呢 ?
觀察下這個輸出,規律很簡單,一行倉庫路徑,一行commit
號。如果每兩行合為一行,再適當插入一些shell
命令,應該就可以得到shell
腳本了。
兩行合並為一行,根據經驗sed
和awk
應該都能做,具體命令就得搜索下了,簡單搜索可得到
sed -n '{N;s/\n/\t/p}' test //sed的方法
awk '{tmp=$0;getline;print tmp"\t"$0}' test //awk方法
觀察下,awk
似乎更方便進一步定制,那就選awk
吧
#!/bin/bash
# v3
tag=$1
repo forall -p -c git log -1 $tag --pretty=format:'%h' | awk '{tmp=$0;getline;print tmp"\t"$0}'
得到的結果
$ ./repo_share_tag.sh test-v1
project python/convertfb/ 774ddb6
project rust/cut-trailing-bytes/ e9e78ee
project shell/EasierMinicom/ 059c803
project shell/PathMarker/ 4b6e219
project shell/pop-up-task-diary/ 5d85ed2
project shell/smartbc/ 9d7bc06
以上的project
我們是不要的,只要保留 倉庫path
和 commit
。這一點可以將awk
命令中的$0
改成$2
來實現,$0
是整行,$1
對應project
,$2
則剛好是我們需要的path
。
另外得再插入一些固定的字符,將 <path> <commit>
變成 cd <path> ; git tag <commit> tagname
,考慮處理完一個倉庫后還得要退回源目錄,方便處理下一個倉庫,那再加個 cd -
。cd -
表示回到上一個目錄,很實用的命令。
以上都可以通過修改awk
命令來實現,修改后得到:
#!/bin/bash
# v4
tag=$1
repo forall -p -c git log -1 $tag --pretty=format:'%h' | awk '{tmp=$2;getline;print "cd "tmp"; git tag '$tag' "$1"; cd -;"}'
看起來差不多了,重定向到文件中,加上必要的sheban
,即#!/bin/bash
#!/bin/bash
# v5
tag=$1
file=set-tag-$tag.sh
echo "#!/bin/bash" > $file
echo "" >> $file
repo forall -p -c git log -1 $tag --pretty=format:'%h' | awk '{tmp=$2;getline;print "cd "tmp"; git tag '$tag' "$1"; cd -;"}' >> $file
chmod +x $file
#for debug
cat $file
執行后可得到set-tag-test-v1.sh腳本
$cat ./set-tag-test-v1.sh
#!/bin/bash
cd python/convertfb/; git tag test-v1 774ddb6; cd -;
cd rust/cut-trailing-bytes/; git tag test-v1 e9e78ee; cd -;
cd shell/EasierMinicom/; git tag test-v1 059c803; cd -;
cd shell/PathMarker/; git tag test-v1 4b6e219; cd -;
cd shell/pop-up-task-diary/; git tag test-v1 5d85ed2; cd -;
cd shell/smartbc/; git tag test-v1 9d7bc06; cd -;
接收方運行這個腳本即可。
完善腳本
實際驗證下,很快發現問題
- 已經打過了
tag
需要更新,重復打會報錯,需要先刪除同名tag
- 如果接收方代碼中不存在對應的
commit
(例如代碼未更新),雖然會報錯,但腳本沒有暫停,可能會讓人忽略該報錯 - 沒有提示處理的倉庫路徑
- 存在冗余信息,例如
cd -
會打印路徑,其實是沒作用的
解決方式
- 打
tag
之前先刪除原有同名tag
,即執行git tag -d $tag
,再考慮tag
可能不存在會報錯,加上錯誤log
重定向2>/dev/null
- 打
tag
失敗則中止運行,即加上|| exit 1
cd
之前,先打印目標路徑, 即加上echo tmp
- 屏蔽掉
cd -
的輸出, 即加上> /dev/null
順便加點換行,調整下輸出的腳本,可得到
#!/bin/bash
# v6
tag=$1
file=update-tag-$tag.sh
echo "#!/bin/bash" > $file
echo "" >> $file
repo forall -p -c git log -1 $tag --pretty=format:'%h' | awk '{tmp=$2;getline;print "echo "tmp"\ncd "tmp"\ngit tag -d '$tag' 2>/dev/null\ngit tag '$tag' "$1" || exit 1\ncd - > /dev/null\n"}' >> $file
chmod +x $file
#for debug
cat $file
此時生成的打tag腳本就變成了
$ cat ./update-tag-test-v1.sh
#!/bin/bash
echo python/convertfb/
cd python/convertfb/
git tag -d test-v1 2>/dev/null
git tag test-v1 774ddb6 || exit 1
cd - > /dev/null
echo rust/cut-trailing-bytes/
cd rust/cut-trailing-bytes/
git tag -d test-v1 2>/dev/null
git tag test-v1 e9e78ee || exit 1
cd - > /dev/null
echo shell/EasierMinicom/
cd shell/EasierMinicom/
git tag -d test-v1 2>/dev/null
git tag test-v1 059c803 || exit 1
cd - > /dev/null
echo shell/PathMarker/
cd shell/PathMarker/
git tag -d test-v1 2>/dev/null
git tag test-v1 4b6e219 || exit 1
cd - > /dev/null
echo shell/pop-up-task-diary/
cd shell/pop-up-task-diary/
git tag -d test-v1 2>/dev/null
git tag test-v1 5d85ed2 || exit 1
cd - > /dev/null
echo shell/smartbc/
cd shell/smartbc/
git tag -d test-v1 2>/dev/null
git tag test-v1 9d7bc06 || exit 1
cd - > /dev/null
最后加點提示語句,參數檢查
#!/bin/bash
show_help()
{
echo -e "Usage: share_tag.sh <tag>"
echo -e 'Eg. ./share_tag.sh release-v1'
}
[ x"$1" = x"" ] && {
show_help
exit 1
}
tag=$1
file=update-tag-$tag.sh
echo "#!/bin/bash" > $file
echo "" >> $file
repo forall -p -c git log -1 $tag --pretty=format:'%h' | awk '{tmp=$2;getline;print "echo "tmp"\ncd "tmp"\ngit tag -d '$tag' 2>/dev/null\ngit tag '$tag' "$1" || exit 1\ncd - > /dev/null\n"}' >> $file
chmod +x $file
#for debug
cat $file
echo -e '\033[0;31;1m'
echo "to update tag $tag, please run ./$file"
echo -e '\033[0m'
總結
本地已打好tag
,如 test-v1
,需要分享給他人則運行./repo_share_tag.sh test-v1
本地沒有tag
,那可以先批量打一個tag
,再如上所述導出腳本給他人。
批量打tag
: repo forall -c git tag test-v1
批量刪tag
: repo forall -c git tag -d test-v1
東拼西湊出來的腳本,暫時也夠用了,后續有更新會放到 https://github.com/zqb-all/repo-share-tag
blog: https://www.cnblogs.com/zqb-all/p/13057786.html
公眾號:https://sourl.cn/BKeNSL