Regarding How to unsquash a commit? I want to do the same - unsquash a commit.
For the moment I had the same idea to simply delete the current commit and cherry-pick the squashed commits of it.
Unfortunately, when I try to pick such a commit I get the following error message:
fatal: bad revision XXXXX
Okay. So it seems that the related commit does not exist in my local repository. But it does obviously exist in my remote repository hosted on Bitbucket.
In Bitbucket I can access the related commit via its hash. I can see the diffs, everything's there.
How is this possible, even if I performed a git fetch --all
?
Does Bitbucket store these commits in another manner?
How can I access / cherry-pick these commits from the remote repository, as they still exist there?
In Bitbucket I can access the related commit via its hash. I can see the diffs, everything's there.
How is this possible, even if I performed a git fetch --all? Does Bitbucket store these commits in another manner?
Git objects (commits and other types) do not have a reference count, so they aren't immediately discarded as soon as they become "unused". Instead they remain in the object store for several days or weeks until the next periodic "garbage collection" run. Until that happens, they remain accessible directly by their ID (and can even be re-attached to a branch).
This applies equally to BitBucket as it does to GitHub or even your local Git repositories (especially to your local repositories: it's what allows Git's "reflog" feature to work).
However, fetch
or push
will not transfer unreferenced objects. When you do git fetch
, it will look at the configured refs (typically refs/heads/*
aka standard branches) and will only transfer objects referenced by those branches; not the entire object store.
(And similarly, when you commit --amend
a mistake and then git push
the result, it will only push the new commit object that's part of the branch – but not the old commit, even though it still exists in your local repository.)
So if you want to retrieve stale commits, you need to fetch them by their specific IDs (this requires the full object ID; abbreviations will not work):
git fetch origin XXXXXXXX
Now the commit is in your local repository's object-store and can be cherry-picked, seen in git show XXXX
or git log XXXX
, or even attached to a new branch using git branch foo XXXX
. The latter would actually be the easiest way to un-squash something: just fetch the "topmost" commit and put it in a branch, and now you have exactly the same commits – IDs, committer, signature, everything – as the pull request had.
Not all Git servers support fetching by exact ID. This is a relatively new thing, as it became necessary to support "filtered clone", so you'll still encounter servers which only allow fetching the tip of a branch and nothing else.