【问题标题】:How to git cherrypick all changes introduced in specific branch如何 git cherrypick 在特定分支中引入的所有更改
【发布时间】:2016-05-28 00:13:18
【问题描述】:

背景信息:

由于没有现有系统的工作流程的限制,我们需要设置一个有点非正统的 git 进程。

(patch)    A-B---F
             |   |
(hotfix)     C-D-E
                 |
(dev)      1-2-3-G

在补丁分支上,有一些提交。这里的文件与 dev 上的文件相似但不完全相同(同步脚本会在许多文件中切换设置顺序,使它们在功能相同时看起来已更改)。

此分支需要修复,因此创建并处理了修复分支。这个分支然后被合并回补丁,到目前为止,很好。

需要将相同的修复程序部署到 dev 分支,以便它与补丁保持相对同步,但尝试合并修补程序分支会导致 git 尝试合并来自 A 和 B 的所有不相关和“未更改”的文件,而不仅仅是 C、D 和 E。

问题:

似乎cherry-pick 只从选定的提交中获取更改方面做了我们想要的,但我真的想要一种方法来一次挑选给定分支中的所有提交,而不必查找提交每次都是身份证。

【问题讨论】:

标签: git merge git-cherry-pick


【解决方案1】:

似乎cherry-pick 做了我们想要的,只从选定的提交中获取更改,但我真的想要一种方法来一次挑选给定分支中的所有提交,而不必查找提交每次都有id。


使用cherry-pick

git cherry-pick 允许您选择您在任何分支中所做的任何提交到任何其他分支。在您的情况下,您可以简单地检查 master 分支,然后 cherry-pick 您希望的任何分支中的所有提交(cherry-pick 支持范围,因此您可以指定开始和结束提交,而不是列出所有提交)。

通过这种方式,您可以控制提交在所需分支中的显示方式。

例如:

git cherry-pick ebe6942..905e279

# Find the range of commits you wish to re-add to your branch.
# then use cherry-pick to add them back to the branch
git cherry-pick start..end

# If you wish to include the start commit as well add the ^
# This will result in a cherry-pick of the start commit included as well 
git cherry-pick start^..end

如何找到分支的第一次提交?

git log

 # print out the latest commit (first one) of the given branch
 git log  --oneline | tail -1

merge-base

使用merge-base 命令查找分支与原始分支的拆分位置:

git merge-base A B

【讨论】:

  • 是的,但这仍然需要我浏览日志并找到相关的提交 ID。我正在寻找像 git cherry-pick hotfix 这样的东西,它会自动在这个分支中找到提交并像 git cherry-pick D^..E 一样行事,而无需我提供 ID。
  • 用命令更新答案如何找出所需的提交
  • 我真的希望能得到一个单线。如果没有办法直接做到这一点,那么迂回的方式就像:git cherry-pick $(<some command that outputs the first commit hash of a branch> <the branch>)^..$(some command that outputs the last commit hash of a branch <the branch>)不幸的是,这很复杂
  • 命令 git log --oneline | tail -1 给了我整个 repo 的第一次提交。这肯定不是我们想要的。也许git log --oneline -n 1 更好。此外,git merge-base 命令在分支提交开始之前在基本分支中提供提交。也许git log A..B --oneline | tail -1 会更正确。否则,你就是在挑选一些不是从树枝上摘下来的东西。
  • git log <branch> --oneline | head -1 给出了<branch> 上的最新提交。或者你可以这样做:git cherry-pick $(git merge-base A B)..B
【解决方案2】:

签出您的分支(目标分支)并执行

git cherry-pick -m 1 hashCode

【讨论】:

  • 哪个hashCode?小费?基地?
  • 我刚刚使用了合并提交的哈希值。此解决方案有效,但请注意,您将丢失合并分支的所有提交历史记录,因为它们被压缩为一个提交。
【解决方案3】:

如果你想从分支 dev 中挑选所有提交。

试试:

git cherry-pick ..dev

【讨论】:

  • 这显然选择了所选分支的整个历史记录,包括创建分支之前的更改。考虑一下这并不奇怪,因为 git 分支只是指向分支当前提交的指针。这意味着 git 找不到第一个提交。
  • 据我所知和测试,这个命令会自动找到两个分支的合并库。并且只在合并基础之后选择提交。
  • 是的,当然。但是,如果有分支 A,则从该分支分支并创建 B。现在您只想将 B 的更改获取到 C,而不是将 A 的更改也包含在 B 中。您的方法会将它们作为基础A 和 C 的值在 B 之前。你最终会得到你想要避免的 A 的变化。
  • 是的,你是对的。在这种情况下,需要手动计算合并基数。
  • 当 git 可以完美且轻松地进行 rebase 时,它​​怎么可能无法为cherry-picks 做到这一点?毕竟,(从概念上讲)cherry-pick 只是复制一个分支,而 rebase 只是移动一个分支。
