I truly believe that to have one commit on one issue is a good practice. I'm sure I read it somewhere in an article like “Best practices”.
As such, my workflow has been the following:
git checkout -b new-issue
.squash
the commits and rebase
them into current thematic branch.git revert
the commit, find the bug, fix it, and commit new patch into the thematic branch. I won't change the remote repository’s history.But today, I was surprised to hear the following workflow:
merge --no-ff
to merge the issue branch with thematic branch (so we’ll have “merge-commit” that we can revert
).git bisect
to find the bug.According to the 1st approach, we’ll have a clean git history, and no idea about overhead branches used during development.
According to the 2nd approach, we’ll have a very messy history, with a lot of ugly, unnecessary merges and commits for just one issue. However, we can use git bisect
to find bugs. (Perhaps this is better for refactoring?)
What pros and cons do you see for both approaches?
Which approach do you use, and why?
In practice, have you actually used git bisect
to find bugs? (I haven't…)
A good rule of thumb for Commit SizeEach commit should be able to fit in the working memory of a developer. It's challenging to place an ideal number on how large a commit should be, but each commit should fit in the working memory of a developer.
General Commit Message Guidelines As a general rule, your messages should start with a single line that's no more than about 50 characters and that describes the changeset concisely, followed by a blank line, followed by a more detailed explanation.
Small-scoped commits are so much easier to share with your team members than these large, sweeping changes to a system. By frequently pushing small commits, you're going to improve the visibility of your changes, you're going to reduce the potential for merge conflicts, and it's going to make integrating much easier.
In general, a commit should have one primary change it is effecting in the codebase. Consistency – When starting on an existing codebase, the best course of action is typically to follow the established patterns, at least for a while.
The second approach doesn't have to have a lot of ugly and unnecessary merges and commits. This is what I prefer to do:
--no-ff
into the parent branchThe above steps result in a history that looks like this:
* 354b644 Merge branch 'topic3'
|\
| * 54527e0 remove foo now that it is no longer used
| * 1ef3dad stop linking against foo
| * 7dfc7e5 wrap lines longer than 80 characters, no other changes
| * b45fbcf delete end-of-line whitespace, fix indendataion
|/
* db13612 Merge branch 'topic2'
|\
| * 961eebf unbreak build by adding a missing semicolon
|/
* a5b6b16 Merge branch 'topic1'
|\
... (more history not shown)
The above graph has all the same advantages of approach #1:
You can use the --first-parent
argument to git log
to get a concise summary that resembles what you would get with approach #1:
* 354b644 Merge branch 'topic3'
* db13612 Merge branch 'topic2'
* a5b6b16 Merge branch 'topic1'
... (more history not shown)
git diff 354b644^..354b644
will show you what was changed for topic #3.But you get benefits that approach #1 can't give you:
b45fbcf
and 7dfc7e5
(for the topic3
branch) introduce a lot of noise but no actual logic changes. Someone trying to answer the question, "What logic changes were made for topic #3?" might have a hard time digging through the noise if all of those commits were squashed into one.There is one disadvantage I can think of: It may be hard to configure your software development tools to only follow the first-parent path and ignore all of those intermediate commits. For example, there is no --first-parent
argument to git bisect
. Also, I'm not familiar enough with Jenkins to know how easy it is to configure it to prioritize building and testing the first-parent path over all the other commits.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With