None of this was specific to git, but I love git, so if this is the push (hehe) you needed, then that's great!
I don't like the "cheat sheet" style of most git tutorials, because it makes it seem like magic, and it seems like a bad idea to trust magic. And then one day something weird happens and the 3 magic spells you know don't work here, and you declare git is broken, or at least too mysterious, and your data is gone. Which usually it isn't.
Here's my quick version: Git is a content addressable Merkle tree storage engine, and then they used that to build a version control system on top. What does that word salad mean?
Content Addressable means you want to store some data, like the contents of a file, so you hash that data (make a fingerprint number out of it, if you don't know the word hash) and then store the data at that hash. Using a summary of the content itself, as an address. This allows you to store "stuff" in such a way that if you have two different "things" they'll both get their own place to live in the datastore, because they have different content. And when the data is changed, the address changes too, so the new data gets stored somewhere else and the original data is still where it always lived. The only place that content can live, in fact.
Merkle trees are a kind of tree where data lives at an address, and then references data at another address, which itself may reference other addresses, forming a tree. They're a bit more specific than that, but this'll do.
So I store my file's contents using my content addressable storage, this means I get an address for it. How do I use this to represent a file list, though? Easy, I make a data format that's a list of files' names, their file permissions, and then their contents hash address. Like a link. This folder has these files with these names, and their contents are here, here, and here. But the magic is that this folder listing is itself data, so it can also be stuffed into the content store and given its own address that now represents the folder, and thus all files in that folder if you follow the links. And if I have subfolders, I can represent those by storing the folder listing in the hash store, and then just linking to a subfolder in the parent folder right alongside the other links to the file contents. Easy!
And here's the key: if any of the files' content changes, then the address of the new content will change, leaving the old content at the old address. But that means any directory listing will need to point at the new file's address, which means its contents change too because of the new address, which means the directory with the new file contents will also get a new address, leaving the old directory with the old files where it was too. And any parent directories will also change by linking to this subdirectory at its new address etc., all the while any unmodified files, and directories containing unmodified files, will continue to have the same address, and so won't need to be stored again and just continues to be referenced by both versions. Ultimately, this means the state of the entire tree of files in a folder can be boiled down to a single address, the hash of the listing of the root of the folder. Everything else can be followed from there. And if any of the files change, anywhere in the tree, they can all be stored in the store, and a new root address will describe this, different, tree with slightly different contents, and both trees can be stored alongside one another and share all the same files that didn't change.
Okay, almost there. I assume by now it's pretty clear how this could be relevant to a version control system, but we need one more thing. What this all gives us is the ability to have any snapshot of a directory structure be fetched by a single address, and the ability to have multiple such snapshots coexist in the same store. But what we need is a connection between those snapshots, and specifically a history of those snapshots, to make them proper "versions".
The good news is it's pretty easy! We just make a new kind of data, called a "commit" which links to the address of the root it represents, so we can get its files, and then because it's data we can put that also into the datastore and get an address for it, called the commit hash because of how important it is. So now we can refer to the commit, and through the links inside it find all the files it represents. But importantly when I make a new commit, I make the new snapshot of the files as previous discussed, and then in the commit I link to this new snapshot, but also link to the address of the commit that came before this one. The previous version. Which now forms a tree of history! This commit hash not only allows me to get all the files it represents, but also to follow to the previous one which lets me see those files too. And if that previous one has a previous one, then I can follow it all the way down the chain to the initial commit. And since we've got this commit object anyway, we also allow people to type a human-readable message in there describing the changes, and we mark down the date and time, and their chosen identity, for historical purposes. Might as well, and now those are in the history too.
That's basically git! But there's a few loose ends. Branches. Since all of history can be referred to by the address of just the newest commit in that history, this is all branches are. They're a human name given to a commit meant to be the newest of its history. And when you make a new commit it will move the branch you're on to point at the new commit, but leave the other branches alone, which allows history to be different on different branches, and you can switch between them freely by name. But because history is all pointing, at some point two branches may both point to the same parent commit, and from that point on history of those branches will be literally identical. This makes it easy to tell where branches diverge.
Tags are just branches that don't move. They give a name to a particular commit in the history, by its commit hash of course, and are usually used for releases or other such things.
Diffs are everywhere when interacting with git, so you'd be forgiven for thinking they're part of what git stores, but they're not really. What's stored is, as I described above, the full contents of the tree. But I can choose to go through the trees at two different commits and compare their files to produce a patch describing their difference, and it's often very useful to do so. The most common version of this is to compare the contents of this commit to the one that came before, its parent, to compute the "diff of the commit". It's not truly what's in the commit, but it followed trivially from it.
Collaboration (pushing and pulling) basically just works by sending the remote side any stored objects you have and it doesn't, and then updating the branch pointers to point at this new stuff. Pulling is the same but I'm getting things from the remote instead.
The index. Ah, the index. So remember when I said you store a file's contents in the store when making a commit? That would work, but what if I only want to store some of the changes in my working directory, but not all of them? Maybe some are relevant for the commit I'm making, but others are for a different commit? Or they're just for testing. It might be useful to not have to store literally exactly what's in the files' contents. So instead there's a staging area, called the index, that stores the contents of the files as we'd like to commit them, rather than how they really are. There are commands to add things to the index, and then during a commit it's the index that informs what gets stored in the store and referenced, not what's in the actual folder. This is confusing at first, because most tutorials skip it for being complicated and teaches people to ignore it completely. But I think it's useful.
Okay, this is a monster, so I think I'll cover "cheat sheet of commands given this context" in a reply to myself.