• 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/
    /	/p}' test        //sed的方法
    awk '{tmp=$0;getline;print tmp"	"$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"	"$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"
    cd "tmp"
    git tag -d '$tag' 2>/dev/null
    git tag '$tag' "$1" || exit 1
    cd - > /dev/null
    "}' >> $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"
    cd "tmp"
    git tag -d '$tag' 2>/dev/null
    git tag '$tag' "$1" || exit 1
    cd - > /dev/null
    "}' >> $file
    
    chmod +x $file
    
    #for debug
    cat $file
    
    echo -e '33[0;31;1m'
    echo "to update tag $tag, please run ./$file"
    echo -e '33[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

  • 相关阅读:
    ASPNET下的路径辅助类
    分析函数计算起始,结束日期.
    Debugging SQL Server 2005 Stored Procedures in Visual Studio
    Storing Binary Files Directly in the Database Using ASP.NET 2.0
    Fw:Managing View State in ASP.NET 4 Using the New ViewStateMode Property
    Using ASP.NET 3.5's ListView and DataPager Controls: Displaying Data with the ListView
    Passing Information Between Content and Master Pages .
    转:Querying a Hierarchical ParentChild Structure in LINQ
    续上篇:比较彻底的清除"代理木马下载器"的方法
    面向过程和面向对象--从C到C#
  • 原文地址:https://www.cnblogs.com/zqb-all/p/13057786.html
Copyright © 2020-2023  润新知