Hacker News new | past | comments | ask | show | jobs | submit login
Please stop recommending Gitflow (georgestocker.com)
470 points by gortok on March 4, 2020 | hide | past | favorite | 396 comments



The approach that has worked best for me is pretty simple.

Master should always be in a deployable state.

Any commit that lands on master must include unit tests that demonstrate that it works (or at least doesn't break anything).

Incomplete features that will take more than a week or so to develop should be merged to master early protected by feature flags. This avoids long running branches.

I like squash commits: build a feature iteratively in a branch + pull requests, squash to master once it has the tests and documentation bundled together with the implementation. This gives you linear history.

People who prefer to review smaller commits can do so in the pull request - it's only the finished item that gets squashed into a larger commit.


The fact is that this doesn't work for everyone. What we found to be most workable is master is always "stable", then we have a production branch that is cut from master.

This gives you a few things:

1. More predictable deployments (let's deploy every week with X items)

2. Better rollbacks (rollback to last production deploy instead of guessing SHA1 of last acceptable state)


This is what I use tags for.

Master = deployable state (stable)

Every deploy gets a tag (named with its version). Rollback to previous tag in event of failure. Deployments remain just as predictable.


I've also seen people have a single development branch and multiple release branches (imagine a 'devel' branch that everyone merges into and a branch for each release that you have, and that potentially merges back into devel if you do hot patches). It's essentially the same thing -- branches aren't really all that different than tags in git.


Yeah its basically aesthetics. I do like keeping the release identifier separate from branches though just for categorization purposes (branches = development, tags = releases)


The method you describe is functionally the same and only nominally different.

It could be said:

> What we found to be most workable is develop is always "stable", then we deploy from master.


we deploy to test multiple times a day from out master branch after a Pull request is reviewed & merged. the deployment bundle (zip of the state of the code) that goes to test later goes to staging and production is the same across environments.

The only thing that changes is the configuration variables which are replaced at each env. This ensures predictable deployments and if we want to rollback we just pick the last release bundle and re-deploy that.


My snarky version of this is: ideally head of master is deployable (no regressions, yadda yadda). In reality, how many times has head master of master been broken and one had to rollback, and then had to scramble to find the rollback?

Gitflow is fine - better to use it (w/the girl extension) than spend time arguing branches.


Having to frequently rollback changes can be argued to mean that you don’t do enough testing before merging but are testing in production.


I agree with this approach. It worked beautifully for me at my last job. Keeping master ready to deploy is the way to go, along with squashed commits for a linear history. A side-effect of this approach is that it makes it super easy to bisect your way should a regression occur; something that I've found is impossible with GitFlow.


We use this work flow and it works masterfully. I have no need to save all my small commit - partly because I commit often to run tests in parallel on a CI server.

Does anyone know of a layer that you can use on top of git to make the occasional squash and rebase easier?

I know gitless is a helpful layer on top of git but not sure it support this specifically.


I mean, GitHub allows you to squash merge a PR, which is what we do at work. I find it really simple to summarize everything in the PR in that squash commit that ends up on master. I only rebase/squash locally if I need to make it easier for review


Can you still easily bisect to find which change broke a certain feature? I usually recommend against squashing because it's easier to identify an error in a 10-line diff than in a 2000-line diff.


We use the same method and yes, it's been usually easy enough to bisect from squashed commits. In mobile development it even helps, as bisecting effort is dominated by rebuilding time -- less commits makes for much faster bisecting.


The problem is more the state of the rebased commits. If it is a dirty history, bisect will break for a myriad of random reasons (broken tests, incomplete features etc.). So if you don't squash, and want to run bisect, you _have_ to ensure that all commits are clean and working. Easier said that done.


> So if you don't squash, and want to run bisect, you _have_ to ensure that all commits are clean and working. Easier said that done.

It can be done if the commits are separate logical units of work rather than work in progress commits. One approach I take is to take the diff between your branch and master, make a new branch based off of master and stage parts of that diff (parts that make a logical unit of work) and make a commit out of it. You can do that with commands like git apply with the relevant diff hunks.

Then, to test whether each commit works, you can run git rebase --exec "your-test-commands" master and it will apply each commit in your branch and run the test commands. If they fail, then you can fix the issue in that commit and run git rebase --continue to move onto the next commit.

This way, you can get a logical set of commits where each of them are in a working state.


Can the PR still show all the individual commits when you come back to it? If so, are they still in the repo with some keepalive tag or are they persisted outside of the repo along with the PR-comments/metadata?


Yes, GitHub keeps the separate commits visible in the closed PRs. For what it's worth, I never ever found those useful anyway and worked just with the squashed commits. It's up to the team to make sure the squashed commits don't grow to thousands of lines in size.


> Keeping master ready to deploy is the way to go

Are there really teams that don't do this?


Any software that has more than a few dozen developers and has tests that run for more than a few minutes necessarily will have broken master states at least occasionally. Otherwise you slow everybody down because merging to master can only happen (working hours/test run time) times per day.


And not all projects produce a single output from their source code.

I've worked on multiple projects over the years that had mostly a common code base but had to build and release to support different hardware platforms. In some cases, these needed significant but permanent divergence between the code for one platform and another.

In this sort of environment, concepts like "master branch" and "continuous deployment" have no meaning, and neither does a principle like always being ready to deploy. Your whole approach to things like branches, merging, rebasing, cherry-picking, bug tracking, and what a version number means is probably different to, say, deploying a long-running web application with frequent updates and all users running the same code.


If you're using gitflow, you've got a separate develop branch that everybody merges to, while master remains stable. Only when develop works and passes its tests, can it be merged to master.

And with multiple teams sharing the same code base, breaking master is going to slow a lot of people down a lot more.

I have little experience with multiple teams working on a single code base, but it sounds like a recipe for disaster no matter how you organise that. Make your code as modular as possible, so each team can take responsibility for their own code.


Openstack has the issue of long tests (nova takes two hours: https://review.opendev.org/#/c/711277/ ), but they still keep a green master. I don't think it's a problem in itself. The question is - why is anyone slowed down by merging? Why are people waiting for that action to happen?

Merging can happen more than the (simple calculation) times a day though. What openstack does is check each branch on its own, but then tries to merge all a number of waiting PRs at one time. If they pass on fake master again, that's merged into real master.


Github's interface is a big part of the issue (only for those using Github, of course).

Say you branch off of master, make some changes, and make a PR. Call this branch A.

Then you want to make a new branch, that depends on branch A. Call this branch B. The PR for branch A is still waiting to merge when you finish with branch B.

If you make a PR to merge branch B to master, you get a diff showing both PRs worth of changes. That's annoying to review.

If you make a PR to merge branch B to branch A, you get the correct diff for B. Huzzah! But if branch A's PR then merges, Github deletes the branch B PR and any comments/review. You have to remember to retarget branch B PR to master, then merge branch A.


It is interesting how in such a case there is an increasing blur between git and blockchain.


I work on an embedded device. Our tests include power analysis testing in LTE/GPS simulators of all the sleep/wake states. Total testing of a release takes about two weeks.

Trying to keep master consistent without git-flow means we get PRs sitting around for a month and merge-conflict hell days when we merge everything to make a build to send to QA.

Git-flow is bad if you have CD. Git-flow is great if you can't do that.


The part that's most contentious about git-flow is release branches.

You still should merge often to develop, which when tested becomes master.


The issue there is that you typically find some issues in testing, and need to fix them. That's why you test. You can either have a release branch and a develop branch, cherry-picking/merging fixes into the release branch as well as develop, or you can just hold off on merging any "not-for-this-release" branches for a few weeks. Just let the PR sit there. Alone. In the dark.


Amazon FreeRTOS.

Master is a working branch. Releases are pushed to Release and tagged with a date.


Master should always be in a deployable state.

Isn't that what the develop branch in Git Flow is for? Your project might need some additional steps as part of its release process, and those can take place using the corresponding release branch, but the idea is that you can start a new release off develop whenever you want.

I like squash commits: build a feature iteratively in a branch + pull requests, squash to master once it has the tests and documentation bundled together with the implementation. This gives you linear history.

Indeed. We're talking about using Git. Even if you're using a Git Flow style of workflow, why wouldn't you regularly rebase any feature branches back onto develop if they're lasting more than a short time, so you are keeping up-to-date, and why wouldn't you squash the finished feature down to a single commit that can be fast forwarded onto develop when it's ready, to maintain a linear history with one commit for one significant feature or fix?

The only time I've seen non-FF merges in a Git Flow style project is for bug fixes on release branches, but those tend to be characterised by two key properties: they are relatively simple and relatively important. If it's not simple, you probably need to abort that release and go back to your main development line to do whatever more substantial work is required before continuing, then make a new release. If it's not important, you probably ship that release with the known bug, and the fix gets made on the "develop" branch via the usual process for incorporation in future releases. So the effort involved in merging bug fixes from release branches back to develop is typically both relatively small and well justified, and if you do need any manual conflict resolution, that implies divergence since the point when the release branch was started and then it probably is a good idea for someone to take a more careful look and make sure the bug is properly fixed on develop as well.


> Isn't that what the develop branch in Git Flow is for? Your project might need some additional steps as part of its release process, and those can take place using the corresponding release branch, but the idea is that you can start a new release off develop whenever you want.

In gitflow, “develop” is master. The master branch serves no real purpose.

The intent of “master is always deplorable” is really that every commit, on every branch is deployable.

To me, this is the whole point of CI. Commits need to be restructured in such a way that everything works incrementally, otherwise you’re asking for trouble — how do you even bisect problems?


> In gitflow, “develop” is master. The master branch serves no real purpose.

Yes, it serves a real purpose.

Develop is technically deployable (in CI/CD/CD terms it is delivered but not deployed), Master is that plus socially deployable, and hence actually deployed.

Now, some organizations don't have social controls on deployment that have timelines that necessitate a separation between those things plus release branches to support getting the technically deployable to be socially deployable, but some do, and those are often outside the control of the development organization, potentially the IT organization, and sometimes even the whole organization as they may be external controls.


I'm a fan of "Master is production" , so as soon as a commit is merged into Master it's deployed immediately. That way everyone always has access to what exactly is in production and helps enforce the mindset that anything merged into Master needs to be 100% ready to go.


Master should be the known-good version. Not the version you're deploying right now, but the version you deployed yesterday from a release branch and nobody screamed. That makes it much less likely that your teammates will rebase onto a bad commit that needs to be rolled back in prod.


Some organisations don't have this kind of deployment at all. Most software is not web apps running on servers under the control of the development team with a single version in production at any given time.


Not far from my experience. I advocate preserving history, and wisdom of requiring unit tests for everything depends on project properties.

I think this is a good treatment about why rebasing is bad: https://medium.com/@fredrikmorken/why-you-should-stop-using-...


> I like squash commits: build a feature iteratively in a branch + pull requests, squash to master once it has the tests and documentation bundled together with the implementation. This gives you linear history

I disagree with squashing commits when merging to master, here's why:

Your commit history is what you will use later down the line (think 6 months, a year, 2 years later) to find a very nasty yet incredibly subtle bug in your application.

Imagine said bug was introduced by a feature that took about 1 full month to code, introducing 1000 lines and removing 500.

That feature was probably made of many small commits (say, 25 commits total), on a feature branch called "super-feature".

If your developer cared at all about making "atomic" meaningful commits, each one of those commit will be introducing a small very well defined subset of the entire "super-feature".

The "super-feature" will in the end be the result of all of those commits together.

Squashing that history of 25 commits, will create one giant commit of 1500 changes. Have fun finding where in those 1500 changes that little nasty bug was introduced!

Instead, if you hadn't squashed, you simply use `git bisect` and find in no time that the bug was introduced by the 12th commit of that branch, which itself was a small commit of 1 little change:

  diff --git a/statistics.php b/statistics.php
  index 69e435aa9f..46666daa93 100644
  --- a/statistics.php
  +++ b/statistics.php
  @@ -8,7 +8,7 @@

  - a = b + c
  + a = b - c

If you want a very linear history, enforce a:

  git rebase master super-feature
  git merge super-feature --no-ff
This will rebase your branch on top of master, but, when merging, the `--no-ff` option will tell git to create a merge commit. This merge commit will make it very clear that the code branched off and was later merged back into master, yet, it will keep the history linear.

=======

TL;DR: Your commit history is as important as your code! Don't squash it away! I wish github never introduced that feature and instead developers were taught how to create a meaningful history of commits.

=======


> Squashing that history of 25 commits, will create one giant

> commit of 1500 changes. Have fun finding where in those

> 1500 changes that little nasty bug was introduced!

100% agree, the only purpose of this is to make "pretty graphs" at the cost of destroying the history of the code changes.

I prefer to write small commits with simple messages that explain exactly what that change does. Sometimes it costs some time in Git Gui selecting the lines that make up the commit, but ultimately it's worth it.


I notice that some people seem to care more about pretty history than about useful history. I'm not a big fan of rebasing for this very reason: it changes history, which can misinform about what was really done. It can introduce bugs in commits that didn't initially contain them.

Though I will say that git has one big glaring problem with merge commits: they are effectively black boxes, and when you've have a big merge conflict, many git tools don't tell you how that was resolved in that merge. I recently came across a merge that wiped out some of my changes with no clear reason why. I redid that merge and there was no merge conflict at all, so it's still a mystery what happened there.

Still, rebasing has caused far more problems in my experience. It's only suitable for trivial, local cases and never on commits that have already been shared (pushed).


I think a lot of the fear of rebasing comes from not fully grokking what it does, and not rebasing often enough.

My workflow typically involves rebasing my local branches on master every time I switch a branch or start a work session, so typically conflicts that need to be resolved are very clear and small things (e.g. someone else adds a case to a switch statement in the same place your branch does).

The biggest benefit to this that I see is that your diffs are then much more clear in what they're doing, and you have the option to update the commit message to reflect the conflict resolution if required.

That said, if you have a big feature branch and try to rebase it after a month vacation, you're gonna have a bad time.


Daily local rebase on master is really the only acceptable use I know for rebasing. It's not just after a month of vacation, but simply when your local branch has a lot of commits, that rebasing can become very painful, as you may have to resolve the same conflict over and over and over again. That must be the circle of hell where programmers end up.

Basically you'd have to rebase after every single commit. That would keep it the most painless, I guess. But I can also just do a single merge and I'm done.


Git-rerere and smart merge tool like p4merge help a lot with this rebasing problem.

These are essential if you work with forced rebase mode like in Gerrit. (ugh)

You still have to be moderately careful and read the code again.


> Though I will say that git has one big glaring problem

> with merge commits: they are effectively black boxes, and

> when you've have a big merge conflict, many git tools

> don't tell you how that was resolved in that merge. I

> recently came across a merge that wiped out some of my

> changes with no clear reason why. I redid that merge and

> there was no merge conflict at all, so it's still a

> mystery what happened there.

Hmm, I don't think a merge magically removes the need for testing. If I build a feature A that replaces feature B, and another programmer builds a feature C that relies on some aspect of feature B, when merging, although it will merge and compile fine, it's possible it is still broken.

This is a good argument for well defined interfaces, but it's possible to see how a successful merge may not produce a successful source.

As for things disappearing, I think there is some edge case where it looks like one branch deleted something after you made some change to it. Generally it's best to not have people working on exactly the same part of code. On the plus side, you have a full working history, therefore can recover :)


There's more to it than just knowing it's broken. I want to know why and how it happened. Of stuff needs to be tested after a merge; that's how we discovered there was something wrong in the first place. But I don't want to redo the work that I already did; I want the merge to be correct. In this case, it seems the merge did something it should never have done, and it was not clear why this happened.

Still, the commits that were merged both still existed, to it was easy to redo the merge. I shudder to think what would have happened if this had been a rebase; then the original commit might have been gone.


> I prefer to write small commits with simple messages that explain exactly what that change does.

One thing people should include in commit messages is why the change is being made. In other words, what's the purpose of the change. That can provide a lot of context that can be lost over time. So my commit messages include both what was done and why it was done. The why part can range from things like this method is needed for this feature that does something, or this fixes a bug that was causing this issue, or this other feature is no longer needed in context of this other reason, etc.


> One thing people should include in commit messages is why

> the change is being made.

In a perfect world, there would be a massive document with each commit, specifying exactly how it behaves, all the possible edge cases considered, why it was implemented, other implementation choices and a justification for why this one was selected, etc, etc.

I think the reality of the matter boils down to a few points:

1. Commits are not read that often and if they are, it's usually recent history. If they are being read very much in the future, then the "why" may not even make sense either.

2. Unbounded limits on commit message lengths leads to some people waffling in their commits. If you can't explain it in 80 characters, you should very much consider breaking it down.

3. Usually it's not possible to isolate a commit from the context of the commits around it.

4. Usually it's not possible to understand a commit without the larger context of the system.

Generally if some implementation requires an explanation, it can be better understood as a comment next to the implementation in question.


> 1. Commits are not read that often and if they are, it's usually recent history. If they are being read very much in the future, then the "why" may not even make sense either.

It depends. If you're looking at the commit log directly, then you would tend to look at recent history, but if you're using something like git blame, you can easily come across commits that are far less recent.

For example, part of your change is to update a line of code in a file. You run git blame and see the commit that introduced that line of code, run git show for that sha1 and see the context around why that line was introduced (e.g., a bug fix). If you don't look at the history, you could easily introduce a regression in the code without realizing it. In other words, the VCS history can be used as a tool to help determine what potential effects a change could have.

> 2. Unbounded limits on commit message lengths leads to some people waffling in their commits. If you can't explain it in 80 characters, you should very much consider breaking it down.

There's no restriction on how long a commit message should be. Convention from the Linux kernel and git itself says the title should be limited to 50 characters and the body should be wrapped at 72 characters (other than things like links and error messages/log lines). There's no restriction on how long a commit message should be.

As for whether to split the commit up, that really depends on whether you're doing multiple things in a single commit, which may or may not have a relation to how long the commit message is. Typically, if you see something like "do A and do B", then that's a sign that the commit should be split into separate ones where one does thing A and the other does thing B.

> Usually it's not possible to isolate a commit from the context of the commits around it.

If you use a combination of git blame and git log, you can get that context. git has a feature that can be used with git log where you can search for whether a string/line was added or removed in a range of commits with the -S flag, or if the number of instances of a string/line changed with the -G flag.

> Usually it's not possible to understand a commit without the larger context of the system.

That's true for people who are new to the project, but using something git blame can help people new to the project find the person/people to ask about why that code was written the way it was.

> Generally if some implementation requires an explanation, it can be better understood as a comment next to the implementation in question.

That largely depends on whether people are diligent about updating the comments when they update the code. If you use git blame and look at the associated commit message for that line of code, it will always be up to date. If you make a change to that line of code, then the new commit message would be associated with that line. In fact, a tell tale sign that a code comment may be out-of-date is if the git blame output shows different commits for the comment and the associated line(s) of code.


What can also help is linking to the related Jira ticket (or whichever issue tracking system you use).

And separate "cleanup" commits from commits that make meaningful changes. A commit that changes every line in a file because it's fixing incorrect indenting is fine. A commit that does that but also introduces a small functional change, makes that change invisible.


We've actually changed tracking systems over the years (meaning that the link in the commit message is no longer valid). So, it's best to include both the link and the actual text.

> A commit that does that but also introduces a small functional change, makes that change invisible.

While it's sometimes possible to separate the actual change from the formatting change by ignoring whitespace if the diff, it's definitely better to make each type of change in a separate commit.


Absolutely. I don't recommend replacing the text with the Jira ticket number. List them both. Both are useful.


Agree with the thoughts on atomic and meaningful commits (though would drop the merge commit entirely), and I will rebase my commit history extensively at times to get it into a presentable state along the lines listed here.

Bottom line: the history, content, and message of each commit, in addition to the resultant repo state, are all artifacts of your work and are worth shaping and crafting in order to optimize for communicating to yourself and your fellow developers in the future.


> though would drop the merge commit entirely

The merge commit is important though. It helps delimiting a set of commits that have meaning all together from the rest of your commit history. Here's an example:

  a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> k 
In the history above, did you notice that commits c through h are actually related to one another? They were introduced a while ago by a feature branch.

Compare the previous history to this:

          c -> d -> e -> f -> g -> h 
  a -> b /                          \ i -> j -> k 
Now that's very clear that c through h are part of the same line of work. All those commits were probably part of 1 single pull request and were reviewed in the context of one another.

My history here is still very linear. Yes, there's 1 little branching off, but that's all.

At my previous company we had a codebase of well over 20k commits entirely made of those little branches branching off master, then merging back in. It made it for an incredibly readable history.

An other added advantage of not removing the merge commit is that you can revert the entire merge of your branch in one go.

Without the merge commit, you need to remove each commit one by one (and that's granted you remember which commit was the first one and which one was the last one).


Agreed your history is still linear. But I would suggest your merge commits are perhaps more trouble than they are worth. In commit sequences we prefix almost all commit subjects with the JIRA ticket ID, so the sequenced tickets are quite clearly marked as being related to each other without incurring the merge commit cost.

As for reverting merge commits, I'd rather not personally have to remember the rules as to how to re-apply after the revert (revert the merge revert, then merge the branch again)[1], let alone working on spreading that knowledge losslessly across the organization. This is admittedly something I haven't done much of, but I'd rather revert ~3-9 individual commits, squash the reversion commits (usually with a bit more context on why we're reverting), and push. And it could be scripted. I haven't made a script for it as it doesn't happen that often, but between using rev-parse and a sed script in the GIT_EDITOR environment variable it would be pretty quick.

Having merge commits also means that you have to teach everyone how to view diffs of merge commits, which admittedly isn't that hard, but it is still just one more barrier to know/teach/overcome, and the default behavior of showing a zero-line diff for a no-op merge commit doesn't help.

[1] https://mirrors.edge.kernel.org/pub/software/scm/git/docs/ho...


> Your commit history is what you will use later down the line (think 6 months, a year, 2 years later) to find a very nasty yet incredibly subtle bug in your application.

No, it won’t. If I didn’t catch it then, I will have to read the code and understand it and spot it.

At work, where my builds take anywhere from 30 minutes to 3 hours depending on the target, your git bisect on 1500 commits represents days of building and testing. Or I could just read the code again.

Commit messages should provide context for going through the process of rereading the code, and little else. If a commit message literally describes what the code does (“change constant from X to Y”), it’s self-describing and unnecessary.


It's okay if you really prefer to read a huge squashed commit instead of an atomic commit in a branch, but you made it sound like squashing was necessary in order to reduce building and testing. That's wrong. You can use `git bisect` on merge commits only, then, once the faulty merge is detected, bisect the commits of the merged branch. Bisecting merges won't take more time than bisecting squashed branches.


But can't you just go back and reopen that branch? Just because you squash-merge the branch to master doesn't mean you can't keep the branch archived. I have seen where you basically just "rename" the branch (i.e.: make a new branch of the branch and just leave it as-is) to "merged-2020-03-03-BRANCHNAME" or something and then you don't touch it. Meanwhile, your master is a neat and tidy history of features added, tickets finished, and bugs fixed without all of the "fixed the thing I forgot to add" messages that inevitably sneak their way into your git log.


I think you are being down voted because people are confused about what you are saying.

In git, a "branch" is just a tag on a particular commit. What we intuitively think of as a branch is the set of commits starting from where we diverged on master and ending up with the commit that is tagged with the branch name. There is nothing in git that actually stores that, though. When you rebase the branch, squashing commits, you create a new commit that is that sum of all the other commits. The other commits are left unreachable by any other branch and will be removed the next time you GC the database.

What you are saying (I think) is to add a new branch called "merged-2020-03-03-BRANCHNAME" that points to the same commit as "BRANCHNAME" and then rebase BRANCHNAME, squashing the commits. In that way the old commits are still reachable and won't be removed.

The downside, of course, is that it's a bit of a PITA if you want to find out what commit changed a line of code and why. You would narrow it down to "BRANCHNAME" and then you have to search for the archived version of it, switch to it and then continue looking at your code. But, in principle it could be done.


Yes, that's basically what I'm saying, but I see now it could be a pain with a big repo, so probably not the greatest idea. I do still like a squashed master branch, but I suppose if you trained your developers well enough (ha-ha) they could just commit good messages, however I know several awesome developers who, from time to time, commit a "fixed the thing again" message here and there.


without all of the "fixed the thing I forgot to add" messages

This is why you use rebase --interactive or other ways of amending commits before submitting a PR. It is not an argument for commit squashing.


> rebase --interactive

like 90% of developers (maybe 95%) don't even know how to rebase from master before they submit a PR, so I doubt, highly, that they'll learn how to do a `git rebase -i` in order to make things cleaner. I've personally never seen a repo that utilizes all these "fancy" git commands (fancy to those developers) to make a clean history, I've always seen the messiest shit. Maybe that's sad for me in my career, though.

I agree with you this is the best technical way, though, after reading some more comments in here, but not the best way for everyday developers.


You have made a case for keeping the work-in-progress commits.

The case the other way is that what should matter at the end of the feature development is the visible contribution it makes to the overall project, i.e., the finished code, tests, etc.

This has two fundamental consequences. Firstly, in this model, your final commit that goes onto whatever you call your primary development branch should be a complete, self-contained piece of work, like adding a new feature or fixing some specific piece of broken behaviour. It should include any related tests, data files, etc. It should be something that can be reviewed in isolation and make sense. It should ideally be something that has minimal interactions with any other code that aren't strictly necessary, so it can be removed or replaced later if necessary with minimal effort to resolve or rewrite anything else that has happened since.

Secondly, requiring all work-in-progress commits to also be meaningful and live forever in your shared repo significantly limits the usefulness of source control for the developer during the development stage. They might be experimenting with different approaches but want to keep track of a version where some specific aspect of the code is currently working. They might be about to refactor to clean some new code up but want to make sure they can't lose the exact version they had just got working due to unintended side effects. They don't want to be getting every individual commit code-reviewed or run any slow testing processes, just in case someone later comes along and cherry-picks that particular point in history to use as the basis for something else.

So I will respectfully disagree with you that the full commit history at this level is as important as the final code. My reason for that is that the final code is complete, tested, reviewed, and going into the finished product, while anything in the work-in-progress commits that didn't survive to that point was determined, by the developer doing all of that work, at the time, with the aid of all of the testing and reviews and tools that were involved, not to be as valuable as the final version.

If there really were interesting alternatives considered during development that might be useful to keep around for later reference, you can always tag or branch at that specific point and give it a meaningful name. However, I would suggest that this is like keeping issues open in your bug tracker when you know you'll never fix them: unless you also have some process for keeping track of such ideas and why they are valuable to refer back to them and take some useful action later, you're better off not having them clutter your main development process and tools forever.


I'm actually not arguing the work-in-progess commits should be kept. On the contrary, I do agree that work-in-progress commits are useless, provide no value and should never be submitted for review in a pull request.

I think I should have been more explicit when saying "your commit history is as important as your code". Because I definitely agree that your commit history, representing all the thought process you went through while developing whatever you're coding, does not have its place in the history, and I, as a colleague and reviewer of your code, couldn't care less about it.

I make a distinction between "WIP commits" and "atomic" commits that have well defined boundaries.

While developers work on their feature branches, they should be free of committing however they want, making "WIPs" commits left and right, adding code in one commit, removing that same code in a subsequent one, refactor, type terrible commit messages with no meaning, or not committing at all even.

However, once a feature is ready to be reviewed (and then released), I'm arguing that developers, before submitting their work, should re-organize their commit history: remove WIPs commits, remove refactors that happened during development, squash distinct commits that are part of the same logic, amend commit messages, etc.

The final commit history shouldn't be a series of commits that represent everything that went through the mind of the dev who coded the feature: that's not what I'm arguing for.

The final commit history should simply be multiple small digestible and reviewable chunks of work, that make sense on their own and in the context of the pull request they are part of. Those commits should represent the code in its final, best, most well thought out form.

And once you've gone through the trouble of organizing your work into meaningful, atomic, digestible, chunks of work, with appropriate descriptive commit messages, there's no reason not to merge that work.

As it was useful to the reviewer of your code during the pull-request review, it will also be useful many months later, when you need to review that same old code that seems to be having a bug or to understand why it was written the way it is.

Well, at least, IMO.


> However, once a feature is ready to be reviewed (and then released), I'm arguing that developers, before submitting their work, should re-organize their commit history: remove WIPs commits, remove refactors that happened during development, squash distinct commits that are part of the same logic, amend commit messages, etc.

I agree that this is the optimal solution but isn't this a hefty piece of work that even might introduce subtle bugs?

Eg. I'm thinking of the overall feature needs work to be done in two separate "areas" of the code base and your wip-commits are touching these two areas "separately". Then when you are finished, reordering and squashing the wip-commits into two atomic commits seem like an obvious thing to do. But in a minority of commits you change that tiny little shared part back and forth, from both contexts. Finally you figure out how the shared part should work in the final wip. When you try to reorder the commits the changes in the shared part changes order with leads to merge conflicts when rebasing/reordering/squashing that needs to be solved.

So I'm on the fence of doing that extra work which in practicality could involve an unknown amount of "made up work" to get the clean atomic commits. And those commits then risk being a lie since the reordering, like the shared part in my contrived example, could have made that the first "atomic commit" that never existed also has a bug that never existed that someone down the line potentially stumbles upon when bisecting.


> Finally you figure out how the shared part should work in the final wip. When you try to reorder the commits the changes in the shared part changes order with leads to merge conflicts when rebasing/reordering/squashing that needs to be solved.

An alternative approach is to create a new branch off of master (or whatever the base branch is) and take the diff of the HEAD of your branch with your work in progress commits and stage the hunks of the diff to make a commit that contains a logical change (and repeat the process for the remaining parts of the diff). The git apply command us very useful in this regard.


> [...] unit tests that demonstrate that it works

I'm going on a bit of a tangent here, but unit tests don't demonstrate that it works – acceptance tests demonstrate that it works. Unit tests help reduce the time to narrow down problems and in some cases help with refactoring (if refactoring is limited to the inside of methods).


> Master should always be in a deployable state.

+1 to this and squashing commits. I'd even go far as saying trunk based deployment model over trunk based development.

This can be done via Pull requests or short lived branches. I prefer having all the context, discussions and testing added to the pull request. Even setting up environment per pull request. It's much easier to track and get feedback.


> Master should always be in a deployable state.

I think the head of master should always be deploy-able, but ultimately it seems better to make use of tagging of specific hashes which realize some degree of reliability for field use.

Obviously you have your unit tests and other kinds of tests, but ultimately nothing beats just using it. If it "feels" reliable (which can be hard to quantify sometimes) or has proven itself (you've been using it without issue for some time), then it's probably worth tagging for that fact.

> Incomplete features that will take more than a week or so

> to develop should be merged to master early protected by

> feature flags. This avoids long running branches.

I prefer to properly scope the time/effort required to complete a feature, rather than having massive features. If a feature is taking more than a few weeks to bring into fruition (assuming ~1 dev per branch), then it's probably too large of a feature and needs to be broken down. A properly sized feature should probably fit within a sprint cycle.


I prefer to properly scope the time/effort required to complete a feature, rather than having massive features. If a feature is taking more than a few weeks to bring into fruition (assuming ~1 dev per branch), then it's probably too large of a feature and needs to be broken down. A properly sized feature should probably fit within a sprint cycle.

Ideally, I agree, though this does depend on what your business requirements are. Some features are just fundamentally complicated, and it might not be possible to implement the feature itself, or even decompose it into meaningful parts that can be implemented, in one short round of work. Fortunately, that doesn't happen very often for most types of development work!


> Some features are just fundamentally complicated, [..]

> Fortunately, that doesn't happen very often for most types

> of development work!

Every problem should in theory have some of breaking it down, otherwise we have no hope in programming it. We should be able to break it down far enough that it can be reasonably sanity checked. If the feature is too complicated then you're asking for bugs.

From an objective/goal stand point, we also need some way to measure our progress and be able to evaluate whether a pivot is required. It's very easy to get stuck in a problem for a long time if we have no external measure.


Every problem should in theory have some of breaking it down

I don't see why we should expect that to be the case. For example, I once had to implement an algorithm to visualise a certain set of data using a certain type of diagram, where there were a lot of technical factors that determined exactly how that diagram should be drawn. IIRC, it took a few weeks to implement that algorithm, but there weren't any natural breaks where you could, say, draw only half of the data set or half of the diagram features and get a meaningful result. It was inherently a relatively complicated, all-or-nothing feature.


It's hard to argue a feature I haven't seen, but just the fact you are able to program the feature means you were somehow able to break it up into a series of steps. Okay, breaking it down wouldn't produce something workable in itself, but it should produce something measurable, testable even.

For example:

Step 1 : Extract relevant data

Step 2 : Massage data into the correct format for graphing

Step 3 : Graphing tool visualization for simple example data set

Step 4 : Write tests for graphing/visualization

Step 5 : Plug real data into graphing tool


Yes, it was implemented in a series of steps, but the cumulative result of those steps had zero business value and was not something that would ever have been released to customers until the final step was done.


Yes yes yes. Different teams have different needs, of course, but what you described should be considered the default workflow.


I personally don't squash commits and loose information. I see the value in having a compressed view of the log history, but in that case I use

  git log --simplify-by-decoration


I feel really good when I completely destroy my application and then roll back to the previous working version. It's such a thrill, man.


This cycle seems to repeat endlessly in our industry:

* Early adopter independently comes up with an idea that works well for his/her team.

* Writes an innocent well-written article or book.

* Idea somehow becomes dogmatic. Even for situations that don't make sense.

* After some time, people rebel against dogma because they realize it doesn't make sense for every situation.

* Original idea now considered anti-pattern even though it may still make sense for some people or situations.


Our industry? I think the problem goes well beyond any industry; it's just a human thing.

    1. See success
    2. Want success
    3. Copy behaviors
    4. Behaviors were only part of success


This process reminds me of "Considered Harmful Considered Harmful": https://meyerweb.com/eric/comment/chech.html


It's turtles all the way down, my friend.


My takeaway from this is that more articles should include a 'this works in x situation, probably also works in y, but is not suitable for a, b, c' discussion. Applying the right techniques in the right places is an important part of engineering - some authors talk about it more than others and it's very useful.


Thats a funny way to spell Agile.


My mind was blown when I found out that daily standups were a Kanban philosophy and not a SCRUM philosophy. Only because every SCRUM team I have been on has standups and it is essential, as long as they are less than 1 minute long.


Also not in XP. I eventually came to like standup meetings, but with one caveat: no management (including project management) is allowed to attend. Otherwise it becomes a status meeting rather than a "How can we get past our current blockers" meeting. Management glazes over all the technical talk -- the very thing that we need to speak about if we are to need a meeting at all.

I'm going to say this rather rashly (and maybe regret it sometime in the future): any "Agile" process that includes a status meeting for developers is broken. Status should be determinable from the artifacts that have been produced. If management can not see or understand the artifacts, then your process is broken. If they prefer not to look at the artifacts, then your management is broken. If they need status on something that is "taking too long" then you haven't broken up your tasks enough. If you have broken them up enough and it's still "taking too long", then there really isn't much to say other than "this blew up on us" -- which is obvious.

What a standup is good for is casually checking with your teammates that the approach you are taking is reasonable and to discuss other strategies. It's useful for asking for help, swapping pairs (or getting one in the first place). If you find yourself doing the "This is what I did yesterday. This is what I'm going to do today" dance, then you are not getting much value. It should be blindingly obvious what you did yesterday, because code was written, reviewed and hopefully merged. Possibly you may want to say, "Hey, can you review my code?" It should be blindingly obvious what you are going to do today, because you grabbed a card/task from the backlog. The only thing you need to talk about is, "I'm confused. How do I do this?" and the like.

/end rant ;-)


> status should be determinable from the artefacts that have been produced.

Hell yes! Status meeting can be a band aid while you get people to properly document and communicate those though. You often don’t need the whole team for that though.


I still remember this standup I once had. 8 people, under 2 minutes. It was a dream.


Did it help the work vs not having it in the first place?


Yeah, I think so.


I had a 45-minute standup the other day.


Ridiculous. I would have left the meeting. Just walked away to my desk and started coding after 3 minutes


medium.com articles define the software development practices of most companies.

It's a bit of a joke.


Yup. It's a byproduct of software not being grounded in anything compared to, say, every other engineering discipline. They all have the laws of physics. Software has...a bunch of different mantras and paradigms, and for every one out there, there's one that is its direct opposite.


> It's a byproduct of software not being grounded in anything compared to, say, every other engineering discipline. They all have the laws of physics.

I think experimental evidence helps, but if you look at the physical sciences more closely, you'll find tons of similar narratives. This is a fairly universal human behavior unfortunately. Nonsense becomes "canonized" as I put it, and then continues to be repeated by people until someone finally looks into it and realizes it's nonsense and publishes an article saying so. But unfortunately that's not always the end of the nonsense. Even things which have been debunked can continue to be cited and used. One great example (not engineering but the point stands):

https://www.youtube.com/watch?v=WRoe3xXFtmM

> In one sense we won. We got our points across. People now understand that this research is wrong. But this 2005 article by Fredrickson and Losada gets citations today at 3 times the rate of our article that demonstrated that it was just utter rubbish.

(Note that I'm using "nonsense" as an example here, but the top level comment was more general in that it mentions practices which apply in some situations erroneously being applied for all situations.)


Even things that have science are largely ignored - one I always shake my head at - oracles for ethereum. Proved many years ago - there can't be an oracle. Ethereum developers - we'll build oracles so the transactions are guaranteed.

No doubt there will be arguments for how oracles for ethereum are different, and so on


The author says:

> While I hesitate to say “Worrying about merge conflicts” is a valid reason not to pursue a branching strategy like gitflow

I do not hesitate! This is _very good_ reason to avoid gitflow.

Merge conflicts by definition require human intervention. And human intervention means an opportunity for human error. Resolving merge conflicts properly ranges from trivial to nearly impossible, depending on the specific changes you're trying to merge.

Often none of the authors involved in the conflict are in a good position to understand why it happened. One person makes a small bug fix change to a few lines of code. But that fix required hours of research and a solid understanding of the problem. Meanwhile, someone else is doing a refactoring of that same code in another branch, unaware of the bug.

Neither of these two people may be in a good position resolve the conflict. The bug fixer doesn't understand the refactoring, so they don't know how to apply the bug fix in the new code. The refactorer doesn't understand the subtleties of the bug fix and the code being fixed is just gone in the refactor. It's going to require the two of them working together to fix this, and it may took several more hours and they still may not get it right. And that's the best case! Realistically, one person will muddle through the merge and hope that things still work properly.

Of course, merge conflicts are a fact of life in _any_ version control scheme without exclusive locks (remember RCS?), but minimizing them is a very valid goal.


I don't just worry about merge conflicts. I worry about merges that generate no merge conflicts, but still create bugs because of the way changes on two different branches end up interacting with each other at run time.

In general, I feel a lot more confident with using feature flags and the like to keep work in progress from prematurely going live than I do using branches to do the job.

Or, if I could take a stab at saying it pithily: Having your code be continuously integrated is a prerequisite of doing continuous integration.


I'm not sure feature flags fully solve this either since someone doing work on another feature is just going to ignore the path of a not ready flag.


No, it's certainly not perfect. But, not being aware of a solution that's perfect, I've had to content myself with something that's merely better than anything else I've tried.


I generally like to have feature flags default to true in tests. It helps avoid this sort of situation, because if they ignore the flag, they’ve probably broken some tests. And if they only put it in the flagged path, their code won’t work in prod, but it probably won’t break anything either.


At least they can see it, instead of the code being in an entirely different branch.


I think of those as "logical" merge conflicts, as opposed to "literal" merge conflicts, the kind detected by git. Having good integration tests is crucial!


This is definitely my biggest worry, too, especially since we rarely have merge conflicts on our 6 person team.

Merge conflicts are rare for two major reasons:

1. We plan our changes to avoid the likelihood of merge conflicts. Specifically, we try to avoid two people working on similar areas of code at the same time. 2. We have functionality split into a fair number of different repos (though I probably wouldn’t call them microservices), which not only makes 1. easier, but simply reduces the chance of two people pushing commits to the same repo.

Part of 1. also involves giving slightly bigger pieces of work to a single developer, so that person can see it all the way through from the start. That way, there may not be as many short PRs that are quickly merged, but that developer can really wrap their mind around the complexity of the problem and hopefully have a higher quality functioning product at the end.

Of course, we also have our use cases reasonably solidified when we start building, since our company is over 8 years old and we take the time to know our customers’ desires fairly well (and are always learning more, or course!).


This is also a argument (among many) against rebase. There are a lot of automatic merges peppered in the history and you can track down the bug to an automatic merge (using eg git bisect) only if the history is preserved.


I don't see thus as an argument against rebase. In fact, rebasing your branch interactively allows you to examine the conflicts, if any, in the context of the feature branch and fix them. If you merge the base branch into your branch, you get a mix of changes that are hard to reason about in the context of your feature branch and the automatically generated commit message doesn't provide much information about what was done to resolve any conflicts.

On the other hand, its a lot easier to see if a conflict resolution attempt didn't work during a rebase because the individual commit diffs and the overall diff would make obvious (e.g., unrelated code in the master branch was changed out removed).


perhaps people do gitflow incorrectly? I've been using it for several years and, on teams of about 10-15 people, have rarely had merge conflicts. Feature branches should be short-lived and therefore avoid conflicts. Hotfixes pose a risk to conflicts, but that's their nature. Say you have bug in production code and you fix it via a hotfix and some other developer stepped on that code on the develop branch, you should have a human reviewing the bug fix's merge with the new feature in development.

I really didn't understand this post. To me, it seemed like someone writing a critical perspective of a process they don't fully understand. I know it's not because of a lack of documentation, git flow has been blogged about ad nauseam.

I have friends who use different branching strategies who complain constantly about the same pain points the author brought up. The only thing I agreed with the author with is to find a strategy that works.


Merge conflicts can be minimized, if not eliminated, by only merging branches back into develop via a fast-forward merge (and preserving the merge commit with the no-ff flag for the git merge command). The same thing should apply to merging develop into master.

That way, merge conflicts, if any, are limited to merges that involve porting bug fixes back to earlier released versions.


I find most merge conflicts are easy to understand if both authors work together to resolve it.


When I work together with people on projects, I make them to agree that nobody other than the author of commits is allowed to resolve merge conflicts, and conflicts must be resolved in the feature/pr branch that is going to be merged into the main branch(es)


I would agree that _most_ are easy. But the upper bound for difficulty to understand is quite high. And even if they are easy when both authors work together it'd be even better to _not_ spend two people's time on that. So why adopt a workflow that's guaranteed to maximize merge conflicts?


It's honestly crazy how much time we as a community of software developers spend thinking about Git. We spend time training new engineers how to use it, we spend time researching how to un-wedge ourselves when we run into problems, we spend time standardizing preferred workflows and giving feedback when someone doesn't follow them, and we seem to spend time debating the proper branching workflows within each team or company once every couple years. When all those costs are added up, I felt more productive using svn. I can't be the only one.

Ultimately I think the problem is that Git solves too many problems - it provides a very general data structure that can be used in a lot of different ways, but the tooling itself is mostly agnostic to how you want to use that data structure. Teams can define preferred workflows on top of that, but the tooling itself doesn't enforce the workflow, or provide any specific affordances that help you properly comply with the workflow. Instead, every developer is supposed to keep the workflow in their head, and make judgement decisions every time they interact with it.

The cost of this is huge. We're sorely in need of opinionated tooling that sits above Git's data structures and makes real-world workflows simple to follow correctly, and difficult to mess up.


> It's honestly crazy how much time we as a community of software developers spend thinking about Git.

It's funny, I can't remember the last time I had to give serious thought about how to use git.

Sure, there's a learning curve. When I was just starting out, I didn't get it all right away. But it's been years since I've thought much about branching strategies and stuff. And I don't get myself into "terrible messes" that take hard thought to get out of.

Not saying you're 100% wrong. But it doesn't line up with my experience.


I'm one of the git "experts" that occasionally helps unwedge people who are "wedged" with git.

Every time, they are mentally unwilling to learn git.

And before anyone mistakes that for complexity on git's side… often, these folks have trouble with even basic questions, like, "is this the change you intended to make on this branch?" when shown a diff.


> these folks have trouble with even basic questions, like, "is this the change you intended to make on this branch?" when shown a diff.

I've seen people like this too, when reviewing PRs, I often see unwanted code being committed, and I just don't understand why this keeps happening. One of my defaults when committing code is to run "git status", then run "git diff" on each file I'm committing. I thought this was a pretty well used flow, but apparently it's not.


Or better yet: `git add -p`

If you only ever use `git add -N .` and `git add -p` you'll never have unexpected changes in your commit.


Or better yet: `git add -e`.


I tell people to run git log -p --reverse origin/master.. and git diff origin/master.. to see what changes their commits are introducing and the overall change they're making before they actually push the branch up to the remote. It's essentially proofreading what you're going to submit before actually submitting it.


It’s alarming how true this is. Many seem almost proud of the fact that they have no idea how to use their tools. That’s definitely not git’s fault.


I knew a team of devs whose workflow with git was to have a clone of the repo per branch they worked on.

Needless to say, this caused a bunch of issues that made them think git was bad. When I asked "why don't you just checkout the branch you want to work on?" The answer I got was "I don't like working that way... Now fix my horrible mess of esoteric git features that I misapplied"


> And before anyone mistakes that for complexity on git's side… often, these folks have trouble with even basic questions, like, "is this the change you intended to make on this branch?" when shown a diff.

And yet I don't run into such behavior from people using SVN or mercurial.


Every SVN-using project I've touched has had countless examples of files accidentally being committed.


I am with you on this. Its been years since I had an issue with git, and branching strategies are not that hard. In smaller teams most solution that show up are also usable. I would argue git is actually a fantastic tool that gives immense value year after year for no cost compared to any other alternative. There is a learning curve though.


What rings truer to me is that it's crazy how often building a tool or process, etc, eventually turns into people being able to make careers of just that thing because of all the spins and metaprocesses people will throw on top of or into it.

Git springs to mind for one, but (capital A) Agile definitely wins the championship.


Most teams or companies I've worked in have a git guru but it's far from all they do. It's the occasional stop by at that person's every so often for help with something that is outside the absolute basics.


Sometimes it’s just that by taking enough out, it becomes unclear and complicated. I “taught” eXtreme Programming to two of my own teams by showing them the book. Worked like a charm. I also participated in several “Agile” installs that failed despite training, meetings, demonstrations, etc. All because removing the “customer is in charge and developer is allowed to get the job done as they see fit” component made it complicated.


While I spend a bit of time fixing other people's messes, I have to agree.

I find the biggest issues are people who don't really understand Git enough doing silly things, or just being careless. Like somebody cherry-picking something to Master and then cherry-picking it to a branch we're working on, but on top of some stuff that has changed in that branch, requiring them to fix a merge conflict. But then when it comes to rebasing the branch on Master to merge it, you then have two more conflicts to deal with that you didn't need to have, if it had just gone once to the right place! It's not hard to fix, but annoying, and you risk introducing errors to the code if you accidentally miss a line or something.

So the biggest issues are probably 1) people not following our workflow, creating extra work to fix, 2) people not understanding Git enough; or possibly for some people, 3) you team/company's git workflow isn't very good.

In most teams for 3 (which is probably one of the more common ones), you really only need one or two people who really understand Git well to set up the workflow and enforce it.


If you've never had a git commit FAIL (eg https://github.com/okonet/lint-staged/issues/565) you aren't very experienced with git. By your description, you've probably just used it casually a lot. You don't do anything complicated and conclude that git isn't complicated. Given the history of working with:

* Submodules

* Binaries

* Large Repositories/branches

* A disparate set of development platforms/clients

* Git auth/credentials


>If you've never had a git commit FAIL (eg https://github.com/okonet/lint-staged/issues/565) you aren't very experienced with git.

I follow Linux kernel posts on vger.kernel.org and I've yet to see Linus complain about a git commit failing. I guess he isn't very experienced either?


You could be very correct! I think Linus might not be an experienced "git for Windows" user


You get to avoid the Valley of Despond when you, y'know, write the tool.


Why would he "complain"? or say anything on that mailing list? More than that, it's still git version 1 right? Sigh.


In that example, the user has configured git to ask their linter if it's ok to commit, and the linter is telling git "no" because it's failing.

The git portion of that is pretty straightforward, but whatever the linter is doing looks rather complex.


The largest git repo I touch has half a dozen submodules (some with their own, you have to do a recursive clone), about two thousand branches (historically, we prune them) and about two hundred thousand commits, and has committers on Windows, Mac, and Linux.

It doesn't have binaries in it, 'cause git-lfs. But I don't remember the last time I had a problem with it other than PEBKAC rebases and the like.


> If you've never had a git commit FAIL...you aren't very experienced with git. By your description, you've probably just used it casually a lot. You don't do anything complicated and conclude that git isn't complicated.

You make unwarranted assumptions about another person's experience base simply because it supports your position to do so.


> * Submodules

> * Binaries

> * Large Repositories/branches

> * A disparate set of development platforms/clients

> * Git auth/credentials

I have experience with all of these things and have not had a commit fail.


lol, gatekeep much?


He asked why people spend so much time trying to work with git abstractions then claimed "works for me". It's not gatekeeping, when replying to strawmen. Try to keep track.


I don't think you are applying the principle of charity when reading his words.


Exactly, that something as trivial and obvious as version control costs so much mental overhead is completely bizarre.

Sure, SVN doesn't have as many features as git, and merging sucks, but it offers the most important features of version control (recording "who has done what, when", and undoing changes). Teaching SVN usage to a non-techie takes literally a few minutes (especially with an intuitive UI like TortoiseSVN), and it's very hard to break something.


Gitkraken is easy as pie to use and does everything you are mentioning.. not sure why people are so obsessed with command line stuff for version control.

Also anything like visual studio / vscode you have 99% of what you normally do right in the UI.

I think it's anal people who like to make things as complicated as possible who try to make git as complicated as possible for others.


And that's why everyone still uses SVN and git is this obscure minor player that's relatively unheard of.


That's why everyone should still use SVN (or even something distributed but friendlier, like hg or bzr) and git should be a minor-but-important player that's used where needed (like for managing the Linux kernel, which is what it's actually for).

But of course we've cargo-culted it everywhere, and anyone who objects runs up against the pervasive attitude that there are no bad user interfaces or difficult-to-use software, only stupid people.


>That's why everyone should still use SVN

Ah yes, good old normative claims.

The market wants what the market wants.


Whether it's specifically SVN doesn't matter really, the point is that no other version control system in history required so much mental effort to be "learned" like git. And the worst thing is that new people who don't know anything else than git think that this is normal.

What's happening under the hood is mostly fine (mostly, because git completely breaks down with large binary files, and solutions like git lfs don't help much with this problem).

What's really bad is the git "frontend", that there's no obvious standard workflow, instead there's a thousand ways to do the same thing. That's great if you have a lot of time for tinkering and meddling with the tools layer, but not when you simply need to get shit done. The version control system should essentially 'disappear' and never require attention.


I started on SVN. Had a stint using TFS. Eventually wound up in git. I vastly prefer git.

We had simpler, easier to operate version control tools. Git traded off some of that simplicity for other benefits the community at large seems to value enough to have migrated away from the simpler tools.

I'm skeptical the "git is too complicated for newbies" cry is truly a turning point in tech cycle where we see the folly of our ways and begin to decry the current flavor of the month at the new anti-pattern after enough real-world experience has been accumulated to show us the error of our ways.

I think it's just "I don't like the way this makes me feel".


We teach students SVN as a part of software engineering at the Uni where I work.

70% of students screw up their repository every year.

My experience is that SVN is just as hard as git for the purposes of managing versions and branches of your work.


I have had the same thought. It is laborious to use and people struggle with it.

99% of the behavior it implements people do not use.

Git is not the correct level of abstraction.


Here is an exhaustive (~99.5%) list of the git commands I use:

  git init
  git clone
  git clone --recursive
  git remote add
  git checkout
  git branch
  git merge --no-ff
  git add
  git add -p
  git rm
  git reset
  git commit [-a] [-m]
  git commit -p
  git commit --amend
  git submodule add
  git push
  git push -u
  git pull
  git log
  git show
  git status
  git update-index --assume-unchanged (aliased to ignore)
  git update-index --no-assume-unchanged (aliased to unignore)
The 1 in 200 is probably a git rebase -i.

If we're doing something other than git I'll be happy with those. Specifically add/commit -p are important to me.


I’d encourage anyone using Git take a closer look into “git rebase -i” (interactive rebasing) combined with “git add -i” (interactive staging).

While working, I’m freely taking advantage of Git’s capabilities on a personal level, producing many sometimes messy WIP (work in progress) commits, trying things on short lived branches, reverting changes, and so forth, but afterwards I am able to copy edit the results to produce a sequence of finalized commits that form a coherent narrative that reads like how I would describe my work to a colleague or manager.

To me, interactive rebasing has been the difference between “my team uses Git” and “I first and foremost use Git for myself, and then also use it to share the results with my team”.


I nearly always "git rebase -i" before opening a PR, and even after. Squash, re-order, reword FTW. Recently I started using "edit" to split previously squashed PRs back out.

I like presenting and merging tidy changes.


"git stash" is something I used to use all the time, roughly daily.

"git blame" is also very useful.

"git bisect" is remarkably useful once a month or so. It does a binary search to find the commit that broke whatever thing you've recently discovered is broken.


Bisect might be one of git’s killer features: you rarely reach for it, but when you do it saves your ass and saves the day.

Recently my coworker asked for help tracking a regression after spending hours finding the culprit, I bisected it in a couple minutes and his mind exploded :-)


