.git:拉取和应用已删除分支的提交

9

我有一些旧的更改,已经提交并推送到远程(bitbucket)仓库。这些更改没有合并到任何其他分支。分支本身已从本地和远程仓库中删除。

但是,正如我在bitbucket网站上看到的那样,在删除的分支中包含的提交仍然存在(在仓库中)。我进行了大量谷歌搜索,但没有找到一种方法可以从远程恢复已删除分支的提交。我唯一能做的就是在bitbucket网站上查看它们,并获取提交的sha。

我看到了一些示例,比如:

git checkout <sha>

或者

git checkout -b <branch-name> <sha>

但总是出现以下错误。
fatal: reference is not a tree: <sha>

那么,我是否可以从远程获取这些提交,创建分支并将其合并到发布分支中?

更新:

更具体地说,我已经创建了一个仓库、新的分支,在这个新分支上进行了提交并删除了该分支:
仓库 https://github.com/yurybond/stackowerflow-rocks 链接到(独立)提交的已删除分支https://github.com/yurybond/stackowerflow-rocks/commit/a1c1540abd453773b3ce6445d01e51ad336bbe84 问题仍然是相同的:如何检索属于已删除分支的提交(a1c1540abd453773b3ce6445d01e51ad336bbe84)?

1
请注意,您始终可以将提交下载为补丁并自动创建新的提交:wget https://github.com/yurybond/stackowerflow-rocks/commit/a1c1540abd453773b3ce6445d01e51ad336bbe84.patch && git am a1c1540abd453773b3ce6445d01e51ad336bbe84.patch - Arkadiusz Drabczyk
@ArkadiuszDrabczyk,您的答案是GitHub最好的。是否有办法对其他类型的存储库(如Bitbucket、GitLab等)采用类似的方法? - Yury Bondarau
@ArkadiuszDrabczyk,我猜你的方法是解决我删除分支提交问题的唯一可行方案。请在4小时内(赏金到期前)创建一个答案,并提供逐步指南,说明如何将提交下载到补丁并应用此补丁。 - Yury Bondarau
我已经添加了我的答案,但我想知道fatal: reference is not a tree消息来自哪里。你使用submodules吗? - Arkadiusz Drabczyk
我没有子模块。我猜这个问题可能会发生,如果你本地的仓库副本从未删除过该分支。(例如,分支被创建、推送到远程,然后被其他人删除) - Yury Bondarau
7个回答

8
您可以手动以格式下载缺失的提交,并使用手动应用它们。例如:

  • github (repository you linked to):

      $ wget https://github.com/yurybond/stackowerflow-rocks/commit/a1c1540abd453773b3ce6445d01e51ad336bbe84.patch && git am a1c1540abd453773b3ce6445d01e51ad336bbe84.patch
    
  • gitlab:

      $ wget https://git.weboob.org/weboob/devel/commit/bba7e1b8ffb0743b57f202cf9cdb43fda209fa43.patch && git am bba7e1b8ffb0743b57f202cf9cdb43fda209fa43.patch
    
  • bitbucket:

      $ wget https://bitbucket.org/Kasreyn/linux-3-9-rc3-moxart/commits/434e8f69db2c3effdc8741139adb722a68dfcccd/raw -O 434e8f69db2c3effdc8741139adb722a68dfcccd.patch && git am 434e8f69db2c3effdc8741139adb722a68dfcccd.patch
    
请注意,git am不仅会应用文本补丁,还会在当前分支上重新创建一个完整的提交。
如果您从远程仓库中删除了一个分支,则没有办法从远程仓库中恢复它。然而,通常情况下,由于 git reflog 的存在,您仍然可以在本地恢复已删除的分支。请参考以下示例。
首先,创建一个新的非裸库:
$ git init
Initialized empty Git repository in /tmp/reflog-test/.git/

master 分支上添加 file 并创建一个新的提交:

$ touch file
$ git add .
$ git commit -m 'Initial commit'
[master (root-commit) 81fc76d] Initial commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 file

切换到一个名为new-branch的新分支:

$ git checkout -b new-branch
Switched to a new branch 'new-branch'

修改文件并提交更改:

$ echo new-branch >> file
$ git commit -am 'commit on new-branch'
[new-branch 9c457c6] commit on new-branch
 1 file changed, 1 insertion(+)

切换回 master 分支:

$ git checkout -
Switched to branch 'master'
$ git branch
* master
  new-branch

删除 new-branch
$ git branch -D new-branch
Deleted branch new-branch (was 9c457c6).

已经消失:

$ git branch
* master
$ git log new-branch
fatal: ambiguous argument 'new-branch': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

然而,您仍然应该能够引用提交记录:

$ git show --stat 9c457c6
commit 9c457c69de8a54376a2614ca8cfc0f515c64676c
Author: Arkadiusz Drabczyk <adrabczyk@bigcorp.com>
Date:   Tue Apr 3 10:43:49 2018 +0200

commit on new-branch

 file | 1 +
 1 file changed, 1 insertion(+)

即使在任何分支中都找不到,下面的命令也不会返回任何内容:
$ git branch --contains  9c457c6

Reflog 显示了在仓库中执行的所有操作:

$ git reflog
dbc721a HEAD@{0}: checkout: moving from new-branch to master
9c457c6 HEAD@{1}: commit: commit on new-branch
dbc721a HEAD@{2}: checkout: moving from master to new-branch
dbc721a HEAD@{3}: commit (initial): Initial commit

正如您所看到的,它还有我们在new-branch上进行的9c457c6提交。 只有当reflog过期并运行垃圾收集器时,9c457c6才会变得不可达:

