首页 > 解决方案 > Git fetch 未按预期工作“:gone]”未添加到分支描述中

问题描述

如果我们执行git fetch -p,或者git fetch --prune如果在远程删除,它将修剪分支。

执行此命令后,如果我们这样做git branch -vv,假设显示: gone]从远程删除的本地分支。

就我而言,有时它会按预期工作,但并非总是如此。有时它不会添加: gone]到远程删除的分支中。

我的目标是在远程删除本地分支时删除分支。

我想知道为什么会这样?

标签: gitgithub

解决方案


仅仅因为其他人删除了他的名为 X 的分支而删除名为 X 的分支并不一定是明智的。如果您正在开发一个新功能并且您调用了它feat,并且 Bob 放弃了他正在调用的新功能feat并且 Bob 删除了 Bob 的feat,这并不意味着您应该删除您的feat!

除此之外,让我们看看分支的一个特殊功能。此特殊功能适用于(本地)分支,不适用于origin/masterGit 的哪些部分调用远程跟踪分支以及在git branch -r输出下列出的部分。1 它们实际上有几个特殊功能——例如,您可以使用“进入”分支git checkout,如 aftergit checkout master中,git status命令将显示on branch master。但这不是我在这里要说的特殊功能。

我们在这里关心的特殊功能是它们可以具有上游设置。也就是说,你master的上游可以有一个,而且只有一个develop,如果你有一个develop,你的上游可以有一个feat,如果你有一个,你的上游可以有一个,依此类推。您在这里的选择是有上游,或者没有上游。对于每个分支,您一次选择一个分支。

取消分支的上游设置,请使用. 现在命名的分支没有上游。如果您省略该部分,则它适用于当前分支,即您所附加的分支。这不是取消设置的唯一方法,但通常是最好的方法。git branch --unset-upstream namenamenameHEAD

设置或更改分支的上游设置,请使用. 现在命名的分支有一个上游;上游是你给出的论点。与 一样,省略表示当前分支(您不能省略)。同样,这不是设置它的唯一方法,但通常它是最好的方法,因为该命令会在让您设置它之前验证参数是否合理。git branch --set-upstream-to=upstream namenameupstream--unset-upstreamnameupstreamgit branchupstream

有时分支机构从一开始就有上游设置,有时则没有。我们稍后会看看何时以及为什么。


1我已经到了尝试避免远程跟踪事物的分支一词的地步。我现在只调用远程跟踪名称,因为它们与本地分支名称非常不同。请注意git fetch -p, 或git remote prune origin或 设置fetch.prunetrue仅影响这些远程跟踪名称


究竟什么是上游?

上游只是另一个分支名称——在这里,分支名称是本地分支,如masteror develop远程跟踪名称,如origin/master. 因此,如果您愿意,可以将上游develop设置为 to或 to或 to 。该操作将让您设置任何存在的东西,并且在 Git 看来,这在这里是有意义的。masterorigin/masterorigin/developgit branch --set-upstream-to

Git在这里有一个奇怪的缺陷。这部分是历史性的。回到古代,Git 没有遥控器——没有——所以originGit 也没有远程跟踪名称,如果没有origin,就不可能有origin/master。但部分原因是一个简单的事实,您可能有也可能没有甚至origin/xyz可能origin/xyz在您在分支上努力工作时离开xyz(因为 Bob认为您已经完成并xyz在原点上删除了)。

简单地说,缺陷是,您设置的上游(在它存在时返回,git branch --set-upstream-to因此让您设置它)现在可能已经消失了。当您: gonegit branch -vv输出中看到时就是这种情况。在过去的某个时候,您告诉您的Git,您的分支xyz应该origin/xyz作为其上游,并且origin/xyz当时存在。因此git branch验证一切都很好并进行了设置。但是现在名称已经消失了,因此该设置不再有效并git branch -vv记下这一点。

实际上,分支的上游设置由两部分组成,您可以使用 配置其中一个或两个部分git config,甚至可以将您的配置带入您选择的编辑器 ( git config --edit) 并直接对其进行处理:

[branch "master"]
        remote = origin
        merge = refs/heads/master

在这个特定的存储库中,这个设置表示上游masterorigin/master. 你可能认为你可以只取线中的origin部分和线中的部分,但这是一种陷阱:有一个秘密的复杂映射。要查找某个分支的上游设置,请使用后缀:remote =mastermerge =git rev-parse@{upstream}

$ git rev-parse --symbolic-full-name master@{upstream}
refs/remotes/origin/master

这适用于上游也设置为另一个本地分支的分支。

不管怎样,因为它这样的两个部分,你可以在正常git branch --set-upstream-to机制之外乱用它,你可以打破上游设置。如果你确实破坏了它——如果你将它设置为无意义的东西——<code>git branch -vv 会将上游列为“已消失”,使用与正常时相同的技术,日常 Git 操作会破坏它,因为它确实消失了。Git 不关心它为什么坏了,只关心它现在坏了还是没坏:如果它现在坏了,Git 说“gone”,否则假装它没有设置。

请注意,这也意味着如果 Bob不小心xyz从中删除origin并且 Bob 修复了他的错误并xyz 放回了,您的 Git 可以从“消失”变为“没有消失,一切都很好” git fetch。这也是你不应该让你的 Git 删除你的本地分支的另一个原因,因为有人在其他地方弄乱了其他Git。

上游有什么好处?