I run git rebase -i a lot more often because I like to reword a bunch of commits to make the commit messages clearer just before pushing.

And git reflog is indispensable for fixing things.

And I'm surprised you didn't mention git diff. I do that all the time to compare things, like having a single diff of all commits between the origin diff base and current HEAD. Also useful for summarizing changes of entire feature branches.

Git is like C++. People learn and use a different subset of it and everyone claims their own chosen subset is powerful enough and entirely sufficient for everyone.


Darn I actually missed git diff. I haven't been using a bash history so I don't have something check against. I'm confident now it's complete tho.

Rebase -i is a lot to fun, I ought to practice it more. Haven't had to use reflog yet, but I'll be expecting it now thanks~


me]$ cat .bash_history | grep git | awk '{print $1,$2}' | sort | uniq -c | sort -n

      1 git clone
      2 git cherry-pick
     10 git reset
     17 git merge
     23 git fetch
     34 git checkout
     37 git rebase
     66 git diff
     75 git pull
     79 git log
     87 git branch
    126 git commit
    135 git add
    141 git push
    201 git status
    205 git grep


    122 git bug
    124 git branch
    169 git show
    247 git reset
    274 git rebase
    308 git fetch
    310 git fixup
    576 git commit
    633 git push
    692 git pull
    889 git stash
    949 git diff
   1122 git add
   1681 git checkout

