如何更新 fork 以发出干净的拉取请求?

How to bring fork up to date to make clean pull requests?

我正在 github

上的分叉副本上为开源回购做出贡献

我通常每隔几个月只修复我发现的奇怪错误,与此同时还会发生很多其他开发。我将拉取请求放入我的分支中以获取新更改。

现在它们出现在我的 pull requests back to the original repo 中。

如何创建仅包含每个修复提交的干净拉取请求?我已经尝试 git 在本地获取原始 repo 并从中进行合并,但我遇到了同样的问题。

tl;dr:分支 upstream/master。不要向你当地的主人承诺。

如果您的分支有本地更改,而您只是与上游合并,您仍然会有那些本地更改。当您基于合并后的母版提交 PR 时,它将包含这些本地更改。

我们来演示一下。这是合并本地更改后的样子。

# Your situation after merging with local changes.
# D - E - F are new changes from upstream.
# 1 - 2 - 3 is your local changes.

A - B - C - D - E - F [upstream/master]
         \           \
          1 - 2 - 3 - M [master]

现在你从 master 创建一个分支并添加一些提交。

A - B - C - D - E - F [upstream/master]
         \           \
          1 - 2 - 3 - M [master]
                       \
                        4 - 5 - 6 [feature]

如果您以 upstream/master 为基础将特性作为 PR 提交,它将拖入 1 - 2 - 3 以及 4 - 5 - 6,因为它们都包含与 [=69= 的差异].

这就是为什么要避免直接提交给 master,你不再有新分支的共同基础。相反,在分支机构中完成所有工作并将它们作为 PR 提交。


最简单和最安全的方法是将您的工作分支到 upstream/master 而不是 master。那么你的叉子处于什么状态并不重要。

$ git checkout -b feature upstream/master
# then make a few commits

                      4 - 5 - 6 [feature]
                     /
A - B - C - D - E - F [upstream/master]
         \           \
          1 - 2 - 3 - M [master]

如果您有一个现有的 master 分支,请将其变基到 upstream/master。这将重写每个提交,就像您将其写在 upstream/master 之上一样。可能有冲突,按需修复。

# Before with feature based on master.

A - B - C - D - E - F [upstream/master]
         \           \
          1 - 2 - 3 - M [master]
                       \
                        4 - 5 - 6 [feature]

# Rebase onto upstream/master the commits from master to feature.
$ git rebase --onto upstream/master master feature

                      4A - 5A - 6A [feature]
                     /
A - B - C - D - E - F [upstream/master]
         \           \
          1 - 2 - 3 - M [master]
                       \
                        4 - 5 - 6

原来的4-5-6最终会被删除


要恢复你的 fork 的 master,return 你的 master 到你的上游的 master。

首先,在您现有的 master 上创建一个新分支以保留任何本地更改。

$ git checkout master
$ git branch dev  # or whatever you want to call it

A - B - C - D - E - F [upstream/master]
         \           \
          1 - 2 - 3 - M [master]
                        [dev]

现在您对 master 所做的任何本地更改都将保留在您的 dev 分支中。您可以随意命名它,但要避免使用上游已经存在的分支名称。

然后将本地 master 移动到上游的 master。

$ git checkout master
$ git reset --hard upstream/master

                      [master]
A - B - C - D - E - F [upstream/master]
         \           \
          1 - 2 - 3 - M [dev]

Git 中的分支只是指向提交的标签。 git reset 是您随意移动它们的方式。这会将您的本地 master 移动到 upstream/master 指向的位置。 --hard 指的是如何处理暂存区和检出文件(工作副本)。 --hard 表示也将它们重置为 upstream/master。

现在你的主人就是他们的upstream/master。 dev 有你对 master 的本地更改。

最后,将你的新本地master推送到origin。既然动了,就得用力。 不要使用 --force 使用更安全的 git push --force-with-lease.

如果您想更新本地主控,只需 git pullgit pull 只是 git fetch 加上 git merge。我们将 git pull 作为两个单独的步骤进行演示。

# Fetch new commits, that's G and H.
$ git fetch upstream

                      G - H [upstream/master]
                     /
A - B - C - D - E - F [master]
         \           \
          1 - 2 - 3 - M [dev]

$ git checkout master
$ git merge upstream/master

                            [master]
                      G - H [upstream/master]
                     /
A - B - C - D - E - F
         \           \
          1 - 2 - 3 - M [dev]

因为 master 是 upstream/master 的直接祖先,所以不需要合并提交。 Git 将“快进”到 upstream/master。