如何clone,然后sync/update/push与上游master fork

How to clone, then sync/update/push a fork with the upstream master

我想我已经通读了一些教程,但我仍然停留在一些完全基础的东西上(我几乎从不使用命令行 git,所以请耐心等待 ;))。

我只想从上游仓库更新我的叉子 (https://github.com/abelbraaksma/visualfsharp) to the latest version of Master (https://github.com/Microsoft/visualfsharp)。由于我有一些我不关心的本地更改,我决定创建一个新的克隆(以前我使用 GUI 工具,但它们是如此混乱和限制,以至于我放弃了它并潜入 git命令 ;).

我做到了:

cd /D/Projects/OpenSource/VisualFSharp2
git init
git clone https://github.com/abelbraaksma/visualfsharp
git fetch https://github.com/Microsoft/visualfsharp
git remote add upstream https://github.com/Microsoft/visualfsharp
git remote add origin https://github.com/abelbraaksma/visualfsharp
git fetch upstream
git checkout master
git merge upstream/master

最后两个命令给出:

git checkout master
Already on 'master'
Your branch is up to date with 'upstream/master'.

git merge upstream/master
Already up to date.

我意识到我做错了一些事情,因为我来自 SVN 和 Mercurial 世界,所以我经常被术语搞糊涂。

我知道目前我在上游仓库的 "master" 中。但我需要从上游回购合并到原点(我的叉子)回购。我假设我需要将本地副本更新为我的叉子的任何头部(但 git checkout master 不这样做)。

我基本上是尝试遵循this guide on syncing, combined with configuring remote points

我在哪里感到困惑或更好,我落后了哪些命令?

git remote -v 给我:

origin  https://github.com/abelbraaksma/visualfsharp (fetch)  
origin  https://github.com/abelbraaksma/visualfsharp (push)  
upstream        https://github.com/Microsoft/visualfsharp (fetch)  
upstream        https://github.com/Microsoft/visualfsharp (push)

TL;DR

你没问题,但你有一个额外的存储库,你可能应该删除它。您通常应该首先克隆(使用 git clone)要让 Git 调用 origin 的存储库,然后 git remote add upstream <the other url> 并从那里开始工作。

阅读下面的详细说明,了解您现在拥有的内容以及如何使用它。

长:你做了什么,详细

git init

这会在当前目录中创建一个新的空 Git 存储库。 (如果这里已经有一个 Git 存储库——如果 git rev-parse --git-dir 会打印一些目录名称,而不是失败并显示 "I find no repository"——它基本上什么都不做,可以安全地 运行。这里有一些极端情况,但你不太可能 运行 进入它们。)既然你打算克隆一个存储库,你真的不想这样做,因为 git clone 做一个git init,我们稍后会看到。

在我们继续下面的 git clone 之前,让我们花点时间记下一个新的空存储库的奇怪状态。您现在可能熟悉 分支名称 master 实际上只是保存一 (1) 次提交的哈希 ID 的想法。 Git 使用名称查找分支上的 last 提交,Git 调用 tip 提交。 Git 然后使用 tip commit 找到以前的或 parent commit,并使用 parent 的 parent 来回溯历史。通过遵循父链,Git 从分支名称中找到所有 可达 的提交。

但是空存储库没有提交master 的名称 master 没有指向的提示——在 master 中没有最新的提交,其哈希 ID 可以存储在名称 master 下。 Git 的解决方案是 没有 master 分支。同时,Git 声明您是 "on branch master",正如 git status 会说的那样——所以您 在一个尚不存在的分支上 .

这个奇怪的因素在后面。现在,让我们继续 git clone,看看它做了什么。在这种情况下,它会创建另一个单独的存储库,您随后根本不会使用它。

git clone https://github.com/abelbraaksma/visualfsharp

这主要相当于一系列命令:

  • mkdir visualfsharp: 在当前目录下新建子目录(当前为/D/Projects/OpenSource/VisualFSharp2)
  • cd visualfsharp:进入新的子目录
  • git remote add origin https://github.com/abelbraaksma/visualfsharp:添加名为 origin 的遥控器(这也为其配置了一些设置)
  • git fetch origin: 获取他们所有的提交
  • git checkout <em>somebranch</em>,其中 somebranch 是通常 master:从 origin/* 名称之一创建一个本地分支名称并使其成为当前分支。

完成这些后,您将返回到原始目录(即,仍然是 /D/Projects/OpenSource/VisualFSharp2)。请注意,您的原始目录是一个 Git 存储库,其 visualfsharp 子目录是另一个。

我们将看到您现在再次执行这些命令中的大部分,但这一次,应用于您当前为空的存储库,它处于您在 mastermaster不存在。

git fetch https://github.com/Microsoft/visualfsharp

这会在 https://github.com/Microsoft/visualfsharp 调用 Git 并从中获取提交和其他对象,并将其放入您之前空的存储库(不是您刚刚创建的克隆!)。就像 git fetch <em>remote</em> 除了没有远程跟踪名称——没有 origin/*upstream/*—因为没有远程可用于构造此类名称。 git fetch 的这种特殊形式可以追溯到古代(2005 年),在 git remote 发明之前,人们可能永远不应该使用它。这不是有害,只是在这里也没有帮助

git remote add upstream https://github.com/Microsoft/visualfsharp
git remote add origin https://github.com/abelbraaksma/visualfsharp

这些很好:他们设置了两个 遥控器。遥控器只是一个简称:

  • 保存一个URL,
  • 提供远程跟踪名称的前导部分,分别为upstream/*origin/*
git fetch upstream

几乎 是您之前 git fetch 的重复。不过这一次,您的 Git 使用您指定的名称 — upstream — 来获得 URL。所以你的 Git 再次调用 https://github.com/Microsoft/visualfsharp 的 Git。自上次获取以来,您的 Git 从他们那里获得了任何新的提交(以及任何其他必要的 Git 对象以配合这些提交)——可能 none,具体取决于您在第一个和第二个。如果您没有 运行 之前的 git fetch,这将在获取所有提交时获取每个 Git 对象。

但是现在,在获得提交后,有一个关键的区别:你的 Git 使用 他们所有的分支名称 并将它们重命名为你拼写的远程跟踪名称上游/<em>随便</em>。它现在可以执行此操作,因为现在您使用的是遥控器,而不仅仅是原始 URL。远程 - 文字字符串 upstream - 让你重命名。1 所以你的 Git 和他们的 Git 非常快速地传输所有新对象(可能 none),然后你的 Git 根据他们的 master 等等设置你的 upstream/master 等等。

git checkout master

这就是存储库奇怪状态的来源。您的 Git 会说:

Branch master set up to track remote branch master from upstream.
Already on 'master'

发生的事情是git checkoutmaster没有找到(因为你没有分支),所以创造了一个。首先,它查看了您所有的远程跟踪名称,在本例中为 upstream/*。它找到了一个匹配的:master vs upstream/master。所以它创建了你的 master,指向与你的 upstream/master 相同的提交。然后它还会将您的 master 设置为 upstream/master 作为其 upstream 设置。

完成所有操作后——创建你的 master——git checkout 试图将你放到你的 master 上,结果发现你只在你的 master 上并打印令人困惑的 "already on" 消息。不过,它在此过程中正确附加了您的 HEAD,检查了所有文件,即将它们复制到索引和工作树中。

您可能希望也可能不希望您的 master 以这种方式设置——您更有可能希望您的 master 开始指向与您的 origin/master 相同的提交,一旦你创建了一个 origin/master,并将 origin/master 设置为它的上游。有关什么是上游的更多信息——即,将一个分支设置为 track2 另一个分支意味着什么——参见,例如, to .

您在此处的最后一条命令是:

git merge upstream/master

你自己的 master 刚刚从你的 upstream/master 创建,所以没有什么可以合并:两个名字都指向同一个提交哈希编号.

您尚未从 origin 中获取任何内容。您现在可能应该这样做:

git fetch origin

一旦你这样做,你将拥有 origin/master 以及 upstream/master3 如果你希望,正如我怀疑的那样,拥有你自己的 master 跟踪 origin/master 而不是 upstream/master(并从那里开始),你应该:

  1. 确保没有什么要提交的(不应该给出上面的顺序,但在使用之前检查总是明智的git reset --hard);
  2. 运行 git reset --hard origin/master 使您的 master 指向与 origin/master 相同的提交;和
  3. 运行 git branch --set-upstream-to=origin/master master 更改上游设置。

现在你可以运行git merge upstream/master。如果在您自己的分叉发生后上游有新的提交,这将合并这些提交,如果需要则使用完全合并,或者如果可能的话使用快进非真正合并操作。

在任何情况下,您可能都想删除额外的存储库。


1Git 实现重命名的底层机制非常复杂,可能是出于历史原因,但在正常实践中它只是 "change their master to your <em>remote</em>/master" 和依此类推

2请注意 Git 在这里使用了更令人困惑的术语:如果分支名称 tracks a remote-tracking name(这是你的 Git 根据在另一个 Git 中找到的名称创建的本地名称,另一个 URL 是通过 remote) 然后我们称该分支(或分支名称)为 upstream。这与跟踪文件和未跟踪文件完全不同。哎呀!

3我在这里假设 https://github.com/abelbraaksma/visualfsharp 的 Git 存储库是您自己的,并且您使用 GitHub 的存储库创建了它"fork a repository" Web GUI 界面中的可点击按钮。当您这样做时,GitHub 在 GitHub 本身上做了一些复杂的 git clone,从您选择的任何源存储库在那里创建您的存储库。这意味着您的 GitHub 存储库具有与原始源存储库相同的所有分支。

(克隆 GitHub 不会重命名分支。它还具有特殊的 GitHub-only 功能设置以允许 GitHub 提供的拉取请求工具;这不是 Git 的一部分。GitHub 人员还安排在幕后共享底层磁盘对象,并使用各种其他技巧使它比它更快做得很天真。所以它是一个常规的克隆 原则上 ,但他们已经对其进行了调整,使其通过 Web 界面更有用。这就是他们让你使用 GitHub 的方式而不是自己全部完成。)

我做的事情和你做的很相似,我是这样做的:

  1. 获取叉子的 url。
  2. 切换到终端。 cd 到我们要克隆的目录。
  3. git clone fork-url-here 将克隆我们的分支并将其设置为 remote/origin.
  4. cd fork-name/ 切换到克隆的目录。
  5. git remote add upstream upstream-url-here 将上游设置为 remote/upstream.
  6. git fetch upstream 从上游获取 所有分支
  7. git checkout master 因为我们已经在 origin/master 上,所以我们收到一条消息通知我们同样的情况。所以,一切都很好,这 不是 表示有问题。
  8. git branch -a 列出所有本地 + remote/origin/* + remote/upstream/* 分支。其中之一是 upstream/master(最初我使用 git branch,它只显示本地分支机构,这让我有点困惑,因为我在列表中看不到 upstream/master)。
  9. git merge upstream/master 这会将 upstream/master 分支合并到您当前的分支,即 origin/master,从而同步到上游。

出现您遇到的问题是因为就在您将上游添加为远程之前,您从中获取(代码块中的第四行)。这将阻止您从上游获取所有分支。其他的我觉得还好。

P.S.: 我可以看出这是一个老问题,但我只是想我可能会帮助 git 初学者(像我一样),他们可能会匆忙而无法阅读torek 给出了非常好的和信息丰富的答案。



Edit/Extension 1:另一件事是强制fork的(origin)master与原始repo的(upstream)master处于同一级别。

!!!注意:这将丢弃对您的 origin master 所做的任何和所有提交!!!

如果您完全确定要执行此操作,请将上述步骤中的步骤 9 替换为以下内容:

  • git reset --hard upstream/master 将用 upstream/master.
  • 的内容替换 origin/master 的内容(当然是在本地)
  • git push origin master --force 将强制推送您所做的更改到远程源。

我建议进行此更改,因为我最近不得不自己做这件事,并且发现这可以帮助某人(前提是他们知道自己在做什么)。但是,由于它也有可能破坏有价值的工作,所以我一再强调其中的风险。