Where `fixup` is https://github.com/hashbang/dotfiles/blob/master/git/.local/...


I always recommend to people to not use 'git pull' as it's probably not what you really want.


What do I want? It sure seems to do exactly what I need in the case where I'm on my local `master` and it's exactly the state of `origin/master` but a day or two behind.


What is it you're doing on your local master? It's only going to lead to a mess if you're developing on master locally and trying to do merges from upstream.


Absolutely no development. I use it as my starting point for feature branches, as well as subsequent rebases and merges on them. And I use it to build and run the current master.


I don't use git; years ago I chose mercurial and have stuck with it. And I'm by no means a power user. But I've always wondered why git "won".


git before github existed was a pain to use. I honestly just continued to use SVN because the tool support (like automatic renaming in IntelliJ when refactoring a class) just worked, and there was concern about exactly how to back up our source code.

After github, things became much easier and it is likely the main reason people use git over mercurial.

Of course, now I work at a place that uses Mercurial, because Git can't handle the size of our repo (an order of magnitude, at least, bigger than the Linux kernel)...


git even before github was a game changer for distributed development, kernel being the most prominent example. It did not require random people to have connectivity or authorization on some central SVN server, and yet they could clone and sync their trees and create branches to their heart's content.


But mercurial was also in play then and seemed better (which is why I chose it) or at least on par. Somehow git got more traction and is now the default.


