Git pull as rebase除非存在本地标记

时间:2014-09-09 08:44:42

标签: git tags rebase

每当我执行git pull时,我已经配置git来使用rebase。但是,如果我在头上设置了标记,并且存在远程更改,那么将拉动那些远程更改,然后我的本地更改将作为分支时间轴上的新提交重播,但我的标记将保留在旧提交中。旧提交现在成为其时间轴的叶节点,并且它给出了一个令人困惑的历史视图。

是否可以(通过简单的脚本或最好通过git命令)来实现“git pull'只有当HEAD上没有标签时才进行rebase(或者甚至更好的是没有标签导致HEAD的未提交的提交)。

PS:对我来说,从来没有在我已经推动的东西上设置标签可能是明智的,因为这样可以解决时间线凌乱的问题,但这不是我想到的解决方案

1 个答案:

答案 0 :(得分:2)

内置任何内容,但如果您愿意使用git fetch代替git pull,则构建起来很容易:

  1. 保存当前上游头
  2. 从远程获取
  3. 如果通过提取,测试和改变来引入和/或删除转速(否则停止,没有任何事情可做)。
  4. 步骤1只是(假设 1 当前分支在更新方面映射到origin/branch):

    git update-ref refs/save/origin/branch refs/remotes/origin/branch
    

    您实际上不必命名此refs/save/origin/branch,但显然拥有已保存上游的整个名称空间可以实现未来的灵活性。但是,它更简单(不需要映射到上游分支名称)来使用固定名称,例如ORIG_UPSTREAM(只需使用它来代替拼写出的refs/save/名称)

    步骤3需要列出(或至少计数/测试 - 非空)提交已添加或删除的提交。要获取修订列表,我们需要git rev-list。我们可以很容易地看到使用DAG子集操作添加和删除的内容:

    git rev-list refs/save/origin/branch..origin/branch  # these were added
    git rev-list origin/branch..refs/save/origin/branch  # these were removed
    

    我们在这里不需要实际的提交ID,因此我们可以添加--count并获得两个计数。如果总和(或任何一个)非零,则上游已发生变化,您可能会进行变基。 (如果上游有删除了修订版,你可能不想在没有特别小心的情况下进行修改,但我会在这里忽略它。)

    现在,test-and-maybe-rebase序列如下:

    1. 对于您可以重新定位的每次提交,看看它是否已标记
    2. 如果没有标记,请执行rebase(否则合并?)。
    3. 在这里,对于第1步,我们确实需要修订列表,但这很容易获得:

      git rev-list refs/save/origin/branch..HEAD
      

      这些是您在fetch之前没有上游的提交。 (您可以使用origin/branch..HEAD来获取上游不再拥有的提交,但这可能包括故意删除上游的提交,您仍然拥有副本。一如既往,您可以省略{{1}这个词我在这里使用它来强调它。)

      现在您只需要查看这些提交ID是否是您的代码的目标,您可以使用HEAD进行测试以迭代自己的代码。如果使用带注释的标记,我们必须注意在比较之前解析对提交的标记引用,因为您将在此处看到带注释的标记对象ID。

      这可能看起来像这样(未经测试):

      git for-each-ref refs/tags

      (如果列表通常很小,那么对提交ID列表的两个文件进行排序可能是过度的,但如果不是,则会给你O(n log n)行为 - 如果你不熟悉{{1它找到两个文件唯一且通用的行,将它们打印在三列中;使用TF1=$(mktemp -t rbcheck) || exit 1 TF2=$(mktemp -t rbcheck) || { rm -f $TF1; exit 1; } trap "rm -f $TF1 $TF2" 0 1 2 3 15 git rev-list refs/save/origin/branch.. | sort > $TF1 git for-each-ref refs/tags | while read sha1 objtype tagname; do [ $objtype = tag ] && sha1=$(git rev-parse $sha1^{commit} echo $sha1 done | sort > $TF2 if [ $(comm -12 $TF1 $TF2 | wc -l) -gt 0 ]; then echo there are some tags in the to-be-rebased commits else echo there are no tags, it is safe to rebase fi 跳过除常用项目列之外的所有文件的打印。)

      但最终,我怀疑你真正想要的是这个(更难):

      1. 对于您可以重新定位的每个提交,看看它是否被标记;如果没有标记,只需改变(步骤3-4将是无操作)
      2. 做退缩;看它是否成功(如果没有,必须手动恢复后续步骤,并停止并获得用户的帮助)
      3. 将每个原始提交与其重新定义的等价物配对(注意:这一步很难完全通用;见下文)
      4. 对于标记的每个此类提交,将标记移动到已更改的等效项。
      5. 要在此处实施第3步,我们需要使用comm来获取" pre-rebase"和" post-rebase"提交。如果这些列表长度相同,我们就会处于良好状态。如果post-rebase列表较短,则省略一些提交,可能是由于上游的冗余。 (如果rebase后列表更长出现问题:这不应该发生,至少除非你做了交互式rebase并拆分了一些提交。)

        如果提交已经消失,则来自" pre"到"发布" rebase不再是1比1,你必须决定如何处理一个" new"提交不存在。 (这部分取决于你,你可以重新映射到一个祖先或孩子,或者认为它是一个错误。)此外,并不总是明显哪些提交被省略。为了完全理解这一点,您可以尝试通过-12一次重复一次提交的rebase过程;或者您可以比较提交消息文本,如果这些文本足够独特(这会更快)。

        我会给你(或其他人)实施更高级的方法。 : - )


        1 如果你的git不是太古老,那么获取当前分支的上游名称实际上很容易:

        git rev-list

        通常应采用git cherry-pick形式。请注意,如果没有上游,upstream=$(git rev-parse --symbolic-full-name @{u}) || exit 1 将退出并显示错误,因此此处为refs/remotes/remote/branch。要将其转换为rev-parse之类的名称,请首先确保它以|| exit 1开头,然后替换为:

        refs/save/
        例如