-5

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
  • I don't think there's anything that particularly distinguishes a revert commit, aside from the standard naming format - see e.g. stackoverflow.com/a/3824122/3001761. Commented Sep 5, 2024 at 14:06
  • There are some external actions that I need to trigger in case the last commit was a revert, that's why I need this. Commented Sep 5, 2024 at 14:09
  • "It would have been nice if git reflog worked inside the runner" The reflog is basically just like a more informative Terminal history: it records what Git did on this machine. Unless you somehow managed to tell the GitHub repo itself to create the revert commit, the reflog has nothing to do with this case, because (I presume) that happened on another machine (i.e. the local computer of some developer, perhaps you). Commented Sep 5, 2024 at 17:47

2 Answers 2

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.)

Sign up to request clarification or add additional context in comments.

8 Comments

I am surprised the guys who wrote git emit the commit type with git reflog but offer no provision for the same with git log
@user1953684 There are no commit types. Only naming conventions in logs and reflogs, but they could be different, and all commits are the same type (except, maybe, merge commits or non-merge commits)
@user1953684 they don't emit the commit type, commits don't have types; they emit the command that led to it (i.e. it's marked revert because you ran git revert to create the commit). In GHA those commands never happened, it only has the commits and the switch you're seeing.
@JoachimSauer Absolutely. That's why I wrote "maybe", for the specific contexts where the distinction makes sense. But technically the idea is indeed: there are no commit types.
@user1953684 again, that's not because "revert" is any part of the commit (aside from the default message). It's part of your history of interacting with the repo - that's how you created that commit, using the revert command. So anywhere other than your machine, not just on GHA, where that commit was not created by running git revert, it will not be in the reflog. You can't edit it because that wouldn't make sense - that is how you got there.
|
1

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.