As with programming languages or anything else, this would come with lots of trade-offs.

Whether it is programs like Photoshop and Excel or programming languages like C and JS, general purpose tools have a lot of appeal: learn the once, use it for anything.

Of course there is the opposite as well: niche tools that solve a specific problem in an opinionated way, like photo filter apps, Elm, R, etc...They have a lot of appeal too because they are better suited to the task at hand, but have limited use outside their specific domain.

But the most popular and widespread tools are always from the former group. R is great, but there are always going to be more python repos on github than R repos. If git was opinionated, it would serve a smaller niche better, but other niches worse, so it wouldn't be the industry standard it is today.

For better or worse, I've observed that powerful with footguns is always more popular than more limited options .


Eh? Branches and tags are just symbolic names for commits, and commits define the entire content of the filesystem for a codebase at that commit.

That's almost all you need to know. Get that down path and you're on your way to being a Git power user.

If you can't handle that, then maybe you can't handle programming either.

The last thing we need is more opinionated version control tooling. We've tried it (e.g., Mercurial), and it has failed in the market. But if you want opinionated version control tooling, well, there it is for you to use.


Instead of opinionated tooling you can just have an opinionated lead developer who tells his team how to use git. I'm the git expert at my work and my team spend no time thinking about git any more. New starters understand how we use git on the first day and after that we all understand what's going on.


This.

Git is a glorious tool, but such tremendous power implies a lot of complexity and a lot of different ways that you can work with it. You can have 10 engineers and find they have 10 different opinions and everything ends in ego fightings. The best way is to have an owner of this, make the team understand the governance issues being resolved with the flow and disagree and commit (pun intended ;)


People need to learn Git. Git has a "click" moment. Git "clicked" for me a few weeks after I began using GitUp, an FOSS Mac app. It visualizes Git, and doing advanced Git is easy from within there, and only committing some lines of code is easy from within it. Visualizing Git is really helpful.

A branch is a name that points to a git commit. Every git commit has a parent, so a branch also has all its parents. You have a current commit, known as HEAD, and you have a current branch.

Merge is when you make a commit that "fuses" two commits, leaving both in the repository. It is ugly.

Rebasing is when you reapply the diverging commits of a branch onto the commits of another branch. You give it a new base."

This is important: Branches are names for commits. I have two active branches currently, "floating-plugins" and "master." "floating-plugins" is where I'm working on a plugin system and inter-plugin communication. I made the logging system log a stack trace for warnings. When I did and tested this change, I quickly select the lines of code that bring about this change through a GUI, type in a short message, and commit. Suddenly, I need to implement a feature on "master." As I'm implementing it, I realize I also need the log-stack-trace-on-warn feature in master. So I rebase with floating-plugins, and now, I have that feature on master.

In another scenario, I might have had to reorder the commits on floating-plugins so I could safely rebase to just the commits I wanted on master. In yet a different scenario, a cherry-pick might have been appropriate. The unifying theme of all this is that you need to be able to visualize Git.

Git won't fix people who do not test in a disciplined way. That's a social problem.

Git let's me do a hotfix on a beta testing server while having other code on my current laptop, and then merge the changes together with a rebase.


"So I rebase with floating-plugins, and now, I have that feature on master."

Didn't you just break the golden rule of Git which is to never rebase onto a public branch?


“Rebase” is a bad name for what should be called “edit history”. Never edit the existing history of master. Always do edit the history of other branches to merge them into master.


Is that really a rule? We do that all the time.


The Golden Rule of Git [1]

The fact that a) there even needs to be a golden rule, b) it's not obvious c) not everyone knows about it ... and I'm sure for many other reasons is a very strong reason to be spooked.

Git is like uranium, powerful, but so many don't even know what they are getting into.

Or maybe it's like C++ - really dangerous, but after a while, you learn to avoid most of the pitfalls and then it starts to feel 'ok' even though such pitfalls shouldn't exist in the first place ...

[1] https://www.atlassian.com/git/tutorials/merging-vs-rebasing#...]


Rebasing onto a public branch and rebasing while on a public branch mean completely different things. The first happens all the time, the second creates problems for people doing the first.


Pardon my ignorance, but if you rebase while on master, are you not changing the master branch history for everyone? Isn't the point of the golden rule to not be re-writing the history of public branches?


That is exactly what I was referring to in my comment. We are saying the same thing.


Oh, ok yes, I see.

Again it troubles me that even the language of Git is sometimes ambiguous.

I feel that there should not be a 'golden rule' of something that should never be done - it's a flaw in the product. It simply should not be possible, or at least not with someone without super-admin rights.

I feel Git is very much an administrative level tool, and that there should be something else for devs; something that is fairly obvious, consistent, encourages/forces the org. the policy and makes it so that devs don't have to think to much and really hard to screw up. Admins can give special privileges to the 'true experts' on an as-needed basis.


I think git is pretty good. However there are a lot of random/bad blog posts giving supposedly expert advice about how to use it.


Do you remember the hellish experience that was SVN, or CVS?


I liked CVS with continuous integration. It was problematic if you strayed from the trunk for too long. But one of the big reasons for SVN was to handle directories and version them properly.


Lack of consolidated commit logs is reason enough to condemn CVS. Yes, scripts existed which attempted to simulate one but it's not the same.


Yes, that was a nice feature of Perforce.


+1e6.

Rebase workflows are the best. We used them at Sun long before anyone called it rebasing, long before even any VCSes had the feature in any way. It's the only workflow that works in large codebases (monorepos, repo forests) with thousands of developers. Merges stop being interesting very quickly once you've got a) a large number of developers, and b) a high rate of commits on the same codebase -- it's just too much metadata.

Linear history works like a charm and is super easy to understand (because it's linear).

Nobody cares about the internal history of a feature branch that has delivered. One should only care about what mainline bugs were fixed and what features were added -- bugs that never made it to the mainline are simply not interesting at all to developers on the mainline (though admittedly they might be to someone studying developer performance, but then, feature branch history can be kept and archived, just not on the mainline).


I find it interesting that many people don't seem to know the "git rerere" option [1], even experienced devs. With long lived feature branches that takes away 95% of the frustration people have with rebasing.

Theoretically, one could also use merges on the feature branch and rebase once at the end of development (as git will auto-remove all merge commits during rebase), but that only works well if you have just a single commit between merges on the feature branch.

[1] https://git-scm.com/book/en/v2/Git-Tools-Rerere


My fastrebase script is even better[0].

[0] https://gist.github.com/nicowilliams/ea2fa2b445c2db50d2ee650...


My only beef with rebasing is that it seems to fail much more often, whereas merges will usually do the right thing. I realize this is git-specific, but rebases have still given me much more trouble than merges.


I've yet ever to see a rebase workflow fail, and I've been using a rebase workflow since at least 2002.

I do sometimes have to rebase across thousands of commits because I let a branch rot, and that can be hard to rebase, but it can be equally difficult to merge. Instead I use a bisecting rebase approach where I try to rebase straight to the new upstream, and if there's conflicts, abort the rebase and try rebasing to halfway to the new upstream, continuing the process until the rebase was across just one commit, thus I can resolve conflicts directly with the upstream commit that caused them, then restart the process until done.


Do you have a tool to assist with such rebase? Or something to read? I also prefer linear history without so much superfluous merge metadata, so I'd like to learn more on this.



It's amazing how I'd consider myself as having a pretty good grasp of Bash programming, certainly more than average among my colleages, and still this code got me already in the second line :-) (never used typeset before)

Thanks for the gist


Hmm, that doesn't match my experience. If I remember correctly, I've mostly seen long-running branches where the conflicting code was moved/taken care of still having rebase conflicts because git is running the commits one by one, and is conflicting with the old commits (before they were fixed), requiring me to fix them yet again.

Merging doesn't have that problem, it usually just merges fine unless there are conficts in the latest commit (not all previous ones).


> I've mostly seen long-running branches where the conflicting code was moved/taken care of still having rebase conflicts because git is running the commits one by one, and is conflicting with the old commits (before they were fixed), requiring me to fix them yet again.

is this solved by enabling git-rerere ?

https://medium.com/@porteneuve/fix-conflicts-only-once-with-...


No :/ I had it enabled, which is why I'm surprised it happened. Unfortunately it was long ago that I don't remember details, but I've had my "merges are much easier" opinion reinforced by git multiple times.



But it's also solved by merging, which is why I don't see the point of rebasing. What's the advantage?


Linear history.


How is that an advantage?


It's much easier to understand linear upstream history!

Also, with this script you get to see which of your commits is conflicting with which upstream commits. That's really important information to me.


I'll give it a shot next time I have conflicts, thanks.


You're welcome. Leave a comment on the gist about how it goes, if you remember!

I'd really like git to have this feature built-in to it...



Git rerere can help with multiple identical conflicts.


Git was first released in 2005. Did other VCS/SCMs have a rebase workflow before git?


I was on a team that “merged forward” from base branches in subversion back then. What I learned later was that was one of the few teams that was a actually doing that in svn.


Yes. Teamware with Casper Dik's "turbo" extensions. We used that at Sun, with a rebase workflow going back much earlier than 2002, but 2002 is when I joined Sun.


Recall that "rebase" == identify the end of the linear history in common between this branch and the upstream, call that the merge base, identify local commits between that merge base and the head of this branch, checkout the upstream head, and cherry-pick (sequentially apply) those commits as if de novo (possibly recording this action in new metadata).

One can implement that even with Fossil or any other VCS that pretends it does not implement rebase and pretends it never will.


I'm in the camp that rebasing is unnecessary and useless for large projects. I work in a code base with 50 other devs, no one is reading the commit log line by line, there are 100 commits per day. We use JIRA and have to prefix our commits with TICKET-<ID>. If I want to see the commits for a ticket, I filter the git log on that. If I want to see the combined diff for the commits, I open up the PR in JIRA.

Squashing public commits I'm also against. People say it 'cleans' the history. I would say it erases it. Who, when, why - all gone. For what? to save a few commit lines? Like I said, our commits are tagged with the ticket number already, if you want a summary of what the changes were and all the commits that were part of it, you open the ticket, or filter the git log on the ticket. No need to move commits around in the history, they show in the same order when you apply a filter.


You've just described the problem that rebasing fixes. Because your git history is a mess you have to look up PRs in Jira rather than having your git history be something useful by itself.


What he says is that they can achieve what they want, by other means than looking at the git history by itself, and it sounds like it's less work and scales better with the number of committers.


Except for when you change from JIRA to some other project tracking tool. I think this is worth considering since project and people management trends tend to suffer from more churn than version control systems.

In my book, it's easier to auto-import sensible git logs into JIRA than trying to do the reverse. Then again, I'm of the opinion that git history matters, specifically for commit messages. Otherwise, you might as well create a pre-commit hook pointing to http://whatthecommit.com


...until you change your organisation tools.

If you keep your development rationale with the code, you can always change or rearrange the rest of your tooling without losing vital information. If you put your edit history in a loosely-coupled system, you've now made that system as essential to your codebase's survival as the code itself.


How does rebasing fix any of that? Rebasing changes your history, and can just as easily make a mess of your history as it can make it prettier. I use it sparingly, but I don't mind people avoiding it altogether. Rebasing fixes nothing.


It's not a mess at all, very clear actually. We enforce commit messages to be prefixed with the ticket number which makes it easy to query git log for all changes related to any PR with full history of who, when, and why each change was made.


> no one is reading the commit log line by line, there are 100 commits per day.

I hardly ever refer to the commit log. However, I do often run `git blame` to figure out what changes were done to the code base together with a specific modification to a specific line of code. That works so much better if people have actually spent some effort to make sure related work is in a single commit.

