I’ve been slowly working on and adding to this document as a resource for myself for a few years now as I learn more about git and some general tricks I keep finding myself needing.
General Command Line Stuff
These are just some general command line concepts that are often useful when interacting with git from a command line (the correct way to use git)
ls– List files in working directorycd <relative directory path>– change directoryrm <file>– delete a filerm -r <directory>– recursively delete a folder and everything in it
If you’re using git bash on windows (recommended), you can use the key combo shift-insert to paste which will save time if you’re copying a bunch of junk from this doc
Git Words that are good to Know
Repository/Repo
A pile of versioned files that live somewhere. Its important to note that when using a remote server (think a service like GitHub) that isn’t your repo living on the server. That’s the server holding a copy of the repo. There may be many copies of the repo, and they will pretty much never be totally identical. The repo doesn’t necessarily live anywhere, but you can tell git what remote server it should be trying to sync with.
Upstream
The server(s)-side copy of the repository
Commit
A set of changes to files in the repo
Merge
Combination of changes from 2 or more commits
Branch
A set of changes that are independent of other branches. This is like a timeline of changes, and a repository manages multiple branches at one time.
Main/Master
The main branch on the repo. The one true history of actual changes to the project. A lot of repos now default to this being called ‘main’, but older repos may still have it called ‘master’.
Fork
A completely separate repository with its own tracking of the timeline(s) that at some point matched up with the base repository.
HEAD
The local most recent commit. No, I don’t know why it has to be yelled. (If the server says HEAD, its referring to its own HEAD. Every instance of the repo has its own HEAD)
Git General Best Practices
- Keep some branch in a working state as much as possible. Typically this should be the “main” branch, but some groups have a different policy. What matters is having somewhere where you know the project is stable.
- Do pull requests. If you have multiple people in one repo, whatever branch you have where people put their code on when they’ve finished working on it, don’t let people just push to. People just being allowed to push to main without checks is how main gets broken.
- New features and things that have a chance of breaking other people’s things live in their own branches until they are ready
- As much as possible try to prevent branches from falling too far behind main, merge back early and often to avoid breakage
- As much as possible try to prevent branches from getting too far ahead of main, applying smaller working subsets of changes in small batches makes merging way easier for everyone, and also can help make pull requests more useful.
- Commit whenever you are happy with the state of some sub part and label those commits clearly. Clearly labeled useful commits make it way easier when your team’s git wizard needs to go back in time and rewrite history.
- Committing and pushing are separate actions for a reason. Committing locally regularly is a really useful tool if you’re not sure if the next step you’re taking is going to be correct. Reverting a local commit is a lot easier than rewriting that history on the server.
Git Config Stuff
To make default text editor to be not vi which can be scary
Look I know some weirdos out there actually prefer vi BUT for all the sane people out there who prefer a text editor that doesn’t require memorizing a bunch of key combos to use it at all:
git config core.editor <path to exe for better text editor>
To set up git lfs so git hates big files less
Basically git was originally built for managing the linux kernel project which is a bunch of C files. It handles text files really well. It handles not-text files less well. If you give it large binary files, it may well give up and not help you at all. If you’re using git for something like a game where you have big ol’ textures and models, git-lfs is a really neat solution.
First you’ll need to install the gitlfs module from https://git-lfs.github.com/
Once that’s installed:
git lfs install (run once on a system to integrate the module into your git installation)
git lfs track “.<file extension>” for every large file type you want to track with lfs.
After doing these make sure to commit the .gitattributes file BEFORE committing any files that should be LFS
Git Usage Stuff
This is all assuming you’re using the command line interface, because I still haven’t found a GUI that I like for git. If you can’t figure out how to do these operations with SourceTree or your GitKraken, this handy guide is here to tell you how to overcome the shortcomings of your GUI solutions.
To get a repo
git clone <repo_address>
To update your working copy of the repo
git pull
Which may give errors because your working copy has changes that git hasn’t associated with a commit
To add your changes to the current commit
git add <filename> will add <filename> to commit – if filename is a directory it will add everything within that directory
git add --all will add all files not explicitly forbidden in your gitignore including new ones
git add -u will add updated files, ignoring any files you’ve added. This is normally the one you want.
If you add something you should not have, do not panic, simply
Untrack changes to a file
git reset <filename> will untrack changes to that file, but not modify the file locally
git checkout HEAD -- <filename> will reset a file to the latest committed version
If you messed up
git reset --hard <filename> will reset that file to the last locally committed state. If you don’t give it a filename then it will discard all untracked changes to all your files. Do this only if you’ve messed up bad.
git reset --hard @{u} will discard all of your local changes and reset your local branch to match the current upstream state
If you messed up really bad
If you committed your messed up version
git revert -n <COMMIT HASH> will remove all changes from a given commit and make a new commit that is whatever used to be the current head but without the changes from that commit. The -n flag means it won’t ask you for a message, the default revert message is fine.
To remove a single file from a commit
git reset --soft HEAD~1 to go back to that last commit (you can go farther back)
git reset HEAD <path/to/unwanted_file> to untrack those changes
git commit -c ORIG_HEAD to update the commit so it no longer has those files
If you messed up a merge
git revert -n -m <PARENT HASH> <COMMIT HASH> will remove all changes from a merge commit. You need to specify which branch was the ‘parent’. Note that this will prevent these two branches from merging again immediately, since they have already technically merged. If you want to retry the merge, either branch again or revert the revert commit created by this command.
To see what files have changes tracked
git status
To stash some changes for later and go back to committed state
git stash
This will put uncommitted things into your stash, and bring you back to the last committed state. This is useful when trying to untangle a merge, or just when you’ve made a local change and want to quickly peek at the unmodified state. I don’t recommend going any deeper on stashes than this, as full tracked commits are generally a better option.
To Get changes out of your stash
git stash pop
This will take your changes out of your stash and apply them to your local repo ready to be worked with again. If you want to get the changes back onto your local state without removing them from your stash, you can use git stash apply
To create a new commit
git commit -m “Message saying what you did”
IF YOU FORGET THE -m VI WILL OPEN. DO NOT PANIC.
To exit vim
When you forget to -m on your command line commits, or when a merge happens locally, vim will open. Don’t panic, it’s going to be ok. We’ll get you through this difficult time. You got this. Type your message at the top of this scary looking text editor. There is no mouse input, but arrow keys will move the cursor around. Then:
- Press esc – this enters ‘command mode’
- Type a colon – this is how you start the cool people commands
- Type ‘wq’ – this tells vim to write to file then quit the application
- Hit enter – this runs the command
Now you have made a commit and exited vim, almost like you know what you’re doing.
Renaming Files
If you’re working in a repo and need to rename or move a file: Don’t just move/rename it. This makes your changes hard to track in the repos history, and also makes merging with someone who changed that file suuuuck. The correct way to rename or move a file is:git mv <old_name> <new_name>
You can use this to rename files or move them around but if you use this command git can keep track of that action, whereas if you just use the file system commands good luck getting git to figure that out.
To push your local changes to the remote repo
git push
Working with branches
(this is the real power of git) ((if you’re not branching you’re misusing git)) (((and probably a bad person))) ((((I’m just kidding you might just not know any better but seriously branch before breaking stuff please))))
To create a new branch
Using GitHub’s friendly UI for noobs
- On the repo’s homepage, click where it says “Branch: main”, right underneath the “n commits” thing.
- Type the name you want your new branch in the box that appeared to have and hit enter.
git pull(to update local registry of branches)git checkout <branch_name_here>
The cool people way
git checkout -b <desired_branch_name>(this creates a branch from whatever branch you’re on right now, if you’re on main then it will be main)- Do all your committing mumbo-jumbo, but when it comes time to push this branch for the first time:
git push -u origin <branch_name>
To switch branches
git checkout <branch_name> – git will fight you if you have uncommitted changes, so either commit them, reset them, or stash them
Existing on a branch
Once you’ve switched to a branch, all your git commands are now in the context of that timeline. Pull, push, and commit will now act within that timeline unless otherwise specified.
To get the latest version of main onto your branch
git pull origin main
This pulls the changes that have happened in main since you last ran this or branched onto your branch. This will likely result in a merge, which you may have to resolve conflicts in. Update using this command often to minimize the merge suffering. (This is the command to pull any branch onto any other branch, just replace ‘main’) Sadly though, no matter how often you pull if you’re working on multiple computers or with multiple people at some point you will almost definitely have to deal with merge pain
Dealing with merge pain
So you’ve done some work on your branch, you’re ready to go to main so, like a good little git user, you pulled main onto your branch first. Aaaand you have conflicts.
What files are conflicted? Git has some helpful tools to help you figure that out too.
git status
Will be one of your very best friends thru a merge. Think of it as a ‘to do’ list. If you ask for the status in a repo with a merge in process it will give you a happy list of files awaiting merge, and all you have to do is resolve every single one of those conflicts.
In general, you’ll probably want to use a tool like winmerge or codeflow (most cool text editors that support plugins will have a super great plugin for this too) to help deal with almost all conflicts in text files. They make it really nice to grab changes from either side and end up with a clean file with none of git’s nonsense tags in it.
If you insist on doing it manually, take a look inside a conflicted text file and you’ll see some weird looking tags (if you don’t see them because your file is ungodly, ctrl-f is your friend. they’re in there somewhere). The markers should look something like this:
<<<<<<< HEAD:file.txt
Hello, world
=======
Hello world!
>>>>>>> <massive ugly string>:file.txt
This is saying that on commit <massive ugly string> someone added this line, but didn’t include the comma and was generally more excited about life. Your commit is shown as HEAD since that it your local HEAD, and as the person dealing with this conflict, you are less excited about life in general so included no such punctuation but did throw in a comma for good measure.
Basically, to fix this file, you need to remove the tags and come up with some compromise on the string. You could take yours from HEAD, or theirs from <unholy string>, but since that person was feeling extra peppy and you like commas lets preserve a bit of both. This is the power of manually resolving these things.
Hello, world!
This is great for text files, or for files with only a few changes. But what about binary files, or text files that differ massively. In either case you probably know which version of that file you want. Either:
- ‘ours’ – the one you have/the one belonging to the branch you’re pulling onto
- ‘theirs’ – the one someone else has dared to touch / the one you just asked to pull
Depending on your situation, one of those is probably a clear option if the nice happy text merge compromise from earlier doesn’t work. Once you are sure which option is correct, simply
git checkout --ours Path/to/conflicted/file
OR
git checkout --theirs Path/to/conflicted/file
If you’re an artist and just updated all the textures, but a programmer has gone and programmer arted all over them while you were working on it, you can take all of your .tff files easily with:
git checkout --theirs *.tiff
(be careful when doing things like this though, as this will overwrite all .tiffs even if you probably should have kept some of the ones the other artists worked on. (The magic of git is this is until you commit this, you can still go back and checkout both version of each file freely)
And the most important merge step of all: test it all still works before you commit. Compile it and run the tests, view the page and make sure it all looks right, try playing the game. Verify its all there and behaving correctly before committing the merge. It can be painful to try to figure out a half-resolved merge that’s already committed to main. (If you are in that state, the same things apply. Git just doesn’t tell you which files are bad so you’ll have to rely on your other tools)
So you just switched branch and there’s a bunch of junk left here from your old branch or just like gross build detritus you don’t want any more
git clean -fd
Clean removes untracked files. F means f***ing do it. D means directories too. This will theoretically leave you in a pristine state that matches exactly the most recent commit that you have locally. In practice I still sometimes abandon hope and pull a new copy. Part of the power of git is allowing you to do this. Optionally you can add an ‘x’ to the arguments to only remove files that git is already set to ignore (super useful for cleaning up build detritus)
Further Reading for Nerds
- Git flow – A git branching workflow useful for keeping a stable main build for things like released libraries http://www.devops-blog.net/git/git-flow-how-its-used-and-why-you-should
- Conventional Commits – A methodology for commit messages that makes them easy for robots to parse to help automate workflows https://www.conventionalcommits.org