$ git reflog expire --expire=all  --all
$ git gc --prune=now
Counting objects: 3, done.
Writing objects: 100% (3/3), done.
Total 3 (delta 0), reused 0 (delta 0)
$ git show 9c457c6
fatal: ambiguous argument '9c457c6': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

默认情况下,reflog条目在90天后过期,并且在某些命令之后自动运行git gc,因此如果您最近删除了分支,则应该能够恢复它。


2
如果此提交确实存在于Bitbucket存储库中,那么您可以使用Bitbucket Web界面将其合并到另一个分支中。
  1. 前往“分支”--> 在Bitbucket中创建新分支。将其命名为任何您想要的名称。
  2. 创建分支后,单击比较(右上角的三个点菜单)。
  3. 在比较屏幕中,选择更改源,并粘贴您的提交引用。如果出现结果,则运气好。选择它。
  4. 选择更改目标,并将其更改为您的新分支。
  5. 您应该能够浏览差异,查看您想要带入的所有更改。
  6. 如果您满意,请选择合并。
现在问题来了,如果在第3步中粘贴提交引用失败,则您就没有办法了。这意味着提交在远程或本地都不存在。
如果您讨厌导航并想要直接链接到比较(虽然会与主分支进行比较),请使用以下代码:https://bitbucket.org/<user>/<repository>/branches/compare/<commit-reference>..master

这是一个不错的解决方法,但并非总是适用的。例如,在新分支和所选提交之间存在冲突的情况下,您将无法合并。 - Yury Bondarau

2

git fetch origin <sha> 将从服务器获取提交,但这可能取决于您是否使用GitHub、BitBucket或其他托管提供商。


1
由于您已经在本地删除了分支,因此无法像其他人建议的那样使用sha-1来还原它。
此外,reflog不适用于远程端(而且也从远程端中删除)。
这留下了两个选项之一。要么向Bitbucket支持写信以便他们恢复所需内容。
要么尝试Michael在这里提出的建议:从Bitbucket(git)的远程恢复已删除的分支 所提出的解决方案似乎可以解决您的问题。请告诉我它的工作原理如何。

我已经检查了您提供的链接,它适用于Bitbucket。对于Github来说,不幸的是它不起作用。但这仍然是一个很好的Bitbucket技巧 - 谢谢。 - Yury Bondarau

1
我在Stack Overflow上没有看到有关检查已删除提交的任何内容。但是,可以通过硬重置来恢复已删除的提交:
git checkout -b some_new_branch
git reset --hard <SHA-1 of deleted commit>

这将重置分支some_new_branch到已删除的提交,也许这就足够让您继续进行。
我假设您在Bitbucket中看到的已删除提交来自reflog。如果是这样,那么您还可以通过以下方式查看已删除的提交:
git reflog

如果您看不到已删除的提交,则可能会解释您遇到的错误。在这种情况下,如果已删除的提交确实只存在于Bitbucket上,则需要其他方法来获取它们。

1
这个答案建议按照问题已经尝试过的方法去做,是吗? - Martin G
@Martin 我没有找到有关检查reflog提交的内容,只有重置。如果你的怀疑是正确的,那么他的提交已经从reflog中被清除了。但如果是这样的话,那么他为什么还能在Bitbucket上看到它们呢? - Tim Biegeleisen
这是最好的答案。 - Abdullah Al Noman

0

这个分支里有标签吗?为什么这个分支在Bitbucket上出现了,但使用fetch命令却没有被获取下来的原因还不清楚。

无论如何,尝试一下git fetch +refs/*:refs/*,然后查看.git/FETCH_HEAD文件,希望你能在那里找到你想要的提交之一。如果没有,需要更多的信息。我还会复制您当前的存储库,以避免强制获取远程引用空间可能带来的任何问题。


谢谢您的回复。我仍然无法检索已删除分支所属的提交。我已经在我的问题中添加了一个示例 - 请参见更新。 - Yury Bondarau
1
我认为我明白你想做什么,据我所知这是不可能的。这与Git守护进程(服务器)决定向客户端发送哪些对象有关。请阅读此链接中的“计数”部分以获得详细解释。简而言之,如果一个对象无法通过分支或任何其他引用(简称ref)访问,则不会被打包并通过网络发送。在您的更新中,该对象存在于Github服务器上的存储库中,但由于缺少指向它或其后代的ref指针,因此无法通过Git克隆访问。 - Yazeed Sabri

0

如果您仍然可以在Bitbucket中看到提交记录(而不是在最近的活动中),那么该提交记录仍然可以从某个分支或标签中访问。您可以运行git fetch以获取这些引用的最新版本,如果该特定提交记录不存在于本地存储库中,则也会下载该提交记录。但是,错误fatal: reference is not a tree: <sha>表示sha不是提交记录,很可能是一个blob。如果找到正确的提交记录,则git checkout <commit>git checkout -b <branch-name> <commit>应该可以工作。


我无法GIT CHECKOUT <COMMIT>,因为我在本地历史记录中没有这个提交 - 它只出现在远程存储库中。因此,首先,我需要从远程检索此提交。 - Yury Bondarau
如果您没有提交记录,那么您使用的是哪个SHA1值并出现了“fatal: reference is not a tree: <sha>”错误? - ElpieKay
抱歉,可能我的问题不太清楚。这个提交出现在远程仓库(bitbucket)中,但它没有附加到任何分支(分支本身已被移除)。 - Yury Bondarau
你运行了 git fetch 吗?Checkout 不会联系 Bitbucket - 它只使用本地的 git 知识。Fetch 更新本地知识。 - Jim Redmond

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接