loading...
Cover image for Do your commits pass this simple test?

Do your commits pass this simple test?

sublimegeek profile image Jonathan Irvin ・2 min read

Photo by William Iven on Unsplash

Frequent.
Descriptive.
Atomic.
Decentralized.
Immutable.

I'll leave it up to your imagination to come up with a clever mnemonic to remember that acronym above.

I love writing about Git and SCM in general because I believe it's crucial to proper CI/CD practices. It's how we submit our code. It should matter.

I've come up with a solid metric for measuring the success and value of a commit. It has 5 key points I've mentioned above.

Frequent

When did you last commit? 2 seconds ago or 2 hours ago. The longer you wait, the more trouble you will have with having your commits tell a story. How long did you wait before your last commit?

Descriptive

Click. That's usually the sound a camera makes when it takes a picture. That's the same idea when Git captures a diff of your changes and you put your label on it. I've found Git commit messages are oftentimes more useful than code comments. Can you describe your change verbosely, yet concisely?

Atomic

Are you the type that throws everything in a laundry bin or do you sort your clothes before washing? We can relate laundry to commit messages, in a way. Your commit should only have related changes. Each commit should be easy to revert. Keep your commits small and relevant and you will never have to undo hours of work. Did you keep this commit small and only containing relevant changes, not ALL changes?

Decentralized

This one is super easy. Git doesn't automatically upload your commits to the "Cloud". Everything is local until you push your commits to a separate repo. Most of us humans use GitHub or something like it. Have you pushed this commit yet?

Immutable

A commit is a commitment. Once it's done, do your best to avoid changing it. That means timestamps, authors, and even the contents should never change after it's saved. Is your commit a "forever commit"?

Posted on by:

sublimegeek profile

Jonathan Irvin

@sublimegeek

I'm a full-stack cloud-minded engineer who enjoys devops and clean code.

Discussion

markdown guide
 

Can you describe your change verbosely, yet concisely?

To put it concisely: no.

Or, to be more verbose:
NOOO

😉

 

Very true. I'm not a fan of squashing commits because you lose the history (in a way) of each commit message. One of the tools I use in troubleshooting or continuous development is relying on the commit messages of other developers from a quick git blame on a file I'm working on. 9/10 times, the commit message is much more useful than a code comment.

 

I agree 100%, but if squashed there is not too much use of blame no?

 

I’ve been thinking about the complete git/GitHub workflow.

  1. Working commits as described here.
  2. Squashing and reorganizing the history in preparation for a pull request.
  3. What to include in a pull request and what to move to another pull request.

Thanks for prompting my thoughts again.

 

What's the point of squashing?

 

Squashing is about organizing. The most common case are putting a few commits that do very similar things together as one for the final history.

 

I suppose the second point makes sense if you are creating specialized branches or when you are working locally and before you push your changes you want to show a unified picture in your log.

 

It is actually part of a common workflow with GitHub. Commit locally often. Push to a working branch on a personal fork often. Reorganize, squash, rebase, locally then git push --force-with-lease the result you want in a pull request onto your branch before sending it back to the parent project.

 

Do you have a recommendation for how frequently one should make changes? For instance, I am working on a new feature today, and I plan on committing after I'm done in about three hours. Does that make sense?

 

Commit frequency depends on the task and your workflow. We use feature branches with git.

When I'm adding a feature to an existing code base or fixing a defect, I usually do a bunch of refactoring commits. I try to keep similar changes together. So I'll get commit messages like:

  • run codestyle tool
  • refactor variable names (using refactor tool)
  • update documentation
  • add more unit tests

And then I'll write commits for the new code:

  • add new class x that does y with unit tests
  • add new class a that does b with unit tests
  • etc.

If it gets too big I break it into multiple pull requests. 8-10 commits per pull request is about as high as I'll go.

My main goal is to make my code changes reviewable. Having the refactoring changes separate from manual changes makes code much easier to review.