上游设置仅提供一些次要功能。有时,有些人真的很喜欢这些功能,但它们从来都不是必需的。所以从某种意义上说,上游一点也不好。只要你避免使用它们,就不必使用它们git pull

但是,这些功能是:

  • 更容易推送:如果你想在没有额外参数的情况下运行,标准push.default设置要求你的分支有一个上游。git push

  • 更容易的合并和变基:git mergeandgit rebase命令可以使用它。包装器git pull,你不必使用,我建议避免使用它,需要上游。包装器首先git fetch为您运行,然后立即(在您有机会根据git fetch所做的决定它是否是一个好主意之前)运行git mergegit rebase为您运行。

  • 更有用git status:第一行git status告诉你是否处于分离 HEAD 模式,如果不是,你在哪个分支上。有一个上游意味着有第二条线,就在你所在的分支的那条线之后,当你在一个分支上时。第二行将分支与其上游进行比较,并告诉您它是否与其上游“最新”。

这就是上游的好处:它使一些事情变得更容易和/或更有用。但是您一次仅限于一个上游。有时,您可能希望获得与git status任意一对名称相同的第二行输出。有一种方法可以做到这一点git rev-list --count,但我们将把它留给其他 答案

为什么有些分支已经有上游集,而有些则没有?

在 Git中有很多方法可以创建分支。每一种方法都与其他替代方法略有不同——这就是这些方法存在的原因,但也给你带来了这种混乱。

创建新分支的两个主要命令是git branchgit checkout。当您用于git branch创建新分支时,始终可以完全控制:

git branch --track newbranch origin/upstreamname

或者:

git branch --no-track newbranch origin/upstreamname

尽管 upstream 被称为upstream,但控制它的参数被拼写--track——另一个历史事故或错误,真的。

如果您不使用--track--no-trackgit branch请使用您在配置时选择的默认值branch.autoSetupMerge。请参阅下文了解这意味着什么。

当你使用 时git checkout,你可以使用--trackor --no-track,但如果你不使用任何一个选项,它就会变得更加复杂。branch.autoSetupMerge设置仍然很重要,但是......好吧:

  • git checkout -b newbranch创建newbranch,但从创建它HEAD,所以它不设置上游。也就是说,新名称newbranch现在标识了HEAD在此之前标识的相同提交。

  • git checkout -b newbranch origin/name创建newbranch并使用. 也就是说,新名称现在标识与. 这符合您的配置。origin/namenewbranchorigin/namebranch.autoSetupMerge

    git checkout -b newbranch existing-branch创建newbranch,existing-branch用作其起点。也就是说,新名称和现有名称现在标识相同的提交。这也符合您的branch.autoSetupMerge配置,但请参阅下文了解我将其分开的原因。

  • git checkout name 要么使用现有的name要么创建一个名为nameusing的新分支。如果它创建一个分支,它会服从.origin/namebranch.autoSetupMerge

    请注意,这将永远不会使用本地分支名称创建新分支,即,它不能创建具有另一个本地分支作为其上游的本地分支。如果是这样的话,两个 * name* 将是相同的,所以定义的分支已经存在,所以它只是检查本地分支而不创建任何东西。

  • git checkout --track origin/name创建一个名为的新分支name设置其上游。

  • git checkout --no-track origin/name创建一个名为的新分支name并确保它没有上游。

所以,如您所见,这很复杂!创建分支的确切方法部分确定它是否具有上游,其余的确定基于您的branch.autoSetupMerge配置。此设置具有三个可能的值:

  • true:当起点是远程跟踪名称时设置上游。
  • false: 不要设置上游。
  • always:当起点是远程跟踪名称(本地)分支名称时设置上游。

如果您根本没有设置,默认设置branch.autoSetupMerge是假装您将其设置为true. 因此,默认情况下,所有这些分支创建选项的行为就像您所说的--track ,如果您给它们一个origin/*名称或任何其他远程跟踪名称作为它们的起点。即使起点只是隐含的,也是如此,如:

git checkout develop

您没有本地但有远程跟踪的情况:这会您的远程跟踪创建您的新本地,如果是- 包括如果您根本没有设置它 - 现在您的“轨道” - 已经作为它的上游——你的.developorigin/developdevelop origin/developbranch.autoSetupMergetruedeveloporigin/develop

概括

您的一些分支机构设置了上游。哪些分支有上游,这些上游是什么,取决于您,由您的配置和/或命令行选项和/或附加git branch --set-upstream-togit branch --unset-upstream命令控制。

那些确实设置了上游的分支将获得一些有用的功能。如果通过任何过程设置的上游“消失”,git branch -vv列出您当地的分支机构说它们的上游“消失”。其他 Git 命令只会假装您取消设置它们的上游。如果上游再次回来,上游将恢复其用途。

因为其上游已消失而删除本地分支是错误的。删除它,因为已经完成它很好。如果你已经完成了它们,你不需要一个名为的本地分支develop,甚至不需要一个名为的分支,即使它们仍然作为 upstreamnames存在。你可以只使用来引用你的 Git 的记忆——你根本不需要拥有自己的记忆。masterorigin/developorigin/masterorigin/mastermasteroriginmaster

有时,由于某些潜在原因,您完成了一个分支,这也意味着上游将要消失或已经消失。在这种情况下,删除你的本地分支是好的——你删除它是因为你已经完成了它。它将被删除origin或已经被删除的事实在origin这里无关紧要!


推荐阅读