【解决方案4】:

这也应该有效。我们将 HEAD(本例中为 master)与分支之间的分歧点称为分歧点,并将其传递给cherry-pick。

git merge-base origin/dev_single_doc_fit HEAD | xargs git cherry-pick {}..HEAD^

【讨论】:

    【解决方案5】:

    另一种快进方式。

    案例:

    你启动了newBranch,提交了第一次提交并重置了Hard

    结果你得到了空的 newBranch,没有那个迟到的提交和 git 树在 HEAD 是干净的。你在 newBranch 分支(可以用 git 分支检查)。

    这里是两步操作:

    • 通过
    • 获取 'trash' 中的提交列表

    git fsck --lost-found

    • 将错过的提交提交回您的分支:

    git cherry-pick --ff 43a7a2d

    【讨论】:

      【解决方案6】:

      根据已接受答案中的讨论,这里是单行的。当你运行它时,你当前在哪个分支上并不重要(可能是一些非默认分支)。这假定devel 是默认分支,并且您希望在分支Bdevel 分支分歧之后从分支中挑选所有提交。

      git cherry-pick $(git log devel..B --pretty=format:"%h" | tail -1)^..$(git log B -n 1 --pretty=format:"%h")
      

      命令语法为first..last,但我们在这里使用了一个变体,它更像first-1^..last

      命令git log devel..B --pretty=format:"%h" | tail -1 获取devel 分支中的提交,该分支是B 分支中第一个提交的父级。这有点笨拙,但git merge-base 命令没有所需的格式选项。

      命令git log B -n 1 --pretty=format:"%h" 只是获取该分支中的最后一次提交。

      这将使您置身于交互式的樱桃挑选环境中。因此,如果进展不顺利,请务必中止。

      【讨论】:

        【解决方案7】:

        假设您知道要从分支中选择的提交数量,您可以使用相对提交表示法。

        git cherry-pick BRANCH_A~10^..BRANCH_A

        这将在 (~10) BRANCH_A 的 HEAD 之前的 10 个提交中挑选所有提交,包括起始提交 (^),并将获取范围 (..) 中的所有提交到 BRANCH_A 的 HEAD .

        【讨论】:

          【解决方案8】:

          一个班轮将是:

          git cherry-pick $(git merge-base master my/branch)..my/branch
          

          您也可以将其转换为 git 别名或 bash 函数。我更喜欢这样使用它,所以我只改变一个地方:

          BRANCH=my/branch; git cherry-pick $(git merge-base master ${BRANCH})..${BRANCH}
          

          【讨论】:

          • 工作正常,在我看来,这是唯一一个不会对用户提出如此简单的请求的不合理要求的解决方案。最重要的是,这是一个非常正常和普遍的问题。如果您尝试为项目做出贡献并使用错误的基础分支,则必须移动它。这是你需要做的。你不想搞砸两个基础分支之间的提交差异,因为你不是这些分支的维护者。
          【解决方案9】:

          我发现rebase 上的这篇文章对相同的用例很有帮助。

          假设您有如下提交

          commit 6 [my-feature-branch] | merged onto master commit
          commit 5
          commit 4 [master]
          commit 3
          commit 2 [production]
          commit 1
          

          并且您希望只有 6 个包含在生产中。

          git checkout my-feature-branch
          git rebase production 5
          

          这里 5 是您想要(不包括 5)进行更改并放入生产分支的提交哈希。

          【讨论】:

            【解决方案10】:

            使用rebase

                A _ _ _ B
              /
            - - - C
            
            

            git rebase --onto=target start end

            git rebase --onto=C A B
            
                A
              /
            - - - C - - - B
            

            【讨论】:

              【解决方案11】:

              我很失望,没有一个很好的解决方案,所以我自己做了一个别名

              aliasName = "!f() { git cherry-pick -n -Xsubtree $(str=$(git log $1 --grep 'Create branch' -n1 --oneline) && echo ${str:0:11})...$1 ; }; f"
              

              所以这看起来像

              git aliasName my/branch-name-to-cherry-pick
              

              我很幸运,我们对所有被切割的分支都有一个通用名称,并且它们在消息中包含“创建分支”。此别名运行一个 bash 函数,该函数从与指定分支参数 ($1) 上找到的 grep 匹配的第一个提交哈希到分支头部之间挑选范围。

              由于名称匹配的原因,它并不完美,但它在我的情况下效果很好,也许对其他人也适用。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2014-06-08
                • 2022-10-12
                • 2012-02-03
                • 1970-01-01
                • 2011-08-20
                • 1970-01-01
                • 2016-03-07
                • 2022-01-05
                相关资源
                最近更新 更多