(Note that this doesn't mean "squash every PR together" - it means rebasing before submitting a PR to make sure the commits also work as documentation. The result of that rebase might very well be several commits.)


What is history for? You already said yourself that nobody is reading it. So why not squash the whole master branch every time? History is an artifact of development and should be maintained or disposed of. It turns out history is useful for at least one thing: tracking regressions. And that's a hell of a lot easier to do if you can bisect a clean, linear history.


People are definitely looking at history when running git blame on a file. We want to know exactly who, when and why a change was made.

Squashing down a PR to a single commit erases all of that history and we have no idea when the change was made, who made it, who to talk to, or why it was made other than it was part of a larger feature set.

It might work fine for smaller PRs, but where I am we have multiple teams each working on a task that can take months to complete with lots of testing before it's ready to merge into a release branch.


Git blame is very useful, yes. But what do you need to know? Do you need to know that Josh made a commit called "fixes" at 3pm on a Wednesday (while it was raining), or do you want to know what feature this commit was part of?

I don't argue for squashing down the whole branch into one commit. That's silly and could be throwing away valuable information. But cleaning up the history using rebasing is still possible. We use "fixup!" commits so that a simple autosquash does the right thing most of the time.

I've actually had developers undo over-eager squashing before and introduced them to both the reflog and interactive rebasing. They are shocked the first time they learn to "undo" a rebase, but it's a liberating experience to actually understand git.


When people talk about squashing branches, it means squashing all commits into a single commit (git merge --squash, or using the squash feature of github / gitlab / hosted-git-solution).

The reason you use blame is to find the exact commit that changed a specific line, and hope to find the rational / decisions that lead up to this change, assuming people are disciplined enough to document that in their commit messages. If not, maybe you could even ask Josh if they still remember why they changed things a certain way.

Many developers commit append-only until their feature works, which means there are a lot of noise commits in between, which they don't (know how to) clean up. Rather than having all this noise, projects opt to squash the commits instead.


My team also uses feature branches, with a squashed merge commit. They used the argument that it's easy to revert that commit.

But I didn't want to lose the detailed history either, so our squash commit message contains the complete list of squashed messages, and we keep the old feature branch alive forever on a separate remote.


It’s equally easy to revert a merge commit.


That's good to know, I didn't challenge the argument when it was presented.


I'm curious how your commit history looks. Doesn't it have tons of useless commits like "removing comments", "forgot semicolon" etc?

Here's Linus Torvalds' thoughts on rebasing. https://yarchive.net/comp/linux/git_rebase.html


I agree with Linus, "you really shouldn't rebase stuff that has been exposed anywhere outside of your own private tree"

I have no problem if you want to locally squash your commits. Just don't rewrite public history. It's not worth it. At our commit rate, no one is reading the combined history logs without filters first.


You never run git log on a single file or directory?


That would qualify as a filter...


Yes. And it should. It's your commit history, so it needs to be a history of your commits.

One day you'll find a commit labeled "removing comments" that breaks something because the dev palmed the trackpad while hitting the delete key and lopped off a line of code. You want that to be something you can find with a bisect.

It amazes me to see a bunch of discussion here from people arguing that one should throw away history so that some silly graph view is prettier.

History isn't meant to be pretty. It's meant to be history.


Those aren't his thoughts on rebasing, they're his thoughts on rebasing "somebody else's work".


I don’t rebase and I don’t have these sorts of commits. I do use commit —amend when I see a typo in code I just checked in, though, and git stash if I need to set my current task aside for a moment to fix or prepare something else.


Very rarely, I undo and redo a commit to fix a problem with it, in cases that are too complex for --amend.

But once history has been shared (pushed, generally), it really shouldn't be touched anymore.


Sometimes the cure is worse than the disease.


I agree 100%. I too tried Gitflow on many different projects, with different skill levels of teams and we ended up pulling the plug on it and going back to "master is always deployable/production". Developers work in feature branches that are small and atomic, with frequent merges into master with corresponding deployments. Breaking master is a critical blocker issue. There's no need for tags, complicated release schedules or processes following this model and it scales from a team of 1 to a team of 1000, I've seen it done.

Gitflow doesn't work in a CI environment on a modern software application that has good testing culture and systems that are resilient to failure.

Tools and processes work a lot better when they work for you, not the other way around. KISS.


I have used gitflow successfully for some time (starting when it was first announced and there wasn't anyone complaining about it). I've always done "develop is always deployable to production" and the only thing we used master for was rolling back (extremely rare), and the only thing we used release branches for was demos or documentation before a release went out. We had CI built against develop but also every feature branch and every PR. We also leveraged static analysis the same way, and at one company even ran automated pen tests on develop and PRs.

I don't know if I fundamentally misunderstood gitflow but this was my take on it from the beginning and I've never had issues with it that made me want to jump ship.

We didn't use hotfix or support branches, and we never really used tags for anything either. Not using them didn't cause any noticeable overhead.

The nice thing about git-flow in my opinion is that you can implement it without all of the CI stuff, then add that later over time. This is a godsend when you take over a legacy product with little to no test coverage and no CI pipelines.

I've also never run into a ton of merge conflicts, but the largest team I've had is 70 people so maybe it breaks down at some point that I haven't experienced yet.


How can develop be always deployable to production? That doesn’t even make sense in English.


Simple. It’s either the wrong label or we are missing info on the setup.

In my setup I have a develop branch and the idea is that anything in it can be deployed to prod. Of course, that is if nothing fails.

If nothing fails, it merges with master (what’s actually on prod).


You need to learn English. What’s the definition of in development?


I think your last paragraph is pure gold and encapsulates the point of all these discussions.

Use something by applying it with common sense, in the right amount and customization and it will have a chance to work.

Use something as if it is a religion and it will introduce weird side effects.


> Gitflow doesn't work in a CI environment on a modern software application that has good testing culture and systems that are resilient to failure.

Sure it does.

It isn't necessary in such a culture if there aren't bureaucratic barriers to continuous deployment which limits you to continuous delivery.

If you have such barriers, you very quickly need Gitflow’s split between permanent “develop” and “master” branches as well as temporary “release” branches.


> There's no need for tags, complicated release schedules or processes following this model and it scales from a team of 1 to a team of 1000, I've seen it done.

How do you roll back in a process like this? I was imagining that a release would consist of tagging master, so a rollback would be going back to the previous tag. Or perhaps you just keep track of the previously deployed commit hash and roll back to that?


I don't understand the argument here at all. Git flow seems pretty orthogonal to concept of branching lifespan. We did the "merge early, merge often" approach with SVN 15 years ago and it was fine. We do git flow now and it's fine. It meshes perfectly with agile development where you work on the smallest feature set that adds incremental value. That means your branch only ever lives long enough to do one tiny thing and it gets merged as soon as it's working and no sooner. I've been following this approach for years with dozens of teams and it's very successful. And I've never run a rebase on purpose in my entire life.


What sort of repository and deployment structure are you using right now in which it works fine? I think that will influence the outcome a lot.

I just was working with a client that used something like git-flow, where develop was deployed to staging in order to test, master was always production, and the codebase is largely a monorepo. Then there were a couple other legacy repos, which also are using git-flow. It... "works", but it also is a needless source of pain. You need to PR to develop, then PR develop into master, and I'd inevitably get bitten with merge conflicts in the process. All that for what gain? I saw no upside to it. Maybe if you have steady release cycles, but git-flow never seemed to fit the whole disposable branches, master is good to deploy dozens of times per day sort of workflow I've gotten used to.


Continuous deployment triggered by a merge into develop is basically a degenerate case of gitflow where there is an automated release associated with every merge into develop.

So if you are doing full gitflow or if you are doing this degenerate case of gitflow you still shouldn't be getting merge conflicts when releasing develop (merging develop into master). This should basically be a fast-forward with perhaps a merge commit for record keeping. The only way to get conflicts in this case is if there is some other process that is changing master independent of the "releases" from develop.

In gitflow those other changes are managed through the hotfix branching rules, which ensures that you resolve the conflict when you close the hotfix and back merge it into develop.


Just pull develop into your feature branch daily. Merging develop to master is a one-lane highway so merges are only required if you did a production patch.


> I've never run a rebase on purpose in my entire life

While it's great that you've found a branch strategy that works for you, I would hesitate to take recommendations from anyone who doesn't rebase their patches. Then it's just an elaborate way of doing cvs style development.

Which there is nothing wrong with of course. That works great for many people. It's just not what git does best.


Git was built for giant open source projects where you have dozens of contributors making unplanned code changes to an organically growing code base. That's not a use case for any well-managed enterprise. Pull requests come a few at a time and representing a few days work and are rarely stepping on each other. Every conflict can be worked out face to face very quickly. If I have to manage a large group of devs, I break them up by vertical so they're not on top of each other.


This is the thing I've never really understood about the common objections to merging and resolving conflicts in larger projects. Sometimes people talk as if that happens all the time and it's not scalable if you have hundreds or thousands of developers all working on some huge code base. But how do you even get hundreds or thousands of developers working on the same huge code base so they keep getting in each other's way? Where is the modularity and coordination?

It always sounds like this happens at a hellish organisation where some Agile consultant once came in and said not to bother with boring stuff like management or software architecture. These must be strange places to work, because reportedly they also need to be prepared for requirements to change significantly every 27 seconds, and not being able to deploy changes in minutes is an existential threat even though the new feature has barely finished running the automated test suite and doesn't yet have any kind of user documentation, knowledge among the sales and support teams, etc.

Personally I do find rebasing and squashing to be very useful in workflows that involve Git, but there are plenty of effective ways to run a project and use a source control tool that don't rely on a 100% linear history with GitHub-style pull requests for change management.


Some people talk about merge conflicts as something inherently bad, but that's a flawed perspective. Merge conflicts are a great feature of a version control system. Given that multiple persons changed the same line, conflicts are the easy way out.

Conflicts have only given me a hard time when someone did something out of the ordinary. Even very large and unstructured open source projects such as the Linux kernel will see people naturally gravitate to different parts of the code. It's not often a filesystem developer suddently change the networking stack, for example.


Exactly this. Whatever you call your branching methodology, if they last too long that's when the fun stops.

This article is full of bluster about how bad gitflow is, but very little actual reasoning why.

I don't like the ceremony all that much, but its working really well at my current large corporate gig across thousands of repos and engineers.


"Whatever you call your branching methodology, if they last too long that's when the fun stops."

Doesn't git-flow explicitly keep a long lived branch around by design? The whole concept of having "develop" run in parallel to master is introducing a long lived branch. That's typically the branch that I'd always have the most trouble with.


That's assuming that there is anything going on on master at all. A gitflow master doesn't really count as a branch of it's just a permanent alias for the latest mainline release tag.


Often when I encounter (startup) teams using this git-flow inspired strategy there definitely are things going on with master that shouldn't be. For example:

Feature A is in staging ('develop'), but can't get into prod ('master') yet. However, the opposite is true of Feature B -- a customer needs it now and timelines get changed by sales or management (your largest customer needs it now). It conflicts with something in A and can't go into staging just yet (another team needs it). So that feature branch needs to go straight into master without hitting develop. The whole deployment infrastructure was designed on the (false) assumption that develop is always deployable to staging, and master to prod, rather than being able to quickly deploy and test feature branches at whim. So now we've diverged between master and develop a bit, and while we could still get our way back to a "clean" git-flow at this point pretty easily, if you're not careful this can keep compounding. I've seen it compound for months, and personally have wasted days trying to clean up conflicts that set in because teams I'm consulting for weren't careful.

It's easy to have an inexperienced startup team make these git-flow based decisions early on, get stuck with them for some time, hit rapid growth, and then get themselves into a messy place.

My point is sure, use it if you have pretty steady release and deploy schedules. But if you're deploying dozens of times per day you need to design your infra and CD strategy to handle it. Typically I've seen teams with fragile deploy strategies they've adopted due to assumptions they made from starting with git-flow.

So why even do it? There are simpler strategies like GitHub Flow that force a team to design a deployment strategy that accepts that feature branches can be tested in a real environment, and master can always be delivered (or rolled back).


I have some long-lived branches lying around for work I only occasionally have time and energy to do. E.g., I've an "ALWAYS DEFERRED" feature patch for PostgreSQL I need to eventually finish. Every time I come back to it I've fallen thousands of commits behind, and neither a merge nor a rebase can save me, but a rebase bisection script I have does save me by identifying the one commit upstream, in order, that causes conflicts that I have to resolve manually, then resumes the rebase bisection.

Specifically, the algorithm is something like this (not tested; I've a script I should publish):

  bisect_rebase() {
    local ultimate_target=$1
    local target=$2
    shift 2
    
    b=$(git merge-base HEAD $target)
    while [[ $b != $(git rev-parse $target) ]]; do
      n2u=$(git log --oneline $b..$ultimate_target | wc -l)
      n=$(git log --oneline $b..$target | wc -l)
      
      # Try rebasing directly onto the target
      git rebase --onto $target $b && return 0
      
      # If we have conflicts and rebased across just
      # one commit, stop here
      ((n2u == 1)) && return 1
      if ((n == 1)); then
         echo "Fix conflicts and exit 0 to continue or 1 to stop"
         if $SHELL -i; then
             git rebase --continue
             return 0
         else
             git rebase --abort
             return $?
         fi
      fi
      git rebase --abort
      bisect_rebase $ultimate_target $(git log -n $((n/2)) --pretty=%h $b..$target)
    done
  }



That is the scariest script I have ever seen in my life.

It makes me believe there is a real existential problem with git as a product.


I wrote it on the spot. The actual script is here: https://gist.github.com/nicowilliams/ea2fa2b445c2db50d2ee650...


The nice thing is that git is serverless, so you can clone a throwaway repo and run this sort of risky thing safely. It makes what's impossible in SVN possible, though maybe not easy.


... unless someone scripts pushing and pulling.

... unless changes were not already pushed and therefore 'saved'.

Git is inherently dangerous and overly complex - such a script shouldn't ever have to exist for so many reasons.


Oh get off your high horse. Git started as a collection of small programs and shell scripts, and as time went by more of those scripts became programs.


How it came to be is basically irrelevant when determining how materially useful it is, given all the bizarre artifacts of git. Nobody really seems to really have mastered it, most people seem to grasp only a subset, and there are so many potential pitfalls. It's a very powerful, raw technology that hasn't been properly productized, or rather, 'it works how Linus wants it to for his reasons' and everyone else just gets dragged along.


> Nobody really seems to really have mastered it

You can speak for yourself. Don't speak for me or my colleagues.


I'll bet $1000 that zero of your colleagues have truly mastered git.

I've watched countless times as employee X did something wrong, the so-called 'git experts' gaggle around them and spend quite a bit of time trying to 'fix' the situation, all with varying approaches that their peer gitters barely understood. All blissfully unaware that their 'intelligence signalling' is woefully misplaced as such situations should not arrive in the first place, and if they did, they should be easily remedied.

Moreover, it doesn't matter that 'a few people' have very strong Git skills, the point is - the vast majority don't and there should absolutely be no need for them to - the problems most developers face re: code management are simply not complex enough to warrant the power of the tool. The failure to understand this among so many glib gitters is basically existentially problematic. The inability to grasp the difference between 'raw power' and usability, complexity and the resulting costs ... is the real problem.


What you describe is not what “Gitflow” proscribes though - you don’t have a separate “develop” and “master” branch.


I have two long lived branches, develop and master.

* major features are done in feature branches, w/rebasing prior to merge

* minor stuff directly in develop

* master is auto deployed following successful CI, whenever we merge develop in to it

* we merge frequently, but not automatically

* we tag releases manually when we do merge

It's a more minimal version of git flow. Small teams so not much problem.

We could probably just drop the develop / master split and still be fine, but would force all changes to be in feature branches instead.


This still sounds like an anti-pattern to me, at least in a typical web app. I’ve seen a very similar workflow used in a previous org, and the main benefit was that it limits the number of times you have to cross your fingers and hope for the best (which is obviously masking other reliability problems). Merging each feature branch wasn’t scary because it wasn’t going to deploy yet, but that means it incentivizes somewhat of a “not my problem” culture. Then at some point someone builds up the courage to merge the develop branch to master and you hope for the best. Repeat every few days.

There was also some wishful thinking about doing more qa testing than anyone actually did. The develop branch would immediately deploy to the staging environment, so someone might have tested new features there before they ship to production. But if there’s no rigorous process around exactly what needs to be QA’d and whose responsibility it is, then let’s be realistic, it’s not gonna happen.

The downsides are that release times were less predictable, deploys were bigger which means potentially more difficult to trace down bugs that do show up and roll back when needed, and things would get delayed for no reason. Oops, I thought I shipped that thing for marketing a few days ago but nobody deployed the develop branch since then.

Developing small features right into develop sounds like a bit of an issue on its own too. Seems like you’re not quite confident enough to deploy it right away, but it’s also presumably circumventing the regular code review process so it’s not going to get reviewed.

The only reason I can see for why these downsides would be worth it, is if you have some hard constraint that you can’t deploy very frequently (for instance releasing a downloadable desktop app or mobile app), and/or you have a formal, rigorous manual QA process when merging from develop to master.


In my experience, an additional gap between working directly on master is many teams don't understand the concept of promoting builds. So e.g. you can build on every commit, then _after the fact_ promote one to an actual release (without re-building). This isn't suitable for _every_ situation, but it is for all the ones I've encountered so far.

Relatedly is "release branches". An alternative is to simply tag the release that went out, which is the same thing. If it needs a hotfix, you branch off the tag, fix it there, then apply that fix (cherry-pick or redo) back to master.

Overall, IME the biggest benefit is the cultural changes it instills in the team: Everything must be good enough to release, and everything goes to the same place. For projects that can afford it (again, most can I bet) its a huge drop in mental effort to understand the state of the world at any given time.


I don’t get it. I manage multiple teams that are doing web apps. Many of those use Gitflow like life cycles and none of them has issues based on their lifecycle. Some of them do release branches, some not. Some do ff-only other don’t. Doesn’t really matter as long as it is efficient (simple) enough. Of course we are also not only deploying the long lived branches but actually every push results in some sort of ephemeral environment being spun up tested and spun down again (or stick around in car of failures) so Feedback on what is happening is quite direct but any way. If you help teams find a pragmatic lifecycle for them you can avoid all the perceived issues of any cv lifecycle.

One exception might be the occasional group of optimists trying their hand at micro repos. These are just bound to blow up :-D unnecessary conplexity and all...


Indeed. i have been using Gitflow for the last 4 years on more than 14 repositories with teams spanning from 2 to 30 commiters and it worked. I honestly do not see what is the problem with having the code running on production on master, develop harboring the code deployed on review branch, and release/tags branches deployed on QA environment. Moreover each team can adapt the model to it's own workflow...


> * major features are done in feature branches, w/rebasing prior to merge

Rebase in a feature branch, that can be long-lived and have many commits, seems like a painful experience to me.

You can end up solving the same conflict more than once, or even solving conflicts that don't exist in the final result. no-ff merge commits are usually easier to solve.

Am I missing something? Is there an easier rebase strategy?


Sometimes it's painful, normally it's not, and yes - the longer lived it is the more painful it's likely to be.

no-ff merges are easier, but leave a messier non-sequential history of when things actually go out which is sometimes confusing when understanding why things are the way they are; I generally, but not always, judge it to be worth the pain!


Same. I think people just have different ideas of what "long lived" means. To some it might be a few days of work, to some weeks.

We actually merge the master into our long lived feature branches first. Then run all the tests on that branch, if it's good it can be directly merged into master (master is locked, and you need permission to commit anything to it).


I think so. Sometimes we have long-lived branches with multiple contributors for new features. Is this some kind of anti-pattern? We don't use feature flags, they seem hard to maintain and restrictive in terms of what you can do (e.g. structural changes). OTOH, because we're a small team, when there's a lot of work in a new feature branch there's little work on master, so there's little hassle there too.


This is pretty much exactly what we do, except add in a third “qa” branch between dev and master, for the purpose of manual testing before things go to prod.


This is where GitFlow makes sense - when you have a QA and/or enduser acceptance process before code is committed to a master/release branch.

Also for DevOps you sometimes need to be able to branch off Master to apply a critical patch which then gets merged back into Dev later. You don't want to ever need to do that, but if subverting the normal process makes sense because you have all signoff parties gathered at the table - you do what needs to be done.


We use this pattern in a monorepo and it works great. Master is a reliable history of releases, and Dev is a reliable place to start your feature branch. Yes, someone has to own the decision about when and how to thoroughly integration test Dev before release, and do so often. But for 100 other reasons, this judgement call is necessary anyway.


> but would force all changes to be in feature branches instead.

So what? Those branches live for exactly as long as the minor stuff takes to develop and review.


I don't mean that as a problem, just that that's what I would do if I did it that way (and I don't see it as a problem, just the concomitant requirement for changing).


Cool! This is almost exactly what I've been doing and recommending for many years.


That model tends to break down as soon as you need more than one development branch.


Unsure how, but I've only had to work in small teams to relatively low complexity. Can you explain your use case for multiple development branches in the sense that I describe (just a combined endpoint for all merged features that have not yet been deployed).


In my opinion the problem with GitFlow and a lot of other git workflows is you end up holding a bunch of state inside of your git branches which is hard to decipher, complicated to manage, creates work for people ( with merge conflicts, and managing feature branches that need to be merged ), and is capable of losing progress.

My solution: USE FEATURE FLAGS. Keep the list of features and refactors you want deployed in a format that is easy to audit and reason about.

I like the Microsoft branching strategy: https://docs.microsoft.com/en-us/azure/devops/repos/git/git-...

Basically:

* Create feature branches.

* Merge into master when the feature is done. For long lived branches: Don't keep them around. Merge them into master and put them behind a feature flag.

* Deploy to environments using release branches.

* USE FEATURE FLAGS. Do not hold state in your github repo/branch merge status. Do not control features and refactors inside of your git branches, only merging the code you want to be deployed. Instead, put them behind feature flags and activate them using code.

There is a good book on the topic of feature flags titled "Effective Feature Management" that is a good overview. Feature flags do not have to only protect features. You can put refactors behind feature flags as well, or really anything.


Thanks for the feature branch book recommendation.

Overall, feature branches work well as an approach for relatively modern systems. I once worked on a distributed, multi-process system from the 90s that powered more than 40 factories across the world monitoring and steering production of goods. Every factory could decide whether to upgrade and to which version. Each upgrade was a separate project, which had to go through a public offering and deploy at times when the factory was not operating.

The inability of central IT to exercise influence on the factories to upgrade (due to the rollout costs) and reduce the number of parallel versions, caused a multitude of support branches to be maintained for this system. We maintained at least 6 active branches in parallel, backporting bugfixes across branches and being able to release hotfixes at any point in time for these versions. Although another VCS was used for managing source code and build artifacts, for development, we have successfully managed to use git with multiple, long-lived support branches and merge fixes across these, like in gitflow.


I've never understood how Feature Flags can be used like that. For example say I have a button component that needs to be refactored and it used in hundred of places throughout the codebase. How can I use feature flags to prevent a long running branch? (Serious question :))

Do I keep both implementations around and everywhere a button is used switch on a flag, that seems quite messy, and I have to go and delete the switches on the flag everywhere when I'm finished. Or I could abstract that out into a function or use dependency injection to choose the right button, but that just seems to be adding another layer of complication, that some future dev is going to wonder why exists when there is only one implementation of the button.

Compared to having a long running branch that I periodically merge the main branch into to keep up to date with the current development without impacting the code that is due to released in 2 days (as it is every 2 weeks)


Regarding your button question:

You do need some sort of seam at the point of use so that the feature flag logic can be hidden behind a lookup function. For example in Rails you might include your button code via a partial template. You could use a view helper to provide the name of the partial and that helper could respond with different names depending on the feature flag:

    <%= render partial: primary_button %>

    def primary_button
      feature_flag_enabled ? "new_button" : "button"
    end
Obviously the particulars are going to be different in different frameworks.


This is all covered in the book I recommended! :)

For the button:

I would say abstracting it out into a function or using dependency injection, and having a centralized switch statement is the way to go. I don't see why you would need to break your interface for most refactors, and if you DO have to, that means your code had a major issue anyway.

Additionally, how is the "just using git for feature management" alternative better? With a feature flag you can enable and disable the feature easily without a deployment. With git, you have to do a revert/rollback/whatever to ship a fix.

I struggle to think of an effective way to reuse a button component over and over again without using a function or dependency injection. If you are speaking of react: Those are functions. That HTML you are using is actually just some syntactical sugar around functions.

You would not keep both versions of the button around permanently. You would merge the branch into your master branch, and enable the feature for the desired environments. When all environments are using the new refactor, and it has been verified to be working, you open a pull request to delete the old version of the refactor and purge the feature flag.

"Well, that is still complicated!" That is true, there is still process around managing features with feature flags. The benefit is that feature flags are easy to grok. It is an if statement. Your application state is no longer contained in an interminably long git log. Instead, it can be determined by checking your feature flag configuration.

> Compared to having a long running branch that I periodically merge the main branch into to keep up to date with the current development without impacting the code that is due to released in 2 days (as it is every 2 weeks)

This becomes laborious over time. If you have many people working on a codebase, the feature branch will constantly break and you will have to go back in fix it.

What if someone has to revert something on the main branch that you have merged with your feature branch? You are now in merge-hell and you have to spend a bunch of time figuring out how to make your branch and the fucked-up master branch have the proper code in them.

What if someone on your team does a hard-reset to the master branch? ( Well just don't do that! Oh, sorry, I didn't realize you were the tech lead for your place of business, and get to decide how everyone else solves their various problems. ) Now your branch is out of date and you have to revert commits and fuck around with your feature branch, and HOPE that you actually unfuck your version history properly.

Look at the alternative: You uncheck a box on a web GUI, the bug is contained, you go in and fix it, deploy it, and re-enable the feature.

Your "what if we just don't worry about it, and not solve the problem" suggestion here does exactly that: Ignores the problem we are all experiencing, and then does NOTHING to solve it.


I've used nvie-like gitflow strategies without significant issues.

One of the things that I like about the strategy is that it works well for code-like things and for things like handbooks/documentation sets built with e.g. Hugo+docdock. Authors branch, work, PR, PR gets vetted for accuracy/policital-correctness/whatever and then gets merged.

Anyone have any experience using feature flags with static web site generators?


"Any branching model you choose is ultimately meant to make humans work together more easily to produce software"

There are things in this article I agree with, and things that I don't, but the quoted point is something that I wish could be universally understood. Some developers commit to the dogma of one blog post or another and forget about the fact that the whole thing is for the _people_ who are writing the code. Branching strategy should help to optimize workflows and minimize friction, regardless of how badly bastardized your approach is vs. its source material.


I can't seem to get people to agree on the point of unit tests either.

Nowhere is this more apparent than when we argue about test coverage %'s. There's a whole bimodal distribution of people very wrong for very different reasons.


I feel unit tests are difficult to swallow to some people because they are not used to (or able to) develop code in distinct and definite units. And without units, unit tests are sort of meaningless.


I find about half the people don't see the point (mainly can't be bothered). I can cut that group by about a third by telling them "the tests keep other people from breaking your code"

(Personally I think tests keep me from breaking my and others' code, but I don't tell them that)


Always helpful to think about your future self as that other person.


We don't use any branching. Git is complicated enough to explain to non-software engineers without branching. We treat all commit versions equally for CI/CD. As most people are not software engineers, this seems a reasonable course of action if a project encompasses non software elements. We have non CI/CD testing anyway (nontrivial hardware projects require much more external process).

For releases we assign versions without tags, instead using replication of a subdirectory tree under a version number. This ensures all versions are available locally to all users without adding any access complexity.

This is basically optimizing for simplicity for the users.


At work we were using TFS when I joined. I managed to win the battle and migrated everything we have to Git, but for some reason they wanted us to follow Gitflow (other parts of the business were on Git and were using it). A couple of horrible releases, regressions and lost functionality (merge errors) later I managed to push everyone into using Microsoft's Release Flow[1]. We haven't had a single regression since then and everything in the process is more or less smooth.

The funny thing is I've used Gitflow successfully before in another job, but nowadays I'm mostly scratching my head how it ever worked correctly. It's so overly complicated. It would never be my process of choice.

[1] https://docs.microsoft.com/en-us/azure/devops/learn/devops-a...


I've seen Git Flow fail in even relatively small software houses.

I've seen sensible trunk-based development with feature & release branches (equiv to what MS call 'Release Flow') work well from small to huge (400+ devs) environments.


Something that really surprised me is how anyone has issues with git work flows. Git is one of my favourite software and I think it solves version control and collaboration issues beautifully. I can unhesistently say only one of the teams used git recall really well and we didn't agree on a work flow or no one was dogmatic about it. Each seemed to just know what they were doing.

I think just create your own branch always for your work is a good idea. Use merges to take or give contributions (reduce using complex things like rebases). If branching on sub branches, try not to squash merge. You can't avoid conflicts all the time but just sticking to branching and merging reduces this problem quite a lot. Team size has been about 12 engineers not sure how it'll impact team of much larger projects. But I have also contributed to open source projects using this same techniques without an issue.


We implemented Gitflow almost exactly how it was described in the original blog post a while ago. It was simple and we built continuous deployment around it and it posed 0 challenges in that department. It was much harder to change culture and build a process and systems to actually do the continuous deployment than Gitflow ever caused. I disagree with about everything in this article.


It seems to me that most of the authors objections stem from 100% strict adherence to git flow, which IMO is the wrong approach.

I loved the concept of git flow when I first read that legendary blog post. I learned a lot and adapted it into my own development workflow. I've bounced back and forth over the years in how strictly I adhered to the One True Way (tm) based on how well it worked for me in actual projects.

IME I think the best approach to git flow is to learn and understand it and then adapt it to your project's workflow, as opposed to strictly adhering to every tenet of git flow. I have multiple projects using different "amounts" of git flow - e.g. a large project with dev, qa, hotfix and release branches vs a small personal project with only master and dev.


> 100% strict adherence to git flow, which IMO is the wrong approach.

I agree, as with most things, there certainly is room for flexibility here.

One problem I've seen repeatedly occur when migrating to git flow is a misconceptions about 'bugfix/' branches, which are intended to be used when stabilizing a 'release/' branch. However devs use that prefix for every bug they find which is not related to a story/feature. And to be honest, they are right to do so. The naming is just bad here, since the term 'bugfix' is so generically used, it's not very intuitive to use it only for release branches. So I've come to the point where I would agree to use 'bugfix/' for regular bugfixing if the team/company is comfortable with it, even though it is opposed to original gitflow.


In my opinion this is one of those "pick the right tool for the job" problems. If you have a constraint that forces you to have multiple versions in development and maintenance simultaneously then Gitflow works well. This is especially true for things with long QA cycles. If you have a SaaS business and pushed code can go straight to production after automated tests, then don't bother with the management overhead of the versions.

In personal experience... We use Gitflow for our software that ships on our IoT device where we do monthly releases and multiple weeks of QA/reliability testing. Of course, this is coupled with a slow rolling canary release process -- nobody wants customers to be forced return a device due to a bug that can't be fixed over the air.

We also use it on our mobile app and it works well. Again, this is a situation where we have to have multiple versions in development simultaneously. One in "release mode" doing critical bug fixes only as it goes through QA and internal in-home acceptance testing and one for "v-next".

Some 20 years ago we used to do roughly this same process for releasing software on CD's. But, no git back then so no "Gitflow"...

We don't use it for various services and internal tools that can get by without such rigor around versioning. Just simple branch -> PR -> automated test -> merge to master from PR -> automated test -> deploy.

Use the tool/process with the least overhead that manages what you need managed.


>If you have a constraint that forces you to have multiple versions in development and maintenance simultaneously then Gitflow works well.

Because of the way App Store approval works, and the fact that it can take weeks from initial submission to full rollout, and that many users never upgrade their versions, strongly implies that using something like gitflow would work well for teams delivering appstore binaries.


Most of that blog post's workflow seems like pretty normal merge request development. Separating develop and master is where most of the complexity arises. The reported advantage is "the develop branch is cleared to receive features for the next big release". I have never ran into a problem with features needing to land in master before a release is cut, likely because I have never scheduled releases and I think most projects don't. Even if they did, the project has to be pretty massive to have a feature ready, but scheduled for a future release and also be blocking the development of another scheduled feature... If your project is that busy, it is seems like a reasonable workflow. Everyone else can probably just treat master as their develop branch and everything else in the article is pretty good advice.

The only other oddity I see is treating hot patches different than feature branches. In my experience, hot patches are no different than feature branches in projects that use master as the branch to deploy from. In projects that perform manual QA, I use a branch for each deploy environment to stagger rollouts, the final branch being for the production environment. In that case it makes sense to start a merge request on the production branch before master.


Speaking for myself, git flow is intuitive. It is something you also have to practice discipline to adhere to. That is not simply "additional cognitive load", it is just like any other process worth following.

Their criticism about "tripling the number of merge conflicts" because of the number of branches doesn't make any sense. As long as developers are doing development work and committing, regardless of what branch they're committing to, there is potential for conflict. It might not be a "merge" conflict in the sense of two separate branches, but it will manifest as a merge conflict in pushing the local to the remote. The potential for conflicts stay the same. What git flow gets right here is it isolates logical workflows from each other, so people working can work for as long as necessary without conflict.

>Gitflow abandons rebasing

Rebasing is a nightmare for distributed work, and in my opinion, only makes sense in limited circumstances where your code is not already pushed and pulled in multiple locations.

>Continuous delivery is a practice where the team release directly into production with each “check-in” (in reality, a merge to master), in an automated fashion. Look at the mess that is gitflow and explain to me how you’re going to be able to continuously deliver that?

Every modern CI/CD framework can run automated jobs after a merge into master, so I'm not sure what the criticism here is?

>Have you ever tried a complex branching model like gitflow with multiple teams, and hoped for everyone to be on the same page? Can’t happen.

That is less a criticism of git flow and more a criticism of any software organization that tries to have repeatable deployments with many services. It's a difficult problem in general, and not one that git flow is attempting to solve in the first place. You will have that problem regardless of if you use git flow or not.

I could go on but there's a lot here. It sounds like the author is very frustrated and annoyed with git flow, and perhaps is used to cowboying code (the desire for frequent rebasing give me a hint to this) without regard for the impact on other developers. I've worked with people like this, and they often provide no alternative solutions, just frustration and resistance to following an imperfect process. It's a rant without much substance.


> Every modern CI/CD framework can run automated jobs after a merge into master, so I'm not sure what the criticism here is?

The criticism (and the one thing I can sort of agree on) is that with git flow you are not merging to "master" on a continuous basis. Select quotes from the nvie post describing gitflow:

> When the source code in the develop branch reaches a stable point and is ready to be released, all of the changes should be merged back into master somehow and then tagged with a release number

> The essence of a feature branch is that it exists as long as the feature is in development, but will eventually be merged back into develop (to definitely add the new feature to the upcoming release)

> By doing all of this work [release prep] on a release branch, the develop branch is cleared to receive features for the next big release.


> If you pursue gitflow, you’re gonna have to give up rebasing. Remember, rebasing does away with the merge commit — the point where you can see two branches coming together.

I disagree with this. You can rebase your branch and then do a "no fast forward" merge. You'll end up with something like:

    * Merge commit
    |\
    | * My branch commit 3
    | * My branch commit 2
    | * My branch commit 1
    |/
    * Base
Notice I have not "done away with the merge commit".

When you rebase your branch on top of "Base", and then try to merge, git will by default do a fast-forward merge, but you can prevent it from doing so.

It is as though you created your branch, did your work, and then merged it back in so quickly that no other branches were created in the meantime, and if we ignore what I personally did on only my own computer, then I am still following Gitflow.


I dont even get why people rebase for a merge. I never did this and I get the entire history not a distorted version of it. On top of which I can go back between commits and even revert a very specific commit.

Reality is everyone prefers to organize code differently I prefer to have all the commit history. It is more meaningful to see all the files involved in a single stage of a commit than potentially multiple changes in one PR all blended together.


> I dont even get why people rebase for a merge. I never did this and I get the entire history not a distorted version of it.

Except that the history is disorganized and is not organized into distinct patches that only do one thing where each patch has a well written commit message.

People seem to want to treat the VCS as a back up system rather than a system to keep track of changes. Technically you could get the former by configuring your editor/IDE to generate a commit every time you save the file, but that in itself would lead to a very large history that wouldn't be useful (meaning commands like git blame would be next to useless).


> Except that the history is disorganized and is not organized into distinct patches that only do one thing where each patch has a well written commit message.

This seems like a separate problem... I know where commits come from cause the first thing I write on mine is the JIRA / Trac or other related ticket number / code. It is pretty obvious where a commit originated from if you do this. The rest is useful meaningful information. If somebody writes useless commit messages, that's on them and the rest of the team for letting them do so.


I hate rebases because they create multiple hashes for the same logical commit. It confuses the crap out of me sometimes. Also, they tend to involve more manual processing, and thus human error, then merges.


> I hate rebases because they create multiple hashes for the same logical commit

Some of those commits may have changed (either the commit message or the diff itself). There's a git command called range-diff[1] that was added recently that allows you to diff between rebases.

> Also, they tend to involve more manual processing, and thus human error, then merges.

Before actually pushing up the rebased branch, you run a diff against the upstream tracking branch to see if any inadvertent changes were introduced. For example:

  git diff @{u}..
And use the range-diff command to see the difference between the upstream tracking branch and your locally rebased branch.

[1] https://git-scm.com/docs/git-range-diff


This. I've had to spend time fixing history after a Jr engineer did a bad rebase. After a few episodes of that I decided I had to either stop hiring Jr engineers, have them always supervised by a Sr engineer, or stop rebasing.

I stopped rebasing.


You'd be better off blaming the force push rather than the rebase. A rebase I do on my computer will not affect you unless I force push.

I think it's completely reasonable to ask all developers to get a second opinion before force pushing.


> I've had to spend time fixing history after a Jr engineer did a bad rebase.

I tell people to run git log -p --reverse origin/master.. on the rebased branch and check that everything works before they push up their branch to the remote. I also tell them to run git rebase --exec "linting-command" --exec "unit-test-command" --exec "integration-test-command" and make sure that tests pass for every single commit. If they don't, then they spend time fixing it until they do.


Yeah, this is exactly right. I put up something on Github awhile back that compares different approaches in more detail, if anyone's interested: https://github.com/jiaweihli/git-workflow-strategies-example


Source repositories are all about managing complexity. The more unmerged code you have from your releasable branch... the more complexity you have being held in the source repository.

In the field I've seen git-flow lend itself to having trouble handling releasing some features and not others that have already been merged into a release or environment branch. This leads to cherry picking or more complex merging trees. With trunk based development, once a thing is merged to master it is shipped.. so it keeps the complexity being held in unmmerged branches to a minimum.


git flow never made sense for me. Because of the complexity some devs would always mess things up or ignore the branching altogether. Merging all the related branches into master when doing a release took multiple days and often introduced subtle bugs.

Switched at some point to GitHub flow and never looked back.


> Merging all the related branches into master when doing a release ...

--- doesn't sound like Gitflow. In Gitflow, the topic branches have been merged into the develop branch first, as they were finished. In contrast, merging into master is a merge from a single branch. You branch from develop into a release branch. Then you merge the release branch into master.


What if you have multiple ongoing release (maintenance) branches whose changes should all be merged to master and develop? It quickly becomes messy (but to be fair so does most alternatives).


That's not the problem with git-flow, that's the problem with discipline.


Gitflow is not robust enough to handle human error and is way too complex to onboard.

It’s a mediocre branching model incapable of handling a modern workflow, like continuous delivery and so on

We gotta tendency to excuse bad software as long as it’s old, especially on HN


I've have 400+ devs acoss several states working with 25+ repos, all using a modified Gitflow. It took a month of negotiation to get buy in, so far, it's been working for the last 2 and a half years.

And the beauty is, if a team wants to be more "agile"/more continuous, all we have to do is kill a few branches, and start building/releasing directly from master.


I work in a bigger place with a lot more repos in a company that’s used gitflow since it came out. What does have that to do with anything?

Why do you use agile in quotation marks? Is there something wrong with your keyboard? What’s the deal?


My company uses gitflow and releases daily from a monorepo with a team of 40 devs. Some of us rebase. Everyone is happy.

I think that disproves just about every point in the blog post.

I have no idea what non-obvious thing, if any, we're doing to make this work. Then again I don't think gitflow is complicated either.


I think the main point here is that it doesn't work for everyone, and is not a silver bullet. (Which is evident from comments)


Gitflow didn't just pop into the head of Vincent Driessen. It seems he curated an opinionated selection of the options from the book Pro Git (https://git-scm.com/book/) and I wouldn't be surprised if many projects were already using something like it to various degrees.

I went for many years with only a master branch and sometimes a develop branch, because I often worked alone. It took me years to venture into branching at all. Before Git I used Subversion. I don't think we branched once. But easy branching is one of Git's distinguishing features. For me, branching was like learning a programming language. At first it's mindbending and intimidating. But with reading, practice, re-reading, and more practice, it can eventually become second nature. The crazy diagram that George Stocker holds up makes perfect sense to me now.

I was forced to adopt Gitflow when I joined a bigger team. But now that I have learned it, I kind of like to use it even when working alone. Topic branches remind me of functions, in that they are small and encapsulated. I can concentrate on a narrow range of code and feel safe. The eventual merge might have conflicts, but they are soluble. And the way that feature branches stem from develop while bug-fix branches stem from master, and how you have a release branch to freeze features without freezing work --- I don't know, it feels very safe and structured.

However, yeah, like Stocker says, it may be too much ceremony, especially if you're just starting out. I like to tell people to ease into it. Stop when it hurts.


From my days working at a perforce shop... Advanced SCM Branching Strategies was something that I was familiar with. https://www.vance.com/steve/perforce/Branching_Strategies.ht... (from '98).

The essence of it is establishing different roles for different branches. mainline, development, maintenance, accumulation and packaging.

With those roles in mind, when I was introduced to git flow it made sense - mainline is develop branch; development and maintenance roles are done in feature branches. Accumulation isn't one that is seen too often but should be kept in mind, and the release branch is the packaging role.

These ideas have probably been realized time and time again.

The conclusion of the paper:

> Planning and analysis are critical to the success of any SCM system. A branching strategy motivated by a risk-based analysis of the organization's development needs will provide a strong foundation. Incorporating the concepts of branch roles, codeline policy and codeline ownership will assist in performing the required analysis. Application of the principles of branchpoint, merge policy and branch life span will ensure that the parameters governing codeline policy are properly and completely addressed.

> Once the branching strategy has been formulated, the organization can implement the customizations required to make the SCM tool suit its environment. Until SCM systems have reached sufficient maturity to address the larger issues of policy, adopting the practices put forth in this and other papers will help an organization achieve success in their software development endeavors.


I've never liked git flow due to:

A) the way it abuses the commit DAG: you never merge master directly back to develop, and so your tags don't become ancestors of the commits on develop until some timer later when an unrelated hotfix is branched from master. E.g., you can't rely on "git branch --contains v1.0.1" to tell you which branches do/don't contain the 1.0.1 hotfix because the hotfix branch was merged to develop, but not the merge commit that was tagged as 1.0.1.

B) its blatant disregard for one of the most basic software development principles: when you want to deploy a new version of your software, you test a specific a revision of your code and that's the revision that you deploy. With Git Flow, you go to great lengths to ensure your release branch is isolated from other work for testing, but then you merge to master and deploy the merge commit instead of the commit you just tested.


