现有的答案没有确切地涵盖它是如何工作的,这有点复杂。在内部,Git 将这个东西称为 DWIM 模式。
长篇:背景
让我们从这个开始:你的分支名称是你的。其他一些 Git 可能有一个名为 newbranch 或 branch2 或其他名称的分支,但如果你没有那个分支名称,你就没有那个分支名称。嗯,还没有。
还请记住,每个提交都有一个唯一的哈希 ID。要查看当前提交的哈希 ID,请运行:
git rev-parse HEAD
特殊名称HEAD 始终命名当前提交(通常也命名当前分支名称,但我们稍后再讨论)。 git rev-parse 命令将为您提供丑陋的大哈希 ID — 对人类来说不是那么有用,但对 Git 至关重要,因为该哈希 ID 是 Git 实际查找提交的方式。
同时,每个分支名称仅包含一 (1) 个提交哈希 ID。如果您有一个分支名称master,您可以通过运行git rev-parse master 找到该名称所代表的哈希ID。和以前一样,git rev-parse 把名字变成了丑陋的大哈希 ID。
现在,这意味着要创建一个 新 分支名称,您告诉 Git:创建一个新分支名称。这是要存储在其中的哈希 ID:_______。您告诉 Git 的方式是使用各种命令中的任何一种:
-
git branch <em>newname</em>:这告诉 Git 使用通过将 HEAD 解析为哈希 ID 找到的哈希 ID 创建新名称。
-
git branch <em>newname</em> <em>hash-id</em>:这告诉 Git 使用您输入的哈希 ID 创建新名称。哈希 ID 很难输入,因此您可能会使用鼠标剪切和粘贴一个。但你不必这样做,因为:
-
git branch <em>newname</em> <em>any-other-name-that-works-with-rev-parse</em>:这让 Git 在 last 名称上运行 git rev-parse,以查找哈希 ID,然后创建分支以使其包含您提供的哈希 ID。
-
git checkout -b <em>name</em> 和 git checkout -b <em>name</em> <em>start-point</em>:这与使用 git branch 后运行 git checkout 非常相似。
但还有另一种方法可以创建一个新分支名称,那就是运行git checkout <em>name-that-does-not-yet-exist</em>。
通常,如果您执行git checkout supercalifragialistic 之类的操作,您只会收到错误消息:Git 尝试将该名称转换为哈希 ID(使用 git rev-parse 的内部等效项),这完全失败,整个事情就停止了有错误。但是git checkout 内置了一个特殊的技巧。
现在,除了 branch 名称之外,Git 还支持我称之为 remote-tracking 名称 的东西(Git 称它们为 remote-tracking 分支名称 但是branch 这个词在这里有点误导,所以我认为最好把它去掉)。这些非常简单,真的:当你告诉它时,你的 Git 会连接到其他 Git。你可能称它为另一个 Git origin,因为这是标准名称。您偶尔会运行git fetch origin 或git pull origin master 或类似名称:origin 名称是您的 Git 如何找到用于调用另一个 Git 的 URL。
位于origin 的其他 Git 具有分支名称。 你的 Git 记住他们的分支名称,但由于你的名字是你的,你的 Git 会以备用名称记住它们。这些是远程跟踪名称。您的 Git 将他们的 master 重命名为您的 origin/master,将他们的 xyz 重命名为 origin/xyz,等等。
在您的问题中,您谈到了upstream/newbranch。名称 upstream 是 second Git 存储库的标准名称,您可以使用 git remote add 添加它。您与之交谈的每个“其他 Git”都有一个名称,远程跟踪名称具有 remote 名称,后跟另一个 Git 的 branch 名称,中间用斜杠他们。所以你可能会同时得到origin/newbranch 和 upstream/newbranch,这很重要。
DWIM 模式
当你运行 git checkout 时,会因为你没有分支而出错,git checkout 会在实际失败之前尝试一个新技巧。
您的 Git 将扫描所有您的远程跟踪名称。例如,您可能有origin/master、origin/xyz、upstream/xyz 和upstream/newbranch。
如果你已经有一个master 并运行git checkout master,那么你有一个master,所以git checkout 将使用它。但是如果你运行git checkout newbranch 并且没有新分支,Git 将扫描以上所有内容。只有upstream/newbranch“看起来不错”,所以Git会对自己说:啊哈,如果我现在自动创建 newbranch from upstream/newbranch,我可以切换到 它! 这就是它的作用:创建 this 作为一个新分支,然后切换到它。假设是当你说切换到现有分支newbranch时,你必须意思 从newbranch创建新分支upstream/newbranch . Git 照你说的做,而不是照你说的做。
请注意,如果您运行 git checkout xyz,Git 会遇到一个新问题:现在有 两个 候选对象可以创建 xyz。它可以从origin/xyz 或upstream/xyz 创建。默认情况下,DWIM 模式不会创建任何内容,您会看到错误。
(Git 2.21 及更高版本有 --no-guess 完全禁用 DWIM。如果您不希望 Git 猜测所有可能的远程跟踪名称,这主要用于 bash 完成脚本。)
其他几件重要的事情要知道
当你创建一个新的分支名称时,你可以让 Git 设置它的 upstream:
- 每个分支名称要么有一个上游,要么没有上游。
- 例如,
master 的上游通常是 origin/master。
- 上游设置为您提供来自
git status 的更多信息,并允许您运行git fetch、git merge、git rebase 和git pull,而无需指定更多信息。所以它是为了方便。如果您觉得方便,请使用它;如果没有,请不要。
要显式设置分支的上游,请使用git branch --set-upstream-to;要删除上游,请使用git branch --unset-upstream。当git checkout 使用 DWIM 模式创建分支时,它通常会将该分支的上游设置为它在创建分支时使用的远程跟踪名称。您可以使用git config 进行调整;见its documentation。
当使用git branch或git checkout -b时,你可以明确告诉Git是否设置新创建分支的上游,使用-t或--track选项(这些是相同的选项:一个只是更长的拼写)。请注意,在同时拥有origin/xyz 和 upstream/xyz 的棘手情况下,使用:
git checkout -t origin/xyz
是一种简捷的跑步方式:
git checkout -b xyz --track origin/xyz
也就是说,它:
- 指定在本地创建
xyz时用于获取哈希ID的名称;
- 指定本地名称为
xyz,因为使用的远程跟踪分支为origin/xyz;和
- 指定新的本地
xyz 应设置为origin/xyz 作为其上游。
使用git checkout -t upstream/xyz 的工作方式类似,只是您的新xyz 使用通过解析upstream/xyz 找到的提交ID,并且您的新xyz 将upstream/xyz 作为其上游。