Please stop recommending Gitflow
georgestocker.com
georgestocker.com
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.
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.
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.
Are there really teams that don't do this?
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.
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.
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.
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.
Master is a working branch. Releases are pushed to Release and tagged with a date.
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.
You still should merge often to develop, which when tested becomes master.
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.
=======
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.
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).
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...
> 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.
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.
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.
> 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.
> 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.
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.
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).
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.
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.
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.
> 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 :)
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.
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 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.
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.
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.
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.
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.
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.
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.
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.
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?
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.
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)
Gitflow is fine - better to use it (w/the girl extension) than spend time arguing branches.
It could be said:
> What we found to be most workable is develop is always "stable", then we deploy from master.
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.
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.
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.
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!
> 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.
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.
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
I think this is a good treatment about why rebasing is bad: https://medium.com/@fredrikmorken/why-you-should-stop-using-...
+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.
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).
git log --simplify-by-decoration* 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.
1. See success
2. Want success
3. Copy behaviors
4. Behaviors were only part of successNo doubt there will be arguments for how oracles for ethereum are different, and so on
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.)
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 ;-)
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.
It's a bit of a joke.
> 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.
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.
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!).
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).
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.
That way, merge conflicts, if any, are limited to merges that involve porting bug fixes back to earlier released versions.
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.
99% of the behavior it implements people do not use.
Git is not the correct level of abstraction.
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.
"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.
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 :-)
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 like presenting and merging tidy changes.
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/...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.
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~
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)...
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.
Git springs to mind for one, but (capital A) Agile definitely wins the championship.
* Submodules
* Binaries
* Large Repositories/branches
* A disparate set of development platforms/clients
* Git auth/credentials
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.
> * 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.
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?
The git portion of that is pretty straightforward, but whatever the linter is doing looks rather complex.
You make unwarranted assumptions about another person's experience base simply because it supports your position to do so.
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.
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"
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.
If you only ever use `git add -N .` and `git add -p` you'll never have unexpected changes in your commit.
And yet I don't run into such behavior from people using SVN or mercurial.
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.
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.
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.
Ah yes, good old normative claims.
The market wants what the market wants.
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.
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".
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.
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.
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.
Didn't you just break the golden rule of Git which is to never rebase onto a public branch?
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#...]
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.
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 ;)
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 .
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.
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 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.
Merging doesn't have that problem, it usually just merges fine unless there are conficts in the latest commit (not all previous ones).
is this solved by enabling git-rerere ?
https://medium.com/@porteneuve/fix-conflicts-only-once-with-...
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'd really like git to have this feature built-in to it...
One can implement that even with Fossil or any other VCS that pretends it does not implement rebase and pretends it never will.
(Viktor here is https://github.com/vdukhovni/, and I did heavily alter that code.)
Thanks for the gist
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.
[0] https://gist.github.com/nicowilliams/ea2fa2b445c2db50d2ee650...
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.
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
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.
Here's Linus Torvalds' thoughts on rebasing. https://yarchive.net/comp/linux/git_rebase.html
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.
But once history has been shared (pushed, generally), it really shouldn't be touched anymore.
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.
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.
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.
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.
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.
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.)
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.
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.
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 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.
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).
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.
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.
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
}It makes me believe there is a real existential problem with git as a product.
... 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.
You can speak for yourself. Don't speak for me or my colleagues.
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.
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.
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 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.
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.
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.
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.
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.
* 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.
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.
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.
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...
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.
So what? Those branches live for exactly as long as the minor stuff takes to develop and review.
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?
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).
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!
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.
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.
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)
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.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.
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?
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.
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.
(Personally I think tests keep me from breaking my and others' code, but I don't tell them that)
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.
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 sensible trunk-based development with feature & release branches (equiv to what MS call 'Release Flow') work well from small to huge (400+ devs) environments.
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.
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.
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 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.
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.
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.
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.
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.
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.
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 stopped rebasing.
I think it's completely reasonable to ask all developers to get a second opinion before force pushing.
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.
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.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).
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.
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.
Switched at some point to GitHub flow and never looked back.
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
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.
Why do you use agile in quotation marks? Is there something wrong with your keyboard? What’s the deal?
--- 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.
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 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.
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.
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.
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.
> 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.
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.
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?
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 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’.
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)
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.
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.
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.
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.
"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.
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.
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...).
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.
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).
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.
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.
TL;DW: integrate continuously, don't use branches, use feature toggles.
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.
Being worried about merge conflicts might be a sign that your continuous integration might not be continuous enough.
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.
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.
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.
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.
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.
(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.
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?
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.
The author seems to bash the workflow without offering up any solution or alternative.
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…
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.
Maybe they're struggling because it's not efficient, or intuitive?
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?
this article is worse than useless. it's just a rant about inconveniences without any solutions
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.
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.
> "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.
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.
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.
I always thought this is also Gitflow, but reading this article, I'm not so sure anymore.
I have heard the terms "git branching model" or "git workflow" used for the concept that you call "gitflow".
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.
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.
* 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.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'.
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.
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.
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.