Well did you get it? The Dad jokes are real…
Today, for Tech Tuesday, I want to share some of my most commonly used GIT commands and workflows. If you’re a developer and you’re not using GIT I highly recommend you start learning. GIT usage is easily becoming a must-have in a developers toolbox. I won’t cover the basics in this post but if there’s interest, reach out to me and I could be convinced to write a Beginners Guide.
Let’s start by breaking down the workflow. Let’s assume I’ve already performed a ‘git clone‘ and downloaded the code repository to my machine. Before doing any real work, my first call is almost always to break out into a new branch, which means my first call after a clone is most often, ‘git checkout -b feature-id-name‘. This gets me in my own workspace and allows me to move forward without worrying about any other developers work or changes.
Now let’s say I’ve made a couple of changes to a couple of files and now I want to add them to my commit history. Usually at this point I could perform a simple ‘git add .‘ or ‘git add fileName‘ and both options would include either all of my changes or a single file worth of changes.
Often times though I find that this doesn’t give me as detailed of a break down in my commit history. I may have multiple changes, of various context, over the course of an hour and if I represent those as separate commits, instead of one big commit not only do I have a clean, readable commit history for my fellow developers, I also have the ability to cherry-pick specific commits or even skip commits that maybe have a bug or mistake in them.
To that end, I like to use ‘git add -p‘ for staging my commits. The ‘-p‘ argument stands for ‘–patch‘ which really just means I can breakdown my changes into chunks. Git will try to give you parts of files and let you decide if you want to stage them. I find this perfect for when I have a bug fix over here, a feature over there, a couple of tests mixed in…. I can now break these down and say, “okay this chunk of code goes with this feature and this test but doesn’t involve this bug fix.” You also get some nice options on how you want to stage the changes: You can use
y to stage the chunk,
n to ignore the chunk,
s to split it into smaller chunks,
e to manually edit the chunk, and
q to exit.
Now, let’s say I wasn’t being a good little dev and I accidently staged and then even worse, committed some changes that I didn’t mean to. Well it’s really not so bad to fix up. A little, ‘git reset –soft HEAD~1‘ will undo that last commit and get you back to having all of your changes staged. Now you can add/remove any other changes you need and set things back straight.
But let’s say you did you really bad thing… let’s say not only did you commit a password to your repo but then you pushed that change up to a repo… shame… shame… shame… but don’t sweat it. We can fix that too. We can still use a little reset magic like we did above, undo our change, remove our commit of the password, and then when we are ready to push our changes back up with ‘git push -f‘ to force those changes up. What this will do is re-write our git history with what we fixed locally. I can’t say how I know how to do this… I’ve never pushed a password to a repo….
Before I perform a force push of anything though, I run a ‘git log‘. This is one of my favorite commands because it allows me to see every commit that is in my local repo and then match that up with what’s in my remote repo. When I’m building releases this is extremely valuable for me to make sure I’ve captured every commit for a feature and nothing was missed or anything extra added. Use ‘git log‘ as your sanity check.
Speaking of building releases, ‘git rebase‘ can be one of your best friends… or your worse enemies… A rebase call is great to take a feature branch and update it so that when you look at the history of commits, it looks as if the feature was just worked on today. The rebase will take all of the other changes from other developers, put them at the bottom, and then put your feature on top of all those. This can make merging features into a release branch or a master branch easy peasy, however… if that branch is extremely old, if there is an enormous amount of conflicts in the the rebase, you may be better off performing your own merge. Don’t be afraid to call ‘git rebase –abort‘ and look for a cleaner solution.
When a rebase fails, I typically look to either perform a merge OR to call ‘git cherry-pick commitHash‘. Cherry-picking allows me to pull one single commit out of one branch and into my local repo. This can be very handy for grabbing a change here and there and pulling it into a release. After all, who doesn’t like cherries?
And typically when I’m building releases I’m pulling changes from various branches other developers have worked on. Sometimes I’ll jump into their branch and perform a rebase to clean things up and make the merge into master run smoothly. But to make sure I’ve got all the latest on whose branches are out there with what commits I’ll run a simple ‘git fetch‘, which downloads all of the latest branches and their commits.
So, we covered quite a bit so let’s recap on what we’ve discussed so far. We can run a, ‘git checkout -b feature-id-name‘ to start a new branch and then use ‘git add -p‘ to stage and commit just the changes we want. If we make a mistake we can ‘git reset –soft HEAD~1‘ to undo our last commit and if we really messed things up we can ‘git push -f‘ to force our remote branch to accept our new changes.
To make sure everything is just how we want it we can use ‘git log‘ to review our commit history. If we need to update an old, stale branch we can call ‘git rebase‘ and if things get hairy we can back out with ‘git rebase –abort‘. If our rebase is a no go we might look to ‘git cherry-pick commitHash‘ and grab exactly what we want. Finally, we can use ‘git fetch‘ to make sure we’ve got the latest and greatest on everything in the repo.
These are my most commonly used git commands but I’d love to learn more. If you’ve got some commands you’re using often that aren’t on this list let me know so I can include them. If you’re using any of the these commands in a different way I’m interested to hear how!