DEV Community

Cover image for How to better organize your git commits
Krzysztof for 2n IT

Posted on • Edited on • Originally published at 2n.pl

How to better organize your git commits

Git is an admirable piece of software. It might be the best one I've seen so far in my programming career. I think it's one of those that I'm impressed the most because very often when I do learn something new it leads me to another new thing. This constant process of exploration happens very naturally to me in my daily workflow and during free-time programming workouts cause I use git as my version control system in all my projects so far and honestly I find it kinda sad that a lot of people limit their work with git to a very basic subset of commands. Amongst many programmers, younger or not, that I met personally or just happen to read about their workflow online I see a lack of interest in git besides basic commands, used for the basic workflow. It’s nothing bad, but I think it’s kind of a shame, and a wasted opportunity to learn something fun and useful.

This basic workflow with git many people limit themselves to goes as follows:

  • git clone and git init to get an existing project or create a new one

  • git branch branch_name* (or git checkout -b branch_name)

  • git rebase to update branches with some other branch changes

  • git pull to... well to pull from remote

  • git push to send your changes to remote

  • git merge - to merge...

  • some helpers command like status, diff, log

  • and obviously git add; git commit -m 'message' to stage your changes and commit them

Honestly, that usually is enough to work on a completely functional project and if none makes some mistakes you can go from nothing to a fully functioning git repository using only those commands. And if you and your team can make a new project and create a readable, sensible 'git tree' using only those commands then bravo, none made a mistake, none pushed an old branch with force, none merged merge conflicts, none rebased with the wrong branch, etc.

Anyway, I would like to encourage you to check out, and maybe use an interesting option passed into git add. I've learned about it recently and became really fond of it.

WHAT DOES add . EVEN DO?

We should probably quickly go over this, right? Without getting too much into the inner workings of git cause that's not why we are here git add . adds(duh!) changes you point to “.” indicates to everything that is currently changed but not staged from the directory we are currently in. So being in the main project directory will add all the stuff nested in it. You can also pass a certain file path) to a magical place called index. The index is this space where your changes go to, before becoming a real commit. When you commit, you take the index changes and make them into a commit. You may think of the index as a sort of checkpoint before actually doing something. In reality, git index is actually a binary file in a hidden git folder of your repo, but that is not very important right now.

WHY YOU SHOULD DIVIDE YOUR CHANGES TO MANY COMMITS

So imagine you just got a new task, and you estimate it as a task for at least 3-4 days of work. A lot of new files, lots of changes to existing ones, the task involves a lot of business changes and many logically separate components. If you are a good git worker, you will know that tasks like this should consist of many commits. Each of those commits should introduce a new business or logical change to your project. You want to push your changes in a way that will make it easy for people to type git log and then look at your last few commits and immediately know what was added from a business point of view, and why was it added this way (by commit description for example). If you commit two days of hard, intense work on a complex code that tackles a lot of problems it is very probable that this commit won't be very explicit or helpful. It might even prove confusing for other people. The way big batches of changes should be implemented is usually series of smaller commits (or at least one commit with a more explicit description if the changes are really tightly connected)

You may think this is obvious but believe me, it is not. I saw senior developers commit work worth of two weeks with a message of the date they started working on the feature. And oh my god, so many "hotfix" commits with a description of "fix2" or "fix bug_4". It's a really bad practice. I think that when we type in git log into terminal, and we scroll down a bit, we should get a good description of what has changed in our app, why it changed that way, etc. If we write our code in a way that makes it as explicit and understandable for others, why shouldn't we treat our git tree the same way? It's just plain English (or any other language) in those messages.

SO WHAT DOES add --patch DO?

When you use git add . everything is done in the blink of an eye, but git add --patch will take a while, and give you more to do (hurray, more work with git, right?). Alright, we will work on some legacy rails app, and do some mockup changes to it. Let's say we are working on a new branch, where we got a lot of different changes to do, they are all connected for the client that tasked us with the changes. They come from one ticket (task) and should all be deployed to production at the same time. But from our point of view, those changes are not that much connected. Let's see git diff of those changes.

So there's a change to a mailer that welcomes the new user to the system, there's a change in the validation of Post model, there's a change in some method of AuditLog model that probably affects its data, and finally, there's a change to PostController delete action (keep in mind those changes are just mocked, it does not matter they don't do much, what matters is that they represent different business and logical changes and can be displayed easily for me to explain).

We can have two approaches to situations like this. We could just git add .; git commit and then name the commit, and put in the description what exactly we changed. But for git messages to be easily readable they should be limited to 50 characters. Describing changes to 4 different concepts in 50 characters might prove difficult. Especially if we had some bigger changes that would take up more than 4 files.

Lets finally use add --patch

Okay so we only see one bit of change, the controller one. And we have a prompt. What is git asking us about, what is this whole hunk thing? Hunks are the bits of changed code you see separately when you use git diff. We will talk more about hunks and how git sees them soon. For now, let's just add it and go with the flow. Type y

Another hunk. Hmmm, let's not stage this one, okay? Type n

Again, we don’t want this hunk... We don’t want any other hunks added right now, we added one and we are happy with that. Type q to indicate that we should skip this and all other hunks that were not added yet. We can use d to only skip hunks from the current file.

Alright, we have 4 modified files, 1 of them is staged. Let's commit and see our logs.

Looks okay, right? Alright lets stage and commit all other changes with their own commits using git add --patch and see our final product.

Now, we have a more clear view of commits. You might ask yourself though, "well I will just plan my workflow correctly, do all business and logical changes together, commit them and move to next batch of connected changes". Sure it sounds nice and all, and if you can do that, then good for you. But reality verifies statements like this and other brave statements like "we will refactor this later". In reality, most of us when tasked with a new big feature and a limited portion of time, just jump right into it, and start building until we are finished. Create a new branch and work, work till its ready to be presented to others for code review. What's great about --patch is not only the ability to separate your work as logical batches (in the form of commits) but also a more thorough, step-by-step review of your code. Once you get the hang of it, it does not take that much time and might save you a lot of work later on. It will also help others to understand what is going on (code review by commits, not by the entire branch is easier when commits are separated nicely).

Top comments (6)

Collapse
 
xayden profile image
Abdullah Gira

Thanks, that was my intro to --patch 😁️! I will make sure to use it.

Collapse
 
fwolfst profile image
Felix Wolfsteller

... and if there are a lot of changes in many different files and you want to select only the ones relevant for the current changeset, consider git add --interactive (or git add -i). With that you can select beforehand which files are relevant. Also good if you forgot to stage a hunk that would come really "late" when going through all changes via --patch (you can call git add -p and git add -i as often as you want until you git commit -v).

Collapse
 
krzykamil profile image
Krzysztof

--interactive is also an interesting option that's true, but I decided too skip on it in this article cause it's formula is a bit more complex with it's prompts and I wanted to showcase the more basic way of spliting hunks into commits. But it's good to mention it at least, so thanks :)

Collapse
 
jessekphillips profile image
Jesse Phillips

Completely agree and why I wrote

Though I recommend git gui over the command line for breaking up commits.

Collapse
 
krzykamil profile image
Krzysztof

Yea sure gui's are nice and helpful but also a matter of preference I think. Personally im very used to and comfortable with CLI and I thought it's better for an article like this to showcase basic, and kinda of a "default" usage of this command.

Collapse
 
jessekphillips profile image
Jesse Phillips

Oh I completely understand, but I think this Cli can scare people off of doing the right thing. That doesn't mean I want to write a tutorial on the unintuative git gui.

I showed a dev the technique with Git gui and the response was, "I prefer the command line." I let him know he could do the same thing there.