TL;DR:你需要一些 new 提交。您无法通过现有的提交获得想要的结果。
长
Git 不是关于分支或文件:Git 是关于提交。拉取请求2 使用分支名称,但只是为了让 Git 可以找到 commits。
换句话说,一切都是关于提交的。名称——像master 或develop 这样的分支名称,或者像v2.1 这样的标签名称,或者像origin/master 这样的远程跟踪名称,甚至是GitHub 上的拉取请求——实际上只是识别一个特定的提交。每个提交包含个文件——实际上是所有个文件的完整快照——以及元数据,例如谁创建它(用户名和电子邮件地址)、何时(日期和-时间戳),以及原因(日志消息)。
在一个分支或一个拉取请求中 有多个提交的事实发生是因为每个提交都通过哈希 ID 记录其 父 提交。每个提交都有自己唯一的哈希 ID,每个提交1 都说 ... 如果您想知道我之前发生了什么,请查看提交 XXXXXX,其中 @ 987654325@s 是上一次提交的哈希 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 发出拉取请求,该请求要求某人将 c1 和 c2 合并到他们的存储库中,并将他们的名字之一设置为指向 c2 .
无论你做什么,你总是会问他们——不管他们是谁——获取指向现有共享提交 B 的分支名称,这已经在他们的存储库以及你的存储库中, 并将新的提交添加到提交 B 的末尾。您控制的是您要求他们使用的特定提交。他们将从他们的最后的每一个提交——他们的B 的副本——到这个新的提示提交。所以你可以做一个新的提交,我们称之为c3,它在c1之后,你记得通过分支名称name3:
c3 <-- name3
/
...--B--c1--c2--D--E--F--G <--master
这个提交c3 有你喜欢的任何内容。然后,您向他们发送拉取请求,要求他们设置他们的 master(或任何其他分支名称),以便他们复制提交c1 和@987654352,而不是指向共享提交B @ 进入他们的存储库,然后将他们的名字设置为指向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每个非空存储库中至少有一个提交具有 no 父级:这是第一个提交,它没有先前的提交,因为它不能。一些提交有两个甚至更多的父级:这些是 merge 提交。不过,大多数提交只有一个父级。 Git的历史是提交,使用从last提交向后工作形成的向后看链串在一起,通过某个分支名称找到,一次提交一个(或达到合并时有两个或更多)。
2拉取请求实际上并不是 Git 本身的一部分:它们是附加组件,由 GitHub 和 Bitbucket 等地方提供。 Git 提供的是提交本身和方法——git fetch 和 git push——用于在对等存储库之间接收和发送提交。在 GitHub 之前,人们会克隆一个 Linux 存储库,进行新的提交,然后通过电子邮件发送补丁以供审查和修订……事实上,他们仍然这样做。