【问题标题】:Restore branches after cloning克隆后恢复分支
【发布时间】:2016-07-03 02:30:01
【问题描述】:

我从github克隆了一个repo,新建了一个分支,push到github,然后在本地删除了repo,最后又克隆了一次。现在我想将我的分支恢复到我在本地删除 repo 之前的状态,同时我想了解更多关于我的情况发生了什么以及它如何应用于@987654322 的概念@、分离的头部状态、遥控器和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,但他们没有,这也令人困惑。

我还阅读了another post,其中谈到了“分离的头部状态”,我不确定这是否适用于我的情况,但该帖子确实涵盖了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 github


    【解决方案1】:

    为什么我不能重命名分支?

    正如我们将在下面看到的,您不应该重命名远程分支,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 -a 说是remotes/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
    

    remote/origin/HEAD 和 remotes/origin/master 分支是什么意思?

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

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

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

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

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

    这告诉我 master 和 origin/master 指向同一个提交。

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

    当您告诉 Git 您想使用 master 时,它会执行类似于 PATH 查找的搜索,当您尝试运行命令时,您的 shell 会执行此操作。

    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 docs 详细解释了这一切。


    知道了这一点,我们可以找出无法重命名 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 pullgit push 知道如果您不拼写git pull origin mbigras,可以与哪个远程和分支通信。那是origin mbigras,不是origin/mbigras,因为它是git pull &lt;remote name&gt; &lt;remote branch name&gt;

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

    现在您可以在本地分支 mbigras 上工作,并通过它的跟踪分支 origin/mbigras 从远程版本的 mbigras 推送和拉取,这将与您保持同步,但仅当您运行推送时,拉, 并获取。

    我希望至少能在遥控器中找到 remotes/origin/mbigras 和 remotes/origin/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 总是有新的东西!我已经使用它八年了,这是我第一次遇到打包的 refs。

    这似乎有点让人不知所措,但展示你的版本控制系统的胆量意味着你可以完全理解它。

    【讨论】:

    • 信息量很大!只有一件事,当你提到我需要从远程复制我的分支时(或者我应该说从原点?或者说远程或从原点意味着同样的事情?)而不是重命名它,我认为命令是:@ 987654373@ 而不是git branch origin/mbigras mbigras。我对你的理解正确吗?
    • @mbigras origin 是特定远程的名称,是您克隆的远程的默认名称,就像 master 是默认分支一样。而且你不会在 git 中复制分支,这是 svn 的事情。 git 分支只是一个指向提交的名称。新分支只是指向与原始分支相同的提交的新名称。从那里他们可以像树上的树枝一样发散。所以你会说“从 the 远程创建一个分支”(大多数时候只有一个远程)或“从原点创建一个分支”。 git checkout -b bar foogit branch foo bar 加上 git checkout bar 的简写。
    猜你喜欢
    • 2022-01-20
    • 2011-02-10
    • 2011-07-30
    • 2021-06-26
    • 2011-06-16
    • 2016-09-02
    • 2017-05-05
    • 2019-01-19
    • 2020-07-22
    相关资源
    最近更新 更多