When I first researched git, this blog post came up. It's been around forever.

My first thought when reading it, though, was, "huh, we seem to be doing that with SVN already." Indeed, gitflow just seems to be a popular SVN flow ported to git.

Looking at the diagrams, there seems to be a lot of branching and merging, but in practice, it's far less than merge early, merge often. In using SVN, though, you can see why. SVN thrived in a day with more large, monolithic codebases. Further, branching in SVN was heavy.. It made a copy of the entire codebase. It wasn't fun because it took so long. Gitflow emerged from an SVN world.

Nowadays with microservices, modular codebases, and CI/CD, a merge early, merge often approach with git makes more sense. There isn't nearly the overhead or pains of branching and merging with git as there is with SVN.


100% agreed. The workflow suggested in the viral blog post is far too complicated, even for experienced teams. But in practice what happens is that the teams who are most likely to base their git practices off some random blog post instead of off their own experience are the least experienced teams.


I recommended avoiding Gitflow back in 2018:

> There are more complicated systems for branching like Git flow that have a separate “develop” branch and distinguish between “features” and “hot fixes”. These systems are fine if they fit your use case, but if a project releases code quickly, the level of complexity involved in Git Flow is unnecessary.

> Ideally, branches should be short lived. One strategy to allow merging a branch before a large feature is fully completed is to create a feature flag that lets the production system have the new code in it but in an inactive state.

