如何创建一个 git 分支与主分支的自定义差异?

How to create a git branch with custom diff to master branch?

抱歉问题标题含糊不清,我不知道如何更好地表达它。基本上我有一个 master 分支有两个提交 c1c2:

    +-----> dummy1   +-----> dummy2
    |                |
    |                |
    |                |
    |                |
  c1+              c2+
   .                 .
   └── a.txt +       ├── a.txt
                     ├── b.txt +
                     ├── not-wanted.txt +
                     └── c.txt +

c1 已添加 a.txt 文件。如您所见,c2 又添加了三个文件。现在我想要两个虚拟分支,一个来自 c1,另一个来自 c2,这样当我执行 git diff dummy1..dummy2 时,我只看到文件 b.txtc.txt在差异中。因此 dummy1<-dummy2 之间的结果 PR 仅显示 b.txtc.txt 而不是 not-wanted.txt.

这可能吗?

无需创建单独的分支。您可以区分当前分支中的提交:

git diff c1 c2

然后,如果需要,使用 diff 创建补丁:How to see the changes between two commits without commits in-between?

由于您不希望 not-wanted.txt 出现,看来您需要在删除 not-wanted.txt.

后创建一个新提交
Delete not-wanted.txt
git commit -m "c3"

现在从 c1 和 c3 创建两个分支

git branch dummy-1 c1
git branch dummy-2 c3

如果你还想在某个分支中备份not-wanted.txt文件,你可以在c2创建分支:

git branch backup-branch c2

TL;DR:您需要一些 new 提交。您无法通过现有提交获得想要的内容。

Git 与分支或文件无关:Git 与 提交 有关。 Pull requests2 使用分支名称,但只有这样 Git 才能找到 commits.

换句话说,一切都与提交有关。名称——分支名称如 masterdevelop,或标签名称如 v2.1,或 remote-tracking 名称如 origin/master,甚至 Git 上的拉取请求]Hub——实际上只是识别 一个特定的提交 。每个提交 持有 个文件——实际上是 所有 个文件的完整快照——以及元数据,例如创建者(用户名和电子邮件地址),何时(date-and-time-stamp),以及为什么(日志消息)。

分支或拉取请求中 中有多个提交 这一事实的发生是因为每个提交都记录了其 parent 提交,通过哈希 ID。每个提交都有自己唯一的哈希 ID,每个提交1 都表示 ... 如果您想知道我之前发生了什么,请查看提交 XXXXXX,其中 Xs 是先前提交的哈希 ID。

因此,在您的设置中,您对 master 进行了一系列提交,这些提交要么在 c2 结束,要么继续下去:

...--B--c1--c2--D--E--F--G   <--master

其他人可能在他们的存储库中有一系列提交,这些提交在 B 处结束。如果是这样,您可以创建一个指向 c1 的名称,从而得到:

...--B--c1   <-- name1
          \
           c2--D--...--G   <-- master

如果您现在使用 name1 向其他人发出拉取请求,您是在要求其他人(其提交链结束于 B)复制您的提交 c1 到他们的存储库中,并设置一些 他们 的名称指向 c1.

你也可以让名字指向c2:

...--B--c1   <-- name1
          \
           c2   <-- name2
             \
              D--...--G   <-- master

并使用您的 name2 发出拉取请求,要求某人将 c1c2 合并到他们的存储库中,并且将他们的名字之一设置为指向 c2.

无论你做什么,你总是会要求他们——无论他们是谁——接受他们的 branch-name 指向现有的共享提交 B,这已经在他们的存储库以及您的存储库,并将新提交添加到他们提交的末尾 B。您控制的是您要求他们使用哪个特定提交。他们将从他们的末端——他们的 B 副本——到这个新的 tip 提交。所以你可以做一个新的提交,我们称之为 c3,在 c1 之后,你记得通过分支名称 name3:

           c3   <-- name3
          /
...--B--c1--c2--D--E--F--G   <--master

本次提交c3有你喜欢的任何内容。然后,您向他们发送一个拉取请求,要求他们设置 their master(或任何其他分支名称),这样他们就不会指向共享提交 B,而是复制将 c1c3 提交到他们的存储库中,然后将他们的名称设置为指向 c3.

为此:

$ git checkout -b name3 <hash-of-c1>
... make whatever changes you like, e.g., git cherry-pick -n <hash-of-c2>
... fix things up, git add files as needed ...
$ git commit

创建新的提交 c3,您的分支名称 name3 将指向该提交。然后,您可以使用此 name3 名称和新的 c3 提交发出拉取请求。


1每个 non-empty 存储库中至少有一个提交有 没有 parent:这是第一个提交,它没有以前的提交,因为它不能。一些提交有两个,甚至可能更多,parents:这些是 merge 提交。不过,大多数提交只有一个 parent。 Git 的历史 提交,使用 backwards-looking 链从 上一个 提交向后工作形成的链串在一起,通过某个分支名称找到,一次提交一次(或达到合并时两次或多次提交)。

2拉取请求实际上并不是 Git 本身的一部分:它们是 add-ons,由 GitHub 和比特桶。 Git 提供的是提交本身和方法——git fetchgit push——用于在对等存储库之间接收和发送提交。在 GitHub 之前,人们会克隆一个 Linux 存储库,进行新的提交,然后通过电子邮件发送补丁以供审查和修订……事实上,他们仍然这样做。