Regarding 1: A lot of people get quite far in their careers before they learn that squash merges are a thing. Sometimes people are afraid of polluting the history with a bunch of tiny commits, but...
Regarding 1:
Keep commits small enough that you wonder if you're taking this "keep commits small" thing a little too far.
A lot of people get quite far in their careers before they learn that squash merges are a thing. Sometimes people are afraid of polluting the history with a bunch of tiny commits, but you can keep the one line changes out of the final history by squashing the entire feature into one commit when merging.
I hate squash commits. I try to key my local history meaningful, with individual commits being small atomic units of work (added button, renamed variable, etc), each with a task ID heading the...
I hate squash commits. I try to key my local history meaningful, with individual commits being small atomic units of work (added button, renamed variable, etc), each with a task ID heading the commit message and a more detailed reason as well. Squash commits throw all of that information away and make it harder to understand why a particular line was added/modified using blame later on. I clean my history and frequently use amend and rebasing to keep it clean and logical.
They also break the ability for go t to know which branches are merged and which aren’t.
A single commit per ticket is not enough granularity.
I disagree (partially). It would be great if every developer would use good git hygiene and "tell a story" with their commit history in a meaningful and granular way. But the reality is that jobs...
I disagree (partially). It would be great if every developer would use good git hygiene and "tell a story" with their commit history in a meaningful and granular way. But the reality is that jobs are transient, and code bases can be large and long lived. I've found that using 1:1:1 issue/branch/commit with squashing enforced (or just encouraged as the default) keeps the history much more manageable long term while encouraging devs to manage their own branches however they see fit. It also (mostly) solves the "do I rebase" debate, because I could care less about how you keep your issue branch up to date with main as it all gets squashed to one neat commit in the end.
It depends on how you use the history, same thing with merge commits vs rebasing / fast forwarding. Also depends on what everyone in your teams does. In general I only move in the git history by...
It depends on how you use the history, same thing with merge commits vs rebasing / fast forwarding. Also depends on what everyone in your teams does.
In general I only move in the git history by ticket, as different team members have different concepts of granularity, of "every commit should be meaningful", of "every commit should pass tests", etc.
In our team we don't have squash commits set up, and we have no-ff by default, so just checking merges is the cleanest option for me. It is great when you need to revert 30% of a ticket and that 30% is a specific set of commits, but there are always hidden dependencies that make the reverts complicated. I'd rather try to keep that granularity in the software architecture than the git history (which is also very complicated for different reasons).
I think you hit on something important, in that a lot of this is team dependent. It's possible to use a lot of different strategies if you have some well documented procedures and team buy-in on...
I think you hit on something important, in that a lot of this is team dependent. It's possible to use a lot of different strategies if you have some well documented procedures and team buy-in on the use of those procedures. You could have agreement on the structure and content of commits to main, for example.
The original article is written from the perspective of bettering oneself as an individual developer, so a lot of this is out of scope. But working with a team and having some agreement on how that work proceeds is a big part of being a developer IMO.
I disagree. Local branches are just for me, and can be as untidy as is useful to me. Pushing to remote is where you rewrite your history so that it's comprehensible to someone else. My workflow is...
I disagree. Local branches are just for me, and can be as untidy as is useful to me. Pushing to remote is where you rewrite your history so that it's comprehensible to someone else.
My workflow is typically as follows:
1: Create a local feature branch and go hog wild like DeaconBlue, with scores of tiny commits. These may have very descriptive commit messages, or may be a stream of "ugh poking again". I'll do a lot of git reset --hard in this phase, as I realize I've gone on a goose chase.
2: Once I am certain I won't backtrack, I review my changes, squashing, rebasing, discarding, and rewriting commit messages as needed, so that everything starts with the task ID etc.
3: Push to remote. If the feature is complete, proceed, else go to step 1
4: Create the merge request, looking one more time over all the changes.
Like writing, the drafting and editing stages are very distinct to me.
[...] out of the final history by squashing the entire feature into one commit when merging
A single commit per ticket is not enough granularity.
Typically, teams I've been on do this on merge to the develop or main branch, while preserving the feature branch at least for some while. That way the production log has one entry per feature (good) but someone in the future trying to figure out what happened has the whole (tidied up) story.
Good point, most git platforms (gitlab, github) allow projects to have set this as a default on merge requests as well. Having it enabled encourages people like you said to do smaller commits and...
Good point, most git platforms (gitlab, github) allow projects to have set this as a default on merge requests as well.
Having it enabled encourages people like you said to do smaller commits and also keeps out pollution from people who already did but never squash their commits themselves.
I haven’t used it, but jujitsu (jj) seems like a nice alternative to git’s command line tool for people who want to work with a lot of small commits: https://steveklabnik.github.io/jujutsu-tutorial/
I haven’t used it, but jujitsu (jj) seems like a nice alternative to git’s command line tool for people who want to work with a lot of small commits:
Absolutely not. Perhaps there's a common, easily described sub-function that could get abstracted out and used by all 3 implementations. But don't just shoe-horn related logic together under the...
... It's better to have some wonky parameterization than it is to have multiple implementations of nearly the same thing. Improving the parameters will be easier than to consolidate four different implementations if this situation comes up again.
Absolutely not. Perhaps there's a common, easily described sub-function that could get abstracted out and used by all 3 implementations. But don't just shoe-horn related logic together under the same name.
Yeah, this one seems oddly absolute. I agree that it's always good to be looking for situations where code can be consolidated, but there are cases where code is diverging and some copy/paste is...
Yeah, this one seems oddly absolute. I agree that it's always good to be looking for situations where code can be consolidated, but there are cases where code is diverging and some copy/paste is required. I can think of lots of exceptions to this rule just in the projects I've worked on.
I don't think that I agree with every point. First of all such general statements often need word 'sometimes' on the start. Sometimes its ok to have a big commit. Why introduce complexity by...
I don't think that I agree with every point. First of all such general statements often need word 'sometimes' on the start.
Sometimes its ok to have a big commit. Why introduce complexity by forcefully split big logical commit into not-so usefull and maybe pointless small commits?
Sometimes its ok to not refactor every second commit. If you introduce a lot of changes sometime its ok to finish these changes and only then do a refactoring, when its become clear how system will evolve in next iteration.
Sometimes its ok to test framework. Frameworks and programming languages is not immutable and changes quite often. There could be bugs in framework, sudden changes of API, tricky side effects, undefined behavior, etc. etc.
Sometime its ok to have 3 or 4 separate functions with 99% similar functionality. Especially if these functions have slightly different usage, and you have feeling that these functions will evolve in a different ways.
#3 is one I stand by but also tends to be a point of contention wherever I work. Like, above all else, "does the thing do what it's expected to do reasonably well?" is the only question that...
#3 is one I stand by but also tends to be a point of contention wherever I work. Like, above all else, "does the thing do what it's expected to do reasonably well?" is the only question that matters, and challenging that is a tradeoff that introduces risk. Sometimes, the risk is worth taking on, but most of the time, when you desire to alter someone's working code, it should be triaged and treated as debt.
Regarding 1:
A lot of people get quite far in their careers before they learn that squash merges are a thing. Sometimes people are afraid of polluting the history with a bunch of tiny commits, but you can keep the one line changes out of the final history by squashing the entire feature into one commit when merging.
I hate squash commits. I try to key my local history meaningful, with individual commits being small atomic units of work (added button, renamed variable, etc), each with a task ID heading the commit message and a more detailed reason as well. Squash commits throw all of that information away and make it harder to understand why a particular line was added/modified using blame later on. I clean my history and frequently use amend and rebasing to keep it clean and logical.
They also break the ability for go t to know which branches are merged and which aren’t.
A single commit per ticket is not enough granularity.
I disagree (partially). It would be great if every developer would use good git hygiene and "tell a story" with their commit history in a meaningful and granular way. But the reality is that jobs are transient, and code bases can be large and long lived. I've found that using 1:1:1 issue/branch/commit with squashing enforced (or just encouraged as the default) keeps the history much more manageable long term while encouraging devs to manage their own branches however they see fit. It also (mostly) solves the "do I rebase" debate, because I could care less about how you keep your issue branch up to date with main as it all gets squashed to one neat commit in the end.
It depends on how you use the history, same thing with merge commits vs rebasing / fast forwarding. Also depends on what everyone in your teams does.
In general I only move in the git history by ticket, as different team members have different concepts of granularity, of "every commit should be meaningful", of "every commit should pass tests", etc.
In our team we don't have squash commits set up, and we have no-ff by default, so just checking merges is the cleanest option for me. It is great when you need to revert 30% of a ticket and that 30% is a specific set of commits, but there are always hidden dependencies that make the reverts complicated. I'd rather try to keep that granularity in the software architecture than the git history (which is also very complicated for different reasons).
I think you hit on something important, in that a lot of this is team dependent. It's possible to use a lot of different strategies if you have some well documented procedures and team buy-in on the use of those procedures. You could have agreement on the structure and content of commits to main, for example.
The original article is written from the perspective of bettering oneself as an individual developer, so a lot of this is out of scope. But working with a team and having some agreement on how that work proceeds is a big part of being a developer IMO.
I disagree. Local branches are just for me, and can be as untidy as is useful to me. Pushing to remote is where you rewrite your history so that it's comprehensible to someone else.
My workflow is typically as follows:
1: Create a local feature branch and go hog wild like DeaconBlue, with scores of tiny commits. These may have very descriptive commit messages, or may be a stream of "ugh poking again". I'll do a lot of
git reset --hard
in this phase, as I realize I've gone on a goose chase.2: Once I am certain I won't backtrack, I review my changes, squashing, rebasing, discarding, and rewriting commit messages as needed, so that everything starts with the task ID etc.
3: Push to remote. If the feature is complete, proceed, else go to step 1
4: Create the merge request, looking one more time over all the changes.
Like writing, the drafting and editing stages are very distinct to me.
Typically, teams I've been on do this on merge to the
develop
ormain
branch, while preserving the feature branch at least for some while. That way the production log has one entry per feature (good) but someone in the future trying to figure out what happened has the whole (tidied up) story.Good point, most git platforms (gitlab, github) allow projects to have set this as a default on merge requests as well.
Having it enabled encourages people like you said to do smaller commits and also keeps out pollution from people who already did but never squash their commits themselves.
I haven’t used it, but jujitsu (jj) seems like a nice alternative to git’s command line tool for people who want to work with a lot of small commits:
https://steveklabnik.github.io/jujutsu-tutorial/
Absolutely not. Perhaps there's a common, easily described sub-function that could get abstracted out and used by all 3 implementations. But don't just shoe-horn related logic together under the same name.
Yeah, this one seems oddly absolute. I agree that it's always good to be looking for situations where code can be consolidated, but there are cases where code is diverging and some copy/paste is required. I can think of lots of exceptions to this rule just in the projects I've worked on.
I don't think that I agree with every point. First of all such general statements often need word 'sometimes' on the start.
Sometimes its ok to have a big commit. Why introduce complexity by forcefully split big logical commit into not-so usefull and maybe pointless small commits?
Sometimes its ok to not refactor every second commit. If you introduce a lot of changes sometime its ok to finish these changes and only then do a refactoring, when its become clear how system will evolve in next iteration.
Sometimes its ok to test framework. Frameworks and programming languages is not immutable and changes quite often. There could be bugs in framework, sudden changes of API, tricky side effects, undefined behavior, etc. etc.
Sometime its ok to have 3 or 4 separate functions with 99% similar functionality. Especially if these functions have slightly different usage, and you have feeling that these functions will evolve in a different ways.
Upd: accidentally hit 'post' too soon.
#3 is one I stand by but also tends to be a point of contention wherever I work. Like, above all else, "does the thing do what it's expected to do reasonably well?" is the only question that matters, and challenging that is a tradeoff that introduces risk. Sometimes, the risk is worth taking on, but most of the time, when you desire to alter someone's working code, it should be triaged and treated as debt.