一个分支的所有提交逐个推送,一个接一个。

5

有没有一种简单的方法,可以逐个推送功能分支上的所有提交(从master开始),使每个提交都触发远程端的推送钩子? 这对于“测试优先”场景非常有用,因为您首先实现并提交失败的测试,然后再进行修复。

我知道我可以执行git push sha:remote-ref-name,但手动操作很麻烦。

3个回答

3
这个脚本应该是有效的:
#!/bin/bash

if [ ! -d .git ]; then
    echo "$(basename $0): not a git directory." 1>&2
    exit 1
fi

# lbranch - name of local branch
# remote  - name of remote
# rbranch - name of remote branch
lbranch=$(git rev-parse --abbrev-ref HEAD)
remote=$(git config branch.${lbranch}.remote)
rbranch=$(git config branch.${lbranch}.merge)
rbranch=${rbranch/refs\/heads\//}

for rev in $(git rev-list --reverse ${lbranch} --not --remotes);
do
    git push ${remote} ${rev}:${rbranch}
done

这看起来不错,谢谢。你有没有可能使用 git push ref:remote-branch 语法并避免使用 reset --hard - krlmlr
@krlmlr,您无需创建新分支或进行任何重置操作。您只需要循环并将各个SHA1推送到远程分支即可。就像您建议的那样,使用git push origin SHA1:REMOTE_BRANCH命令即可(正如我在我的答案中所建议的)。 - John Szakmeister
我建议将新命令称为 ipush,类似于 imerge ;-) - krlmlr

1
@cforbish的提交列表并没有做到我认为提问者(和我)需要的:获取一个按照从旧到新顺序排列的未推送提交列表。这对我有用(感谢他的脚本的通用性,我在此省略了一些东西)。
#!/bin/bash

for rev in $(git rev-list --reverse origin/master..HEAD);
do
    git push -f origin ${rev}:master
done

你的解决方案与@cforbish的不同,它使用了..符号,省略了--not --remotes并使用了强制推送。这些修改中哪一个使它对你起作用了? - krlmlr
@krlmlr:当我使用他@cforbish的for语句时,只列出了三个提交,而我比主分支领先约200个提交(从SVN转换而来)。如果我回忆起来,当我手动尝试时,Git要求使用-f - darda
这就是我一直在寻找的。 - Ferit

1

我不确定这对于测试优先的场景有什么好处,除非你指的是通过一次提交触发构建, 你实际上会看到构建失败然后成功。

话虽如此,你可以通过在服务器端使用更好的钩子脚本来达到你想要的效果。 post-receive 钩子脚本接收旧的 sha1 和新的 sha1,因此您可以计算其间的修订列表:

$ git rev-list OLD_SHA1..NEW_SHA1

这将包括从合并中带入的单个提交。您可以根据工作流程决定是否需要单独测试它们,因为它们可能已经经过测试。因此,您可以使用以下命令仅限于主干提交:

$ git rev-list --first-parent OLD_SHA1..NEW_SHA1

然而,还有一些特殊情况:

  • 分支可能被变基。您是否要重新测试在分支先前版本中存在的新变基提交?

  • 该分支可能是新的。但您不想重新测试整个分支的历史记录。那么,您用哪个分支来确定已经测试过什么了呢?master

  • 该分支可能被删除。这种情况很容易...您可能什么也不需要做。

这里是在post-receive钩子中读取更新的引用的示例:

#!/bin/bash

# The current working directory will be in the bare repo being updated.

while read oldrev newrev refname
do
    # Skip deleted branches.
    if [ "$newrev" != "0000000000000000000000000000000000000000"]
    then
      if [ "$oldrev" == "0000000000000000000000000000000000000000"]
      then
          # New branch.  Test all commits in the branch by using master
          # as a base.  If this was master being created, then nothing will
          # get triggered.
          oldrev=$(git merge-base master $newrev)
      fi

      for sha1 in $(git rev-list ${oldrev}..${newrev})
      do
          # Tip off build server for each commit between oldrev and newrev
          tip-off-build-server $sha1
      done
    fi
done

请注意,您正在更新的引用名称为$refname(它是引用的完整名称,而不是缩写版本)。 您可能需要对此进行一些修改以适应您的构建服务器。
所有这些都取决于您的特定设置。 如果您的构建服务器只是轮询存储库的分支,则无法避免一次测试多个提交。 如果它被推送了,则可以使其更加智能化。 但是在此处可能需要更多的逻辑。 当每次推送中有超过x个提交时,您可能不希望采用此方法。 想象一下拉入另一个项目的历史记录。 它可能有数千个提交,您可能不希望在这种情况下单独测试它们。 当分支分歧并计算需要测试的修订版时,也可能存在一些问题。 您需要更加彻底地测试以确保它表现出您想要的行为。
顺便说一下,githooks对于确定传递到脚本中的内容非常有用。
您也可以在客户端进行单个推送,但需要一些脚本。您可以使用类似上面的git rev-list,其中$newrev将是您本地分支的当前末尾。$oldrev可能会采用master(或develop)或@{upstream}如果之前已经推送了该分支。重点是您可以使用git rev-list来帮助列出要推送的提交列表,然后逐个推送。我不建议这种方法,但它是可行的。

不幸的是,推送钩子(实际上是 GitHub 上的 Travis CI 钩子)并不完全在我的控制之下,因此我真的更喜欢客户端解决方案。否则,是的,我确定适当的服务器端钩子是正确的方法。 - krlmlr
事先知道这个信息真的会非常有帮助。 - John Szakmeister
抱歉。虽然你的大部分文章对我当前的问题没有帮助(关于rev-list和githooks的参考是最有用的),但它可能对其他人有用,或者在未来的问题中对我有所帮助。非常感谢! - krlmlr
没关系。我只是想让你知道下次在 Stack Overflow 上发布问题的时候,能够提供更多相关情况描述就更好了。非常感谢,我很感激! - John Szakmeister

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接