9

I have some old changes I made, committed and pushed to remote (bitbucket) repo. These changes was not merged to any other branch. Branch itself was removed locally and from remote repo.

But, as I can see in bitbucket web, commits included to removed branch still there (in repo). I googled a lot, but did not find a way, how to retrieve commits of removed branch from remote. The only I can do is to see them in bitbucket web and get sha of commit there.

I saw some examples like

git checkout <sha>

or

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

But always get the following error

fatal: reference is not a tree: <sha>

So, is it possible and how I can retrieve (fetch) these commits from remote, crate branch from them, merge to release branch?

UPD:

To be more specific, I have created a repo, new branch, make a commit in this ne branch and remove branch:

Repo https://github.com/yurybond/stackowerflow-rocks

Link to (standalone) commit from removed branch https://github.com/yurybond/stackowerflow-rocks/commit/a1c1540abd453773b3ce6445d01e51ad336bbe84

The question is still the same: How to retreive commit (a1c1540abd453773b3ce6445d01e51ad336bbe84) that belongs to removed branch?

Community
  • 1
  • 1
Yury Bondarau
  • 847
  • 1
  • 9
  • 20
  • 1
    Note that you can always download the commit as a patch and create a new commit automatically: `wget https://github.com/yurybond/stackowerflow-rocks/commit/a1c1540abd453773b3ce6445d01e51ad336bbe84.patch && git am a1c1540abd453773b3ce6445d01e51ad336bbe84.patch` – Arkadiusz Drabczyk Apr 02 '18 at 10:11
  • @ArkadiuszDrabczyk, your answer is the best for github. Is there any way to do the similar approach for other types of repos (bitbucket, gitlab etc.)? – Yury Bondarau Apr 03 '18 at 06:37
  • @ArkadiuszDrabczyk, I guess your approach is only viable solution of my issue with commits of removed branch. Could you please in 4 hours (until bounty expires) create an answer with step by step guide how to download commit into patch and apply this patch. – Yury Bondarau Apr 03 '18 at 07:54
  • I added my answer but I wonder were does `fatal: reference is not a tree` message come from. Do you use `submodules`? – Arkadiusz Drabczyk Apr 03 '18 at 09:28
  • I do not have submodules. I guess this issue can happen if your local copy of repo never had the branch removed. (For instance branch was created, pushed to remote, removed by somebody else,) – Yury Bondarau Apr 03 '18 at 15:00

7 Answers7

8

You can manually download missing commits in mbox format and apply them manually with git am. For example:

  • 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
    

Notice that git am will not only apply textual patch but will also recreate a whole commit on the current branch.

If you removed a branch from the remote repository there is no way to get it back from the remote reposityr. However, normally you still might be able to restore a removed branch locally thanks to git reflog. See the following example.

First, create a new non-bare repository:

$ git init
Initialized empty Git repository in /tmp/reflog-test/.git/

Add file and create a new commit on master branch:

$ 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

Switch to a new branch creatively named new-branch:

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

Modify file and commit changes:

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

Switch back to master:

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

Remove new-branch

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

It's gone:

$ 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>...]'

However, you should still be able to reference the commit:

$ 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(+)

Even though it can't be found on any branch. The following command returns nothing:

$ git branch --contains  9c457c6

Reflog shows all actions one did in a repository:

$ 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

As you see it also has 9c457c6 commit we did on the new-branch. Only when reflog is expired and garbage collector is run 9c457c6 becomes unreachable:

$ 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>...]'

By default reflog entries expire after 90 days and git gc is run automatically after some commands so if you have removed the branch relatively recently you should be able to restore it.

Arkadiusz Drabczyk
  • 11,227
  • 2
  • 25
  • 38
2

If this commit does actually exist in the Bitbucket repository, then you can merge it into another branch using the Bitbucket web interface.

  1. Go to branches --> Create a new branch in Bitbucket. Name it whatever you want
  2. When the branch is created, click compare (the three dots menu in top right)
  3. In the compare screen, select change source and paste in your commit reference. If it comes up with a result, then you're in luck. Select it.
  4. Select change destination, and change it to your new branch.
  5. You should be able to browse the difference, seeing all the changes you want to bring in.
  6. Select merge if you're happy.

Now here's the kicker. If pasting that commit reference at step 3 fails, then you're out of luck. It means the commit does not exist either on the remote or in your local.

