克隆后恢复分支

  • 本文关键字:分支 恢复 git github
  • 更新时间 :
  • 英文 :


我从 github 克隆了一个存储库,创建了一个新分支,推送回 github,然后在本地删除了该存储库,最后再次克隆。现在我想将我的分支恢复到我在本地删除存储库之前的状态,在此过程中,我还想更多地了解我的情况以及它如何应用于HEAD、分离的头状态、远程和refs/heads的概念。

我知道感兴趣的两个分支(mbigrasmcb-fix-type(仍然存在,但它们已被重命名:

atom-pane-manager git:master ❯ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
  remotes/origin/mbigras
  remotes/origin/mcb-fix-typo

所以最初我认为我可以将remotes/origin/mbigras分支重命名为 mbigras 并完成,但我收到以下错误:

atom-pane-manager git:master ❯ git branch -a | grep mbigras | pbcopy
atom-pane-manager git:master ❯ git branch -m remotes/origin/mbigras mbigras
error: refname refs/heads/remotes/origin/mbigras not found
fatal: Branch rename failed

有几件事我很好奇:

  1. 为什么我无法重命名分支?
  2. 为什么错误消息中的分支名称中包含refs/heads/

还看着分支我也想知道:

  1. remotes/origin/HEADremotes/origin/master分支是什么意思?

我在.git四处闲逛,试图弄清局势😝 😒的正面或反面

atom-pane-manager git:master ❯ tree .git/refs
.git/refs
├── heads
│   └── master
├── remotes
│   └── origin
│       └── HEAD
└── tags
4 directories, 2 files

我期待至少在remotes中找到remotes/origin/mbigrasremotes/origin/mcb-fix-typo,但他们没有,这也令人困惑。

我还阅读了另一篇关于"分离的头部状态"的帖子,我不确定这是否适用于我的情况,但该帖子确实回顾了可能有助于阐明git rev-parse命令:

atom-pane-manager git:master ❯ git rev-parse master
73b3c240ff9b35d760d8e4302308d3c0e725fc76
atom-pane-manager git:master ❯ git rev-parse HEAD
73b3c240ff9b35d760d8e4302308d3c0e725fc76
atom-pane-manager git:master ❯ git rev-parse remotes/origin/HEAD
73b3c240ff9b35d760d8e4302308d3c0e725fc76
atom-pane-manager git:master ❯ git rev-parse remotes/origin/master
73b3c240ff9b35d760d8e4302308d3c0e725fc76
atom-pane-manager git:master ❯ git rev-parse remotes/origin/mbigras
9a25755b0175ba2a3548b439ffdb12b8010e74a6
atom-pane-manager git:master ❯ git rev-parse remotes/origin/mcb-fix-typo
6d235be40c7223fda7a8a6dbdf8b2fd7336e3d00

从这些结果来看,HEADmasterremotes/origin/HEADremotes/origin/master都指向同一个地方,这是有道理的,因为它们指向最后一个提交:

atom-pane-manager git:master ❯ git log --pretty=oneline -n 1 | pbcopy
# => 73b3c240ff9b35d760d8e4302308d3c0e725fc76 Prepare 1.0.1 release

而且这两个神秘分支确实存在...地方。

所以我对到底发生了什么感到困惑,特别是上面提到的三个问题。我特别喜欢一个答案,包括正在发生的事情的视觉表示。谢谢 😄

为什么我无法重命名分支?

正如我们将在下面看到的,您不应该重命名远程分支,git-branch也不会允许您重命名。但事实并非如此。正如 git 所说,它找不到它。

atom-pane-manager git:master ❯ git branch -m remotes/origin/mbigras mbigras
error: refname refs/heads/remotes/origin/mbigras not found
fatal: Branch rename failed
                                                 ^^^^^^^^^

git branch -aremotes/origin/mbigras但分支真的叫origin/mbigras。正如我们稍后将看到的,这甚至是一个缩写。

git branch -a的格式有点令人困惑。 git branchgit branch -r都显示分支名称。 git branch -a显示本地分支的名称,但为了区分远程,它会在它们前面remotes/。例如。。。

$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
$ git branch -r
  origin/HEAD -> origin/master
  origin/master
远程/源/

头和远程/源/主分支是什么意思?

HEAD是当前签出的提交。 origin/HEAD是远程设备上当前签出的提交...或者这是 Git 最后一次看。Git 仅在您获取、推送、拉取和克隆时与远程通信。其余时间它使用存储在.git/remotes/<remote name>/<branch>中的"远程跟踪分支"。这些记录了上次查看远程分支时的状态。这是 Git 如此之快的部分原因,它几乎从不与网络通信。

箭头让您知道 origin/HEAD 与 origin/master 位于同一提交中。这意味着远程设备上当前签出的分支是主分支。它通常指示默认分支是什么。使用不同命名方案的项目可能使用 trunkdevelopment 而不是 Git 约定 master

为什么错误消息中的分支名称中包含 refs/heads/?

本地分支存储在.git/refs/heads/中,远程存储在.git/refs/remotes/中。它们只是一个带有提交 ID 的文件,git 称之为"符号引用"。

$ cat .git/refs/heads/master 
177e49fff4348251ec30e3641d116accc0734c9d
$ cat .git/refs/remotes/origin/master 
177e49fff4348251ec30e3641d116accc0734c9d

这告诉我主和原点/主指向同一个提交。

master实际上是分支的缩写名称。全名是refs/heads/master,这只是.git/中的文件名。Git 将互换使用它们。 master是模棱两可的,它可能是一个标签,它可能是一个分支,也可能是一堆其他的东西。因此,在错误消息中,Git 将使用全名来明确。

当你告诉 Git 你想使用master它会执行一个搜索,就像你的 shell 在你尝试运行命令时PATH查找一样。

1. If $GIT_DIR/<refname> exists (HEAD, FETCH_HEAD, ORIG_HEAD, ...);
2. otherwise, refs/<refname> if it exists; (???)
3. otherwise, refs/tags/<refname> if it exists; (it's a tag)
4. otherwise, refs/heads/<refname> if it exists; (it's a local branch)
5. otherwise, refs/remotes/<refname> if it exists; (it's a remote branch)
6. otherwise, refs/remotes/<refname>/HEAD if it exists. (it's a remote name)

gitrevisions 文档以血腥的细节解释了这一切。


知道了这一点,我们可以找出无法重命名remotes/origin/mbigras的另一个原因:git branch没有进行完整搜索,而是假设这是一个本地分支。

error: refname refs/heads/remotes/origin/mbigras not found
               ^^^^^^^^^^^

远程分支机构在.git/refs/remotes/ 中。 git branch -m永远不会重命名遥控器,因为它们位于不同的目录中。这很好!远程跟踪分支应该与远程存储库保持同步,如果您开始重命名它们,事情会变得奇怪。

您可以进入.git/refs/remotes并开始重命名文件,这将重命名遥控器。不要这样做,因为您实际上并不想重命名远程,而是想从远程创建新的本地分支

git branch origin/mbigras mbigras

就是这样(你可能想看看mbigras(。这大致相当于:

cp .git/refs/remotes/origin/mbigras .git/refs/heads/mbigras

加上对.git/config的一些编辑,以记住mbigras正在跟踪origin/mbigras,这样git pull,如果您不拼出git pull origin mbigrasgit push知道要与哪个遥控器和分支交谈。那是origin mbigras,不是origin/mbigras,因为它是git pull <remote name> <remote branch name>

(然后是打包引用的例外,我们将在下面看到。

现在,您可以在本地分支mbigras上工作,并通过其跟踪分支origin/mbigras从远程版本的mbigras进行推送和拉取,该分支将为您保持同步,但仅在运行推送、拉取和提取时。

我希望至少在遥控器中找到遥控器/起源/mbigras 和遥控器/起源/mcb-fix-typo,但它们不是,这也令人困惑。

这是因为优化。我上面描述的是 Git 在解压缩时的工作方式。Git 将使用包文件和打包引用进行优化,我没有资格解释,但与使克隆更有效有关。

长话短说,如果裁判不在.git/refs/它将在.git/packed-refs中。

$ cat .git/packed-refs 
# pack-refs with: peeled fully-peeled 
5f73dc320dbf320b6a6b497048dade6626d0c74b refs/remotes/origin/80_method_methods
260c0405871b7f92ed301041fef3f6c7ed90a5a5 refs/remotes/origin/appveyor
df99a98b7ba26bf5c4eb2a7fe3ec35bfe8090652 refs/remotes/origin/gh-pages
...

同样,有时对象不会在.git/objects/中,而是在.git/pack/中。包文件的格式远远超出了我的理解。

有了 Git,总会有新的东西!我已经使用它八年了,这是我第一次遇到打包的参考。

这可能看起来有点压倒性,但展示版本控制系统的胆量意味着您可以完全理解它。

最新更新