从另一个分支挑选或合并特定目录的git cherry-pick或merge

37

我在StackOverflow上看到了不同的帖子,它们对樱桃拣选进行了一些解释,但是他们代码中的注释并没有说明什么是分支,什么是目录。例如 git checkout A -- X Y 没有告诉我太多。

基本上我想要做的是:

  • master 分支创建新分支 featureA
  • 将来自 dev 分支的目录 /tools/my-tool合并到 featureA

4
通常情况下,当在git命令中出现--时,左侧是分支名称或提交说明符,右侧是路径。 - torek
为什么你需要针对那个进行挑选呢?你可以简单地手动合并目录。 - Max Yankov
4
谢谢@torek。我最终所做的是git checkout dev -- tools/my-tool - daleyjem
6个回答

42

以下是从另一个分支挑选一个文件夹的提交记录的正确方法:

git format-patch -k --stdout master...featureA -- tools/mytool | git am -3 -k

这将仅针对“tools/mytool”文件应用补丁,并按顺序进行。

如果在任何提交上出现合并冲突,它将暂停以供您修复。 git am --continue 将在离开的地方继续。

另请参阅Git文档:


3
@daleyjem 这真的值得被接受 ✅ 作为正确答案。它完全符合您的要求。如果在您的版本 X 和 A 的版本之间仅创建了新文件,并且没有删除任何文件,则 Checkout checkout A -- X 就可以正常工作。 - Inigo
@sean-dunap,这个问题值得写成一个回答。如果还没有人写过,请告诉我,我会写一个! - Inigo
这绝对比最受赞同的答案更接近我所寻找的。+1 - BHarms
类似地,解决了git format-patch的限制,即合并提交被拆分开来:https://dev59.com/O3E95IYBdhLWcg3wf-AF#8840381。该答案展示了如何为每个顶级提交创建一个提交。 - krlmlr

35
为了回答原问题——如何挑选一些目录(作为提交而非强制检出),这是可能的。假设featureA已经与master分叉,并且你想要带上tools/my-tool提交。

假设您从未进行过同时包含来自/tools/my-tool和其他目录内容的提交

这将得到一个tools/my-tool提交清单,按照时间先后顺序排列(并不在featureA中) ,用于向master挑选目录:
git log --no-merges featureA...master tools/my-tool

换句话说:

git log --no-merges source_branch...dest_branch my/firstpath my/secondpath [...]

要按时间顺序获取您需要的提交记录,您需要先反转输入行的顺序(例如使用 tail -rtac),然后隔离提交哈希的列(例如使用 cut):

git log --format=oneline --no-merges featureA...master tools/my-tool \
    | tail -r \
    | cut -d " " -f 1

要一次完成整个操作,请执行以下操作:

git cherry-pick $(git log --format=oneline --no-merges featureA...master tools/my-tool | tail -r | cut -d " " -f 1)

8
在Linux系统中,无法使用"tail -r"命令,但可以使用"tac"作为其替代命令。请注意,"tac"与"tail -r"在功能上略有不同,但用法类似,都可将文件内容以相反的顺序显示。 - kokorins
对时间顺序进行微调 - git log --pretty=tformat:"%h" --no-merges featureA...master tools/my-too | tail -r - rh0dium
这似乎包括在功能分支中已经应用的所有提交。如何只获取尚未合并的提交? - Chris Dodd
@ChrisDodd 我不认为我理解你的问题。如果你想知道如何列出仅存在于一个分支中而在另一个分支中不存在的提交,那么这可以作为一个单独的步骤来完成。 - Ian
5
除了使用 tac 或者 tail -r 以及 cut,你也可以使用 git rev-list --no-merges --reverse 直接获取一份反序的修订 ID/哈希列表。注意不要改变原意。 - Achilleas
显示剩余3条评论

17

注意:

  • git cherry-pick 用于将一个完整的提交(或多个提交)应用到另一个分支上。它没有“路径”这个概念。
  • git checkout 用于更新工作树(如果没有指定路径,则有效地切换分支并更新HEAD)。

git checkout [-p|--patch] [<tree-ish>] [--] <pathspec>...

当给出<paths>--patch时,git checkout不会切换分支。它会从索引文件或命名的<tree-ish>(通常是一个提交)更新工作树中的指定路径。可以使用<tree-ish>参数来指定要用于给定路径更新索引的特定tree-ish(即提交、标签或树)。

您的命令git checkout dev -- tools/my-tool会更新特定路径,但它不是“合并”或“git cherry-pick”。


13
您可以使用git checkout <from_branch> -- <files_to_bring>命令。例如:git checkout dev -- tools/my-tool 解释:这告诉 git 从分支 dev 和路径 tools/my-tool 复制/替换文件到您当前的分支。

1

那个解决方案虽然有趣,但会删除提交历史记录。如果我只想要这些文件的最新版本,我可以在我的IDE中使用文件历史记录来实现。 - Jason

1
对于需要在从一个仓库移动提交到另一个仓库时也需要排除某些文件或目录的人,请注意您可以使用Sean Dunlap的方法并添加排除规则。因此,他示例的相反方式是包含所有内容但不包括tools/mytool目录,方法如下:
git format-patch -k --stdout \
  master...featureA \
  -- . ':!tools/mytool' \
  | git am -3 -k

-- 之后的 . 包括了仓库中的所有内容。而 ':!tools/mytool' 接着又排除了这个目录(你也可以使用长格式 ':(exclude)tools/mytool')。
(感谢 Nikita Kouevda这篇 Reddit 评论 中的提示。)
另请参阅:Git <pathspec> 文档,了解 :(exclude):!

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