https://blog.carlmjohnson.net/post/2018/git-gud/


The article claims that "gitflow abandons rebasing", but this is simply not true!

In my team we use gitflow with rebasing. In fact, the original gitflow implementation comes with rebasing support built-in (as a flag for the "git flow feature finish" command). We just modified the script to enable it by default.

Another thing we changed is dropping the "master" branch, since it didn't provide anything useful.

Now, you could claim that this means we're not really using gitflow, due to all these modifications. But the fact is, I still give this article as mandatory reading for new developers in our team, and it's still a great introduction for working with feature branches. I'll continue to recommend gitflow.


Another thing we changed is dropping the "master" branch, since it didn't provide anything useful.

This is the one thing about Git Flow that I never quite understood. Feature branches, great. Main develop line, sure. Release branches, OK. But what is the advantage of maintaining a master branch that gets some nominal end point for releases merged in and tagged?

In reality, one of the main advantages of a Git Flow style of workflow is that it doesn't assume you can just conveniently forget history, maintain a single current version of your product, and deploy changes on a whim. Instead, you can maintain a normal process for new development, but also keep control of old releases and of release processes that might involve significant extra steps. But all of the latter benefits come from using release branches, not the odd way of merging from those branches to master, which isn't at all used in the conventional way in Git Flow.

Indeed, the master branch implies a linearity of releases that probably doesn't make much sense for a lot of projects. You might need to push out something like a security fix for several older versions, which is easily represented by making a new tagged public release from each of the earlier release branches, but then what even should be merged to master according to Git Flow?


Git flow is complicated. So is software. If you work at a place that still ships software instead of just running services, you need something more than continuous delivery and a trunk based workflow. If you provide services, you need to care about at most 3 versions of your software at the same time: the version running in production, the version before it, and the version after.

If you ship software you might have to care about every version you ever released to in the last 2 years plus that one super old version your second customer is still using because they refuse to upgrade and shit you need to fix a critical vulnerability that was just published in it.

Trunk based dev doesn't cut it in that world.


The most salient points for me were actually made in the last paragraph. IMO would have improved the article to put this at the top rather than at the bottom.

> The crucial point I’m making is to is ask questions of your team: What problems will this branching model help us solve? What problems will it create? What sorts of development will this model encourage? Do we want to encourage that behavior? Any branching model you choose is ultimately meant to make humans work together more easily to produce software, and so the branching model needs to take into account the needs of the particular humans using it, not something someone wrote on the internet and claimed was ‘successful’.


I used a watered down version of gitflow on a project with heavy customer involvement.

Instead of doing the whole thing, we had feature branches (which I still hate), master, and one more branch for the stuff we were showing to the customer. Who sometimes would decide they didn't like what they asked for and please don't push this to production.

With the exception of hotfixes, master was always just a certain number of commits behind the working branch.

Very aggressive use of dark launching of features could also solve this problem, but we've had our share of problems with that strategy too (Sprint-obsessed thinking makes it difficult to go back and clear out old conditionals once they've settled)


One would think by simply looking at the picture that gitflow is not well suited for processes that do not involve distinct releases. In other news, SaaS is not the only model in the world and CD is not always possible.


My team uses git flow as the basis for our branching strategy. We’ve streamlined things a bit over the years as we’ve found we didn’t need all the flexibility/complexity in that model, but we’re still using a develop branch, feature branches, release branches, tagging our releases, etc.

It works fine for us, and we spend very little effort on branching or merging. I wouldn’t hesitate to recommend git flow as a starting point to any team. Just make sure you don’t follow it dogmatically... if there are tweaks that you can identify to better serve your team, go ahead and make them.


I disagree with the article. there are pitfalls you can fall into using gitflow, but they're not inherent to that strategy.

pointing out the obvious: a master branch is technically redundant for gitflow. you just need to create a tag. it however makes it easier to visualize and think about the flow in regard to hotfixes.

