I need to detect if the last commit was a revert commit and run some appropriate steps in a GitHub Actions workflow. I’m looking for some appropriate git commands that would help me to detect a git revert
.
All the ideas I am seeing suggest to scour through the commit messages looking for the string ‘revert’ but the commit message might as well be ‘abracadabra’.
It would have been nice if git reflog
worked inside the runner, but all it does is output a message saying ‘switched from master branch to feature’. Are there any options I can use with git log
that would indicate that the last commit was actually a revert? My workflow is pretty simple, a bit like the below. What I am looking for are the right commands in the ‘Detect revert’ step.
steps:
# ...
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Detect revert
run: |
#git reflog # doesn't work
git log ... # are there any options that go here that would help detect a revert?
Any ideas?
3
There are no such commands. A revert commit is just a commit like any other. Topologically it will have the feature that it just appears on its branch rather than being a merge commit (i.e. it will have only one parent), and for certain branches, that might be suggestive; but that’s all that distinguishes it. Git maintains no magic “memory” of the fact that a revert
command generated this commit. That is why Git, by default, offers a commit message for a revert commit that includes the word “revert”; if the person who made this revert commit removed that word from the commit message, then no signal remains. (And the situation you are in is exactly why one should not remove the word “revert” from the commit message.)
8
This answer will teach you how to detect the easy stuff, but, …
… but you’re already positing that someone might change the subject line to make it harder to identify reverts. People actually willing to put in some effort to hide a revert are eventually going to figure out that obfuscating the effects of code changes is easy.
If you need some automated guarantee of detecting reverts made by an adversary, your problem isn’t detecting the reverts.
The answer:
That said, for a quick check you can use what Git uses, a “patch id”,
and to find whether that change is a revert of any previous change, you’ll want patch-id caches but the brute force method is
patchid=$(git diff-tree -p @ @^ | git patch-id)
patchid=${patchid% *}
git rev-list @ | git diff-tree --stdin -p | git patch-id | grep -m1 ^$patchid
This detects commits that could have been generated by git revert
(the second id printed here is the commit id that has that patch-id you’re looking for), but cannot detect all reversions. No vcs can, anyone telling you different is selling snake oil.
I’m not going to wade through how to maintain caches on github actions, you get to do that yourself, but here’s how to do it in the shell:
You need two cache files: tips.seen
and patchids.seen
. To update the cache before checking whether the current tip is a revert,
update-patchid-cache() {
>>tips.seen; >>patchids.seen # prep
git rev-list --no-merges --branches --stdin <tips.seen
| sort | join -v1 - patchids.seen
| git diff-tree --stdin -p | git patch-id
| awk '{print $2,$1}' | sort
| sort -um - -o patchids.seen patchids.seen
git log --branches --no-walk --pretty=^%H >tips.seen
}
and to check whether the current tip was a revert it’s just
update-patchid-cache
patchid=$(git diff-tree -p @ @^ | git patch-id)
revertid=`grep -m1 ${patchid% *} patchids.seen`
revertid=${revertid% *}
echo ${revertid:-no commit} looks reverted by the tip
and if your tip’s clearly a revert its id is the first thing printed.