I also like the idea of using commits like a ratchet. So I'll commit when I'm about to move from a change that I'm sure about to one that I'm tentative about. Then I can revert quickly if I feel I've gone down the wrong path without losing the changes I was sure about.

 

I start with everytime you hit save. Can you describe your change in one sentence? Commit it. Then after you get into the rigor of committing that frequently, then you can begin to scale back. It's more art than science.

 

Every time I save? Good lord! I save at least once every 2 minutes.

Lol, but you get my point. It's extreme, but you have to think about the changes you made. Adding classes and methods can constitute a commit.

 

Color me envious. I wish we used Git at work on my project. Frequent, small check-ins would jibe with how I work. As well as frequent branching.

At work, we use a centralized version control system. Check-ins are large and infrequent, and have to have been compiled and tested before check-in, because each check-in triggers the CI/CD build server. And breaking the build causes other developers to break out the pitchforks and torches.

Compiling the project takes between ten minutes to an hour, depending on how much gets touched. (Although long in the tooth, Lakos's Large-Scale C++ Software Design is highly applicable to my project. Wish we could apply it. Alas.)

I certainly don't recommend spending hours on a message. And in your example of the branch names containing the task number, we do that too.

But if branch 2345 is branched off of 1900, then some new commits are added to 1900. By looking at the commit history of 1900, how would you tell when 2345's commits ended and 1900's started?

Date, time, and author may be useful. But that would take more mental power than simply having "2345" at the start of all of 2345's commits and "1900" at the start of all of 1900's commits.

My team's backend branches off of each other quite a bit and sometimes the frontend team does it as well.

So those 2nd-tier branches depend on a 1st tier branch that isn't even completed. So the 1st tier branch could still change as time goes on, from new commits to changing the history of the branch.

That would cause conflicts to occur when the 2nd-tier branch tries to merge into master (after the 1st tier supposedly does) or when it rebases back onto the 1st tier branch.

It all just depends on the team's workflow, the team size, and the complexities of your tasks.

Ideally, nothing would get started that depends on changes that haven't been merged into master.

But in real-life messes happen, and having clear intent and purpose in the commit messages would be beneficial.

If you are spending hours on your commit message, maybe the work was too much for a single commit or you are overthinking the message.

 

I think there is one that you missed "format".

A commit should look like this (although the long description isn't always necessary)


Short title saying what commit does

Long description with as many characters as you want explaining why the commit was needed.

 

This is what I'd meant by descriptive. I'm implying the format of the git commit message.

 
 

Totally agree. That's why I shortened this article down to just "descriptive". I'm implying the commit message should be descriptive.

I just x-posted my larger article from Medium. Check it out dev.to/sublimegeek/check-out-these...

 

Yes and no. If your team's overarching strategy is fork-and-branch, you can still maintain your full commit-history in your fork.

 

Thank for this small an concise article about best practices on the commits usage

 

Love this. Particularly, I agree with the first three. The latter is definitely necessary when working with something like the master branch.

 

That's an incredibly inappropriate acronym.
Good on you!

Yes
But if your branch live for days, we may stay need to have multiple commit
I used to revert my own code before merging my PR

To continue on @beyrem_makh 's point, even if you know your commits will be squashed if you ever need to revert a commit, drop it, or edit it then having clear commit messages would be helpful.

For example, if I've been working on a branch for a few days, but another task is dependent on those changes before it can get started. Then another developer may branch off of my branch and start their work.

Some time later, my branch is squashed into master. But before the second branch can be squashed in, that author will need to know which commits were for one task and not the other.

If neither developer worked on both branches, than it's easier to tell.

But if either of them did or both tasks on worked on by the same person, then having a clear commit message is critical.

On my teams, we include the task number we are working on in the commit message. Always the first thing typed out.

 

It's applicable even in feature branch

 

Great recommendations, Johnathan.

Might I recommend "dad-if" or "if-dad" for the mnemonic.

 

Sharing to my whole team. Thank you.

 
 

I'm always doing "amends" to fix some erratas of the last commit... 😔