If you hate navigating and want a direct link to the compare (will compare to master, though): https://bitbucket.org/<user>/<repository>/branches/compare/<commit-reference>..master

Cody Hamilton
  • 331
  • 1
  • 4
  • This is a good workaround, but not always applicable. For instance you will not be able to merge in case of conflicts between new branch and commit selected. – Yury Bondarau Apr 03 '18 at 07:41
2

git fetch origin <sha> will fetch the commit from server, but this might depend on whether you're using GitHub, BitBucket or other hosting provider

Kirill Osenkov
  • 8,786
  • 2
  • 33
  • 37
1

I haven't seen anything on Stack Overflow about checking out a commit which has been deleted. However, it should be possible to do a hard reset to a deleted commit via:

git checkout -b some_new_branch
git reset --hard <SHA-1 of deleted commit>

This would reset the branch some_new_branch to the deleted commit, and perhaps this is enough for you to proceed.

I am assuming that the deleted commits you see in Bitbucket are coming from the reflog. If so, then you should also be able to view your deleted commits via:

git reflog

If you don't see your deleted commits, then it also might explain the error you are having. In this case, if the deleted commits really only exist on Bitbucket then you would need some other way to get to them.

Tim Biegeleisen
  • 502,043
  • 27
  • 286
  • 360
1

Well because you have deleted the branch locally, you can not restore it with sha-1 as everybody else was suggesting.

Also reflog doesn't work on the remote side (plus it is removed from the remote side too.

This leaves you with one of two options. Either write to Bitbucket support in order for them to restore what you need.

Or try Michael's suggestion here: Recovering a deleted branch from a remote on Bitbucket (git)

The solution suggested sounds like it may resolve your issue. Please let me know how it works

Haris Nadeem
  • 1,322
  • 11
  • 24
  • I have checked the link you provided and it works for bitbucket. For github it does not, unfortunately. But it is still a good trick for bitbucket - thanks. – Yury Bondarau Apr 03 '18 at 09:41
0

If you can still see the commit in Bitbucket(not in Recent activity), then the commit is still reachable from some branch or some tag. You could run git fetch to get the latest version of these refs, and that specific commit will be downloaded too if it doesn't exist in the local repository. However, the error fatal: reference is not a tree: <sha> means the sha is not a commit. It's most probably a blob. If you find the right commit, git checkout <commit> or git checkout -b <branch-name> <commit> should work.

ElpieKay
  • 27,194
  • 6
  • 32
  • 53
  • I can not GIT CHECKOUT because i do not have this commit in local history - it appears only in remote repo. So, first, I somehow need to retrieve this commit from remote. – Yury Bondarau Oct 30 '17 at 07:38
  • If you don't have the commit, what sha1 value did you use and got the error `fatal: reference is not a tree: `? – ElpieKay Oct 30 '17 at 08:14
  • Sorry, probably it is not clear from my question. This commit appears in remote repo(bitbucket), but it is not attached to any branch (branch itself was removed). – Yury Bondarau Oct 31 '17 at 09:16
  • Have you run `git fetch`? Checkout won't contact Bitbucket - it only uses what git knows locally. Fetch updates the local knowledge. – Jim Redmond Mar 28 '18 at 18:24
0

Does the branch have any tags in it? The reason as to why the branch would appear on bitbucket but not get fetched down using the fetch command is unclear.

Anyway, try git fetch +refs/*:refs/* then look at the .git/FETCH_HEAD file and hopefully you will find the one of the commits you want there. If not, more information will be needed. I would also make a copy of your current repo to avoid any headaches that might come with force fetching the remote refspace like that.

Yazeed Sabri
  • 346
  • 3
  • 17
  • Thanks for the response. I still can not retrieve the commit that belongs to removed branch. I have added an example to my question - please see an update – Yury Bondarau Mar 30 '18 at 12:10
  • 1
    I think I see what you are trying to do and as far as I know I it is not possible. It has to do with how the Git daemon (server) decides on what objects to send to the client. Read "The Counting" section of this [link](https://githubengineering.com/counting-objects/) for a nice explanation. The gist of it, if an object is not reachable by a branch or any other reference (ref for short), it is not packed and sent over the wire. In your update, the object exists in the repo at Github servers but is not reachable through Git clone due to the lack of a ref pointer to it or its descendants. – Yazeed Sabri Mar 30 '18 at 19:34