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.