About restoring a deleted Git branch
Recently, a branch was deleted from the server without it being merged.
Luckily for us, we had a local copy.
We used used the command git reflog
to get access to the reference logs of the branch.
The command returned results similar to the below:
271f0084 HEAD@{0}: pull: Merge made by the 'recursive' strategy. 0e71c0b HEAD@{1}: commit: Minor change: Cracked NSA systems c81624b HEAD@{2}: pull: Fast-forward ef1f281 HEAD@{3}: commit (amend): Deployed satelite 1ed1a5a HEAD@{4}: commit (amend): Deployed satelite a8682cb HEAD@{5}: commit (amend): Deployed satelite e4560c8 HEAD@{6}: commit (amend): Deployed satelite 1679a90 HEAD@{7}: commit (amend): Deployed satelite d27d2c9 HEAD@{8}: commit: Deployed satelite aaf8261 HEAD@{9}: checkout: moving from master to crack-nsa-systems 2500e11 HEAD@{10}: clone: from ssh://[email protected]:7999/secret/bananas.git
From this information we got the hash value in front of the commit which we wanted to use to restore, which was 271f0084
.
Then, we checked out that version using
git checkout 271f0084;
When we tried to push the branch back to the server the Git pre commit hooks
blocked the operation.
Based on the rsa
key used, we could only submit changes that were committed by the owner of that key.
The option to disable temporarily the pre commit hooks was unfortunately out of the question.
So we had to replace all author names and emails with the name of the one holding the key.
To do so we used the following command before pushing to the server once more:
git filter-branch --commit-filter ' if [ "$GIT_COMMITTER_NAME" = "Doe, John" ]; then GIT_COMMITTER_NAME="Squarepants, Bob"; GIT_AUTHOR_NAME="Squarepants, Bob"; GIT_COMMITTER_EMAIL="[email protected]"; GIT_AUTHOR_EMAIL="[email protected]"; git commit-tree "$@"; else git commit-tree "$@"; fi' HEAD
git filter-branch
lets you rewrite Git revision history by rewriting the branches mentioned, in our case it was HEAD
, applying custom filters on each revision. Those filters can modify each tree (e.g. removing a file or running a perl
rewrite on all files) or information about each commit. Otherwise, all information (including original commit times or merge information) will be preserved.
HEAD
is a reference to the currently checked out commit. In normal states, it’s actually a symbolic reference to the branch you have checked out.
Looking at the contents of .git/HEAD
you’ll see something similar to ref: refs/heads/master
.
The branch itself is a reference to the commit at the tip of the branch.