repo 導出本地 git tag 給他人


背景

使用 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信息

得到的結果是每個倉庫對應這個tagcommit打印。

有幾個問題。

  1. 沒有打印倉庫名
  2. 對於沒有打上該tag的倉庫,git log會有報錯信息
  3. 打印的冗余信息太多,例如時間,commit信息等,都無關緊要,只需要唯一的commit號即可

定制格式

問題12可以通過為repo forall 加上-p參數來一並解決。

使用repo forall -p -c git xxx,會打印出倉庫路徑,並忽略錯誤。

問題3可以通過定制git log的格式來解決。

我是想到了平時一直在使用的一個gitalias,它可以定制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腳本了。

兩行合並為一行,根據經驗sedawk應該都能做,具體命令就得搜索下了,簡單搜索可得到

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我們是不要的,只要保留 倉庫pathcommit。這一點可以將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 -;

接收方運行這個腳本即可。

完善腳本

實際驗證下,很快發現問題

  1. 已經打過了tag需要更新,重復打會報錯,需要先刪除同名tag
  2. 如果接收方代碼中不存在對應的commit(例如代碼未更新),雖然會報錯,但腳本沒有暫停,可能會讓人忽略該報錯
  3. 沒有提示處理的倉庫路徑
  4. 存在冗余信息,例如 cd - 會打印路徑,其實是沒作用的

解決方式

  1. tag之前先刪除原有同名tag,即執行git tag -d $tag,再考慮tag可能不存在會報錯,加上錯誤log重定向 2>/dev/null
  2. tag失敗則中止運行,即加上 || exit 1
  3. cd之前,先打印目標路徑, 即加上 echo tmp
  4. 屏蔽掉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

批量刪tagrepo 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM