https://github.com/unqueued/git-cache-tag
It copies all untracked stuff (including node_modules) into a leaf tag. It is fairly easy to manage them, or find the latest one. And because they are leaves, they can be pruned and completely garbage collected when they aren't useful anymore.
I have been burnt many times by npm, and I use this script to guarantee that I have a stash of my node_modules, while also keeping my project small.
And I have diffed different snapshot tags to see which module changed that broke something.
And by leaving everything in unaltered text, it exposes it to git which does a great job at compression stuff, especially highly differential revisions of my node_modules.
A 500M node_modules from one of my projects only weighed about 100M extra, even with several snapshots. And I can just delete them anyway.
I need to work on it a lot more, it was just a quick and dirty solution when I had to work with React Native a few years ago. It doesn't handle submodules at all, and there's plenty more I'd like to do with it.