next obvious point: you're just not releasing quick enough if merge conflicts become an issue with gitflow. personally, i think release branches are just quick snapshots you can use to safely run all tests against while other developers keep pushing features on develop. this gives you a short timeframe to merge bugfixes into the release branch, but it really shouldn't exist for > 2 days (one day to identify issues and fix them, another to verify that they're gone).

nor do i see any issue with continues integration, honestly... gitflow just formalizes the way code goes into production. if anything, it makes it easier! now you can automatically spin up new environments for release/* branches to automatically trigger a system test suite, appending the result to the release.

i dont get his issue of multiple repositories either... each microservice surely has a different version, no? so for what conceivable reason would gitflow create any issue here?

if you're a three man team, gitflow is probably unnecessary though. its meant for bigger projects with lots of developers pushing at the same time.


We use a variant of Gitflow without a develop branch and without release branches.

Gitflow has 2 major problems: - develop branch that will always become a mess over time - release branches are never completely accepted. It is much easier to accept features branches.

We try to merge feature/bugfix branches directly to master when they are approved.

We have testing/staging branches that can be thrown away and re-created from master and re-merge any feature/bugfix branches that have to be checked.

This has worked very well for the last 5 years or so and we have used it in dozens of projects every year.

The fact that testing/staging contains no commits (except merge commits) of its own is very useful. You can drop these branches regularly and remove any old feature branches that became stale for some reason.

Lastly, I have seen a lot of teams that want to apply complicated Git strategies from day one of a project where it would be much better to just work on one master branch because too much code changes at the same time. This will create complicated conflicts. The history at the start of the project is not interesting. Git history only becomes useful when the project is live and you want gradually add features or fixes.


Please stop using "Please stop..." in the title of blog posts. It has a passive aggressive, techbro culture, holier than thou ring to it. Which I find grating.

That aside, there's not much in this article anyway. Despite trashing it in the title, he goes on to say it might work in large organisations or those with a long release cycle, but otherwise can't recommend an alternative.

Ok then.


You would prefer:

"Gitflow considered harmful",

"Why I don't use Gitflow",

or any of the other dozen clickbait variants, then?

At the end of the day, people enjoy writing contrarian posts, and people enjoy reading them. They have to be named something. People who use title templates are no more annoying than people who code things they dislike as masculine.


It's as much of an article as one advocating a tool (or workflow) without exploring its weaknesses. In a way, both complement one another.


Rename it to "Gitflow considered harmful".


I think Gitflow is the larger picture, but I think most teams can afford to merge parts together.

I run a small team and we typically will just use "feature" and the master branch. If it compiles and passes the basic tests, it's master worthy. If it passes more heavy testing then it's tag worthy. Features can sometimes be bug fixes depending on the size.

Typically we try to merge branches in less than two weeks to prevent some kind of merge hell.

The assumption that master is somehow perfect is wrong and it shouldn't be treated as such. In my opinion tags are really for commenting that there's something interesting about a commit (i.e. "we ran this for 2 weeks at full load and no issues observed"). Again, tags also don't say there are no issues, it just says there was something note worthy.


I really wish the article covered some alternatives styles.


I highly recommend Trunk Based Development:

https://trunkbaseddevelopment.com

"A source-control branching model, where developers collaborate on code in a single branch called ‘trunk’, resist any pressure to create other long-lived development branches by employing documented techniques. They therefore avoid merge hell, do not break the build, and live happily ever after."

In my experience, new/small teams do well if they stick the "Scaled Trunk-Based Development" model until a legitimate reason to deviate is discovered (e.g. needing to back-port changes to an older release may imply making commits to non-trunk/master branches...).


I default to recommending either Github Flow[0] or Gitlab Flow[1] these days.

Both of these models simplify Gitflow by dispensing with the long-lived development branch and therefore the complication of needing separate hotfix vs release workflows.

The difference between these models is that Github Flow expects you to deploy to environments within a pull request before merging to master, whilst Gitlab Flow expects you to merge to long-lived environment branches for deployment (presumably using Gitlab's CI features).

[0] https://guides.github.com/introduction/flow/

[1] https://docs.gitlab.com/ee/topics/gitlab_flow.html


It's of course possible to have a meaningful discussion about different types of branches, releases, changes and about what changes should be merged, when, and where, but ultimately there is only one good branching strategy: thinking.

Thinking about the purpose of branches, thinking about whether everybody is on the same page and what mistakes are possible, thinking about useful rules about changes and merging (possibly on a branch by branch basis), thinking about when to break and how to adapt the rules, and above all thinking about the objective of making correct and low-effort merges.

Adopting practices because a blog post says so is the opposite of maturity. As Gitflow is presumably a good fit for the project it originated in, any "alternative styles" are suitable for someone else's particular needs and should be treated as examples and anecdotes illustrating general principles.


I use:

1. Master should be always deployable:

2. All features are a separate branch BUT, when merged into master they should also have a config for ON/OFF. This has the benefit of quickly switching between old and new behaviour(feature) as well as forcing a "module-based-architecture" for the code.


One problem with #2 is many types of changes are impossible to toggle on/off (I mean "change to codebase" not "product feature").


True, #2 is not always feasible, but lol the dev can damn well try :P


At my current job when I started people were doing git flow. It was horrendously complicated. I got rid of it and now everyone is happier.

The idea is really simple. You have one eternal branch, "master", which is your integration branch. This is set to be the upstream of all other branches during development. That means it's really easy to keep rebasing on master every day. When your branch is ready it's merged into master (I do fast-forward only, but that doesn't matter). When you want to release, just tag a commit on master. If you want to maintain releases, make release branches for these and cherry pick fixes from master. Reset the maintenance branches to whatever release you are maintaining. It's easy.


GOTO 2017 • Feature Branches and Toggles in a Post-GitHub World: https://www.youtube.com/watch?v=lqRQYEHAtpk

TL;DW: integrate continuously, don't use branches, use feature toggles.


I recall converging toward something a lot like git-flow because, especially when there were hot fixes, you might get conflicts rebasing the feature branch and conflicts again merging the feature branches, doing the work twice at least. Additionally, it was so important everyone used the same procedure to accomplish merges or whatnot or else there would be all kinds of problems.

Sometime ago I noticed I hadn't seen this in a while. Is this anyone else's experience? Have people just gotten better at git or was there a major improvement in how git handles this scenario?

If it was about improvements, has anyone written those changes up? It would be fascinating to know how it was done.


> While I hesitate to say “Worrying about merge conflicts” is a valid reason not to pursue a branching strategy like gitflow

Being worried about merge conflicts might be a sign that your continuous integration might not be continuous enough.


I implemented a variety of GitFlow, but with an emphasis on automation. Normally you branch off of develop; hotfixes will be a branch off of master.

Feature branches are combined with develop before being built and tested. If it passes, it becomes mergeable in GitLab.

all builds of develop that pass are taggable; you go into the CI interface and specify a tag. Once deployed, master is updated by the CI to point to that tag.

The most valuable part of this is the CI pipeline and having a solid body of tests to ensure that changes which don't necessarily conflict in Git will most likely be caught during test, prior to it being allowed for merge.


Some of the criticism in the article is unfair.

Most obviously, the boundaries of source code repositories should be aligned with released modules: if "the system becomes a manifest of the different revisions of the different repositories" the repositories are too fragmented (or the releases too monolithic), if "teams are independent and microservices should be independently deployable" disparate components should not share a monorepo, and in both cases the difficulties are consequences of bad architectural choices, not of branching models.


Funny, I always interpreted the gray branches in that diagram to mean short lived branches.

I just have dev and master with all my projects. Everything else is short lived. And short lived branches are rebased and squashed. Also, all devs work with forks to keep upstream pristine.

And there are those dogmatic about trunk development. I think if you follow the rule of thumb of don’t let dev and master get too out of sync you don’t need to do trunk.


The author claims that "rebasing does away with the merge commit."

It's true that if you rebase you won't need a merge commit, but rebasing is compatible with the idea of having a point of reference between where branches come together, and you can accomplish that by introducing a merge commit with "git merge --no-ff" so the rebased branch doesn't fast forward on top of the target branch.


We use several branching strategies - all depends on the size of the codebase and the number of teams involved. Some work just fine with a trunk based workflow, some need that extra bit of layers.

What I don't get is the infatuation with GitOps, where someone insists on replacing a process with a source code repository and webhooks. Starting to see excessive scripting/bots to talk with GIT bots...and it is so kludgy.


In my ignorance I once pondered a model where [rather than teams] everyone gets their own object named after them. Something like JohnDoe.sortTable(foo)

(Besides from being a stupid idea on so many levels) it seems fun to reason about: John doesn't sort the table properly. When John gets hit by the bus you have a really good idea what you've lost. You also get to identify the productive and unproductive.


For the very longest time I thought I was doing gitflow (I skimmed the article back in the day). I wasn't.

What we do is master is always green, prod ready. Good To Go. Auto shipped to prod by our CI/CD.

Develop _should_ always be good to go. Green. Auto shipped to staging by our CI/CD.

Then we have feature/ch1238-foobar-baz branches for every feature. Go crazy in there, deliver a completed feature.

Is there a need for more process than this?


I think gitflow might be suited to mature product work where development is ongoing.

It keeps coming up in my consultancy work, usually on short term projects that either die or generally just get left to tick over where it's wildly unsuitable. Mostly because of the way it violates that principle of short lived branches.


Anecdotally, it's worked well for my current project as any other branching model I've worked with. It allows release validation to happen while developers continue to work on features.

The author seems to bash the workflow without offering up any solution or alternative.


Gitflow is really meant for software that multiple released versions. It allows for applying bug and security fixes to older released versions. For a project that has a single released version, it really doesn't provide any benefit.


> Take a look at this image and tell me it’s immediately intuitive

I'm using Git, there's nothing intuitive to be found here.

On a possibly related note, I watched a talk by D. Richard Hipp (I don't remember which one - edit: probably "Richard Hipp - Git: Just Say No"[1]) where he compared it with Git (a lot) and explained how Fossil keeps everything, there's no throwing away of history, no need for rebasing (at least as a workflow). It sounded nice - refreshing, even. I'm going to start using it for some projects and see how I get on, because frankly, I'm tired of Git and this need to strategise saving copies of my work, because that's basically what it does.

Sometimes I even pine for the days I used to version zip files…

[1] https://www.youtube.com/watch?v=ghtpJnrdgbo


On the contrary my team and I have been having great luck with git-flow.

When I onboard a new team member I watch them struggle with git-flow and for me that's a great thing to see because I see them learning an efficient way to work.

Once they 'get it' everything flows smoothly, and everyone has the same way of working which makes it very easy to see and help debug when things happen. Communication is also consistent across all team members partly because of the git-flow branching model. We as a team understand why / when to use feature branch, when to use hotfix, when to cut a release etc... That's what I expect from a team. Git flow has been great for our team and workflow.

To each their own. I guess I will continue recommending git-flow.


> I watch them struggle with git-flow and for me that's a great thing to see because I see them learning an efficient way to work

Maybe they're struggling because it's not efficient, or intuitive?


No they struggle because they don’t know the workflow which is what gitflow teaches them. Once they learn it things work very well and they go on to teach other people.


IMHO GitFlow often works, as is a decent baseline.

When people don't use GitFlow (i.e. people starting their adventure with git, with no supervision), very often they report to one of the above:

- one master, and SVN-style

- personal branches, and marges in all directions

- even more mess

Sure, they may be projects in which a different commit/branch/merge workflow is better. (In many projects I did some adaptations for GitFlow, depending on the project scope, scale and skill of programmers.)

However, the original author, after a long criquite recommends nothings (to my disappointment). So, what's the point? Going back to this "anyting marges with anything" branch Wild West, as a baseline?


> Ok, so my team shouldn’t use gitflow. What should we use? I can’t answer that.

this article is worse than useless. it's just a rant about inconveniences without any solutions


Thank you for that feedback!

I purposefully didn't provide any solutions in that blog post because the entire point of the blog post is for a team to figure out what works for them, in their context, culture, and constraints.

There are lots of different workflows:

> Feature branches

> Merge to Master

> GitHub Flow

> GitLab Flow

> GitFlow

I've also seen weird mutations where each developer reuses a branch and just does new pull requests from that an nukes the remote each time. it's problematic, but some teams swear by it (they also don't do any pairing or code reviews by pulling down branches).

This isn't the first time I've heard this bit of feedback, so I think i may put together a blog post on the different styles and their uses (and limitations). But again, it's up to you and your team to figure out what works in your culture, context, and with your constraints.


While I wouldn't give as much weight to microservices—because 99% of teams shouldn't be using them—he makes quite a few good points.


funny thing about git flow is that working with nvie (who coined/created it) at gitprime, they did NOT like it in practice. we just used feature branches go figure


We use a rebase heavy workflow and I love it.


Without looking at how Gitflow works it seems like it’s some Adhoc branch system a lot of company uses in one form or another.


Gitflow was just a silly blog. I can't even begin to understand how it became dogma


One of the challenges of git a decade ago was that people created branches everywhere and it became difficult to reason about what branches were doing and what work should be done in them.

Git flow, at that time was the most reasonable model for git branching.

By reasonable I mean "able to be reasoned about". If I see a feature branch, I know what should be in it. I know what should be in a release branch. I know what the state production is in.

It isn't the best branching model - but when faced with the contemporary other branching models, git flow let was an attempt to put some order to that.

It also works fairly well. It isn't so much that its dogma, but rather that it is consistent and understood across different teams. The problems that it solves in communication are more important in many places than the ones that it causes in other parts of the workflow. Coming up with a new workflow isn't hard - just that one now needs to solve a different set of problems and most people don't want to have to solve those problems... and so git flow is an acceptable default.


can I double vote this? :))


Most of this article is nonsense. It's true, gitflow is not a silver bullet for every single situation, and if you've got a more complex situation than a single team working on a single project, you're probably going to need something that takes the complexity of your situation into account. It's also true that gitflow might be a bit too detailed and complex for simpler situations, but you can easily drop parts of it. It's not a universal solution to everything, but it's a decent basis to start from, particularly if you have no idea how else to organise your git workflow. Because everybody using their own approach without any coordination is definitely going to be worse.

> "Gitflow abandons rebasing"

It doesn't entirely do that, but it does abandon rebasing in situations where it should be abandoned. Rebasing is dangerous. Rebasing changes your history, and as any time-traveler knows, messing with history is risky. If you change history that you already shared, you can end up duplicating it and making a mess.

Rebasing is only ever fine for local changes, and you can still use it there in gitflow. But if you merge a feature that took some time to develop, into the develop branch, rebasing is a terrible idea, and it should really be done with a merge.

> "Gitflow violates the “Short-lived” branches rule"

Is that a rule? Some features really do take time to develop. Any workflow has to account for longer lived branches. Also, if code has to be reviewed before it can be merged, that also extends the lifetime of branches. Fortunately big merge conflicts are easily prevented by regularly pulling the latest develop into the feature branch.

And any situation where you have multiple people potentially changing the same file, is going to lead to merge conflicts no matter what git workflow you use.

> "Gitflow makes Continuous Delivery Improbable"

Nonsense. Once code has been merged into develop, it's ready to deploy. Well, I suppose this one is correct if you consider dropping even the tiniest part of gitflow as completely abandoning gitflow; all continuous delivery teams I've seen, don't use explicit release branches; they just deploy develop (or merge to master and then auto-deploy that).

The only argument the article uses here is "look at all those lines and dots! this can never work!" Well, it can and does.

I've worked in teams that use short release cycles, up to daily, and use a workflow that's pretty close to gitflow. It's a decent default to start from, but if you're working in a more complex environment, with multiple teams working on the same code base, parts of which need to be releasable independently from the rest, then yes, gitflow might not fit your need. So adapt.

See gitflow as a starting point, not as a silver bullet. There are no silver bullets.


> Well, I suppose this one is correct if you consider dropping even the tiniest part of gitflow as completely abandoning gitflow;

Thank you for that feedback.

It's important to note that my blog post rails against Git-flow as described by that blog post, not necessarily as practiced by teams. If teams drop the problematic parts of Gitflow, of course they'd be able to use Continuous delivery!

But the point of Git-flow is very much to not practice Continuous delivery (the textbook example; again if someone practices what they call CD but it isn't what the textbook says "Continuous Delivery" is, I can't help that.

> See gitflow as a starting point, not as a silver bullet. There are no silver bullets.

Now you're getting to the crux of my issue with the original (has since been updated) blog post. The Gitflow the OP described is problematic for all the reasons I laid out in my post (And more that I didn't list, that others have captured in the comments). It necessarily stands to reason that if you don't take gitflow at face-value, you'll be better off -- but that's not what I'm arguing against. I'm arguing against practicing Gitflow as laid out by the OP.


Gitflow might work for some projects/teams but it shouldn't be treated as a reasonable default.


As far as I can tell, the only difference between git flow and not git flow is whether you have a Develop branch or not


I've been swayed to the side of believing that rebase should rarely be used, so that's not much of a downside. See: https://www.fossil-scm.org/fossil/doc/trunk/www/rebaseharm.m...


Are people really calling this specific model of git branching THE Gitflow model ? For me this was a gitflow amongst others, a gitflow being a git workflow.

In the original article "A successful Git branching model" I don't see a single mention telling people they should call this specific model Gitflow.

For me the word gitflow has the useful meaning of a git branching model, am I in the minority ? Using it as a noun for a specific model seems like a waste of the word.

In regards to the article, sure this gitflow is one of the most shared images on the subject. However I recommend that every team use whatever gitflow makes the most sense for them and their project. I was not aware that our industry had a problem with teams cargo culting that specific git workflow.


Interesting. I have only ever heard "gitflow" used to refer to the specific model described in https://nvie.com/posts/a-successful-git-branching-model/ . The author also wrote a tool, "git-flow", to assist with this model https://github.com/nvie/gitflow .

I have heard the terms "git branching model" or "git workflow" used for the concept that you call "gitflow".


nvie/gitflow is a long dead project. There's an active fork at https://github.com/petervanderdoes/gitflow-avh


Thanks for the link, at the very least Jeff Kreeftmeijer in 2010 already used the name git-flow for that specific model. I somehow missed all of that.

Plus nvie.com updated its 10-year old post with a notice yesterday and also uses the name git-flow there.

I guess I'd better use the term git workflow in the future.


+1. We use as many branches as it makes sense on a particular project. Simple ones might have just master (and temporary feature/bugfix branches). Only when we need to have a LTS version do we introduce another branch for it.

I always thought this is also Gitflow, but reading this article, I'm not so sure anymore.


> Have you ever tried a complex branching model like gitflow with multiple teams, and hoped for everyone to be on the same page?

My company does exactly this and I am happy to report there have been no issues. I think the key reason we don't have issues is we are strict about maintaining backwards compatibility between services and also try to maintain forwards compatibility where possible.

> “What’s in production” becomes an existential question, if you’re not careful.

We solved this by embedding the SHA's of a build's constituent repos in the build artifact.


I will admit I would not know the difference between gitflow and a hole in the ground. But that diagram looks an awful lot like the workflow you tend to pick up when using fossil.

  * The primary dev branch
  * branch and merge for feature development
  * long term branch for each release.
The authors only real concern appeared to be no rebase, and rebase is already a big no-no in any sort of distributed development, just adopt that attitude for your personal development.


> rebase is already a big no-no in any sort of distributed development

You are ill-informed on this subject, and I have a proof-by-existence.

Sun Microsystems, Inc., used a rebase workflow from the mid-90s all the way until the end, at least for the codebases that made up Solaris (OS/Net and other consolidations), and it did so while using a VCS that did not support rebase out of the box (Teamware, over NFS). It was essentially a git rebase workflow years before Larry McVoy created BitKeeper, and even more years before Git was created, and even more years before the rebase vs. merge workflow controversies.

When I was at Sun we had ~2,000 developers on mainline Solaris. The rebase workflow worked fantastically well.

Large projects had their own repositories that were rebased periodically, and team members would base their clones on the project's clone (known as a "gate").

Large projects' gates would get archived for archeological interest.

All history upstream was linear. No. Merge. Commits. (We called them "merge turds", and they were a big no-no.)

Solaris development was undeniably distributed development.


> The authors only real concern appeared to be no rebase

This is my take as well. That blog post has not much to say about anything, mostly weak points stemming from personal distaste of some things, but it boils down to 'i like rebase more'.


There's nothing wrong with gitflow... The problem comes with how it's managed. The problem comes when a risk-averse manager wants to follow a 1990s release cycle, and release and feature branches live forever.

Release branches should be created shortly before release, and shouldn't deviate too much from develop. Feature branches also shouldn't last very long.

If a feature is going to be under very heavy development before shipping, a better strategy is to #ifdef the feature. This helps avoid accumulating merge conflicts that happen when there are long lived branches.


There’s a lot wrong with gitflow. For one, the very existence of a release branch.


In real world software dev, I fail to see how a release branch is a flaw. It’s completely reasonable to want to test something in a “gold-master” state before shipping it to prod. Regardless of how many tests you have, that’s just a good idea.


Yeah it's what we call staging. I have a project now that doesn't have a staging environment that's well integrated and it's very nerve-racking. I use hg but it's the same idea.


Staging corresponds to master, tags are branches.


We have a branch called “live”. That’s on our production server. Staging is testing to make sure our dev is worthy of moving to live.


That’s what versión tags are for. Plus real world software development, what does that mean? Just say it straight up: you don’t think I have a real job


No, that's not what I meant and I apologize if it came across that way. What I meant was, it's easy to think of an idealized world where a everything has test suites with 100% branch coverage, complete e2e tests, automated screenshot tests of the UI, etc. But, in the real world, that most often isn't true. I've been on a lot of teams. Lots of legacy code has no automated tests at all. Lots of modern code has decent unit test coverage, but nothing for the UI.

These are cases necessitating a manual testing step before releasing to prod, and manual testing will often uncover bugs that need fixed. Thus, you need to be able to fix these and continue testing, all the while having as little impact as possible on other Devs working on things unrelated to this release.

Having either a single long-lived release/QA branch or shorter-lived release-specific branches accomplishes this in a reasonably simple way.

That's what I meant by real-world software development. If you work at a shop with an amazing test suite and don't need these steps, good for you. But, in my 15 years of development, I've never seen a shop like that.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: