├── README.md ├── file1.txt ├── file2.txt ├── file3.txt └── images ├── commits-master.png ├── commits-new-feature.png ├── commits-using-merge-no-ff.png ├── commits-using-rebase.png ├── commits-using-squash.png ├── diff-using-merge-no-ff.png ├── diff-using-squash.png └── network-graph-cachebusted.png /README.md: -------------------------------------------------------------------------------- 1 | # Git Workflows 2 | 3 | You've finished a feature and are ready to deploy it. But first you need to add it to the `master` branch of your repository. It turns out you have a lot of options, each with their own tradeoffs. 4 | 5 | ## An interactive visual walkthrough 6 | 7 | Let’s say master is in [this state](https://github.com/jiaweihli/git-workflow-strategies-example/commits/master). 8 | ![commits, for master branch](https://raw.githubusercontent.com/jiaweihli/git-workflow-strategies-example/master/images/commits-master.png) 9 | And we have a [new feature](https://github.com/jiaweihli/git-workflow-strategies-example/commits/new-feature) we’ve been working on. 10 | ![commits, for new-feature branch](https://raw.githubusercontent.com/jiaweihli/git-workflow-strategies-example/master/images/commits-new-feature.png) 11 | 12 | We could fold this into `master` [via `rebase + squash`](https://github.com/jiaweihli/git-workflow-strategies-example/commits/using-squash). 13 | ![commits, using squash](https://raw.githubusercontent.com/jiaweihli/git-workflow-strategies-example/master/images/commits-using-squash.png) 14 | Or, we could fold this into `master` [via `rebase + merge —no-ff`](https://github.com/jiaweihli/git-workflow-strategies-example/commits/using-merge-no-ff). 15 | ![commits, using --merge-no-ff](https://raw.githubusercontent.com/jiaweihli/git-workflow-strategies-example/master/images/commits-using-merge-no-ff.png) 16 | 17 | It's easy to misunderstand `rebase + merge —no-ff` as a linear history that makes it hard to see what a complete feature looks like. Kind of like a [pure `rebase` workflow](https://github.com/jiaweihli/git-workflow-strategies-example/commits/using-rebase). 18 | ![commits, using rebase](https://raw.githubusercontent.com/jiaweihli/git-workflow-strategies-example/master/images/commits-using-rebase.png) 19 | 20 | But this isn’t the case. Actually, if you look at the [network graph](https://github.com/jiaweihli/git-workflow-strategies-example/network): 21 | ![network graph](https://raw.githubusercontent.com/jiaweihli/git-workflow-strategies-example/master/images/network-graph-cachebusted.png) 22 | `merge —no-ff` and `squash` look very similar. 23 | 24 | See how both both `using-merge-no-ff` and `using-squash` only have 1 commit in mainline? 25 | We can examine the commit [from `using-merge-no-ff`](https://github.com/jiaweihli/git-workflow-strategies-example/commit/93f6de5073a11cc1b6d4d62e89b8c4da6f0e6bcb). 26 | ![diff, using --merge-no-ff](https://raw.githubusercontent.com/jiaweihli/git-workflow-strategies-example/master/images/diff-using-merge-no-ff.png) 27 | And, we can examine the commit [from `using-squash`](https://github.com/jiaweihli/git-workflow-strategies-example/commit/3c4417df87c588e07325113d1f70831c5bedb559). 28 | ![diff, using squash](https://raw.githubusercontent.com/jiaweihli/git-workflow-strategies-example/master/images/diff-using-squash.png) 29 | 30 | They’re actually the same! You can revert both of these commits to revert `new-feature` if you need to. 31 | But `using-merge-no-ff` has the added benefit of extra (branch) metadata that’s stored in the repo. 32 | 33 | This is useful - it becomes easier to do code review, do `git blame`, and run `git bisect`. 34 | Even if the sub-commits in the branch aren’t split up perfectly, you’re still reducing the amount of code you need to read in each sub-commit. 35 | And you can always view the full feature by looking at the mainline merge commit. 36 | 37 | ## Comparison of all workflows 38 | 39 | ### `squash` (via `rebase && merge --squash`) 40 | - [:star2:] easily revert a feature by reverting a single commit 41 | - [:star2:] ensure git history is linear 42 | - [:exclamation:] lose feature commit history 43 | 44 | ### `rebase` 45 | - [:star2:] preserve feature commit history 46 | - [:star2:] ensure git history is linear 47 | - [:exclamation:] unclear what commits together make up a single feature, need context to revert 48 | 49 | ### `merge` 50 | - [:star2:] preserve feature commit history 51 | - [:star2:] easily revert a feature by reverting a single commit 52 | - [:exclamation:] git history isn't necessarily linear - feature branches can overlap on the mainline 53 | - [:exclamation:] a merge commit might not be created (and reverting will be hard) if a fast-forward happens when merging against the `HEAD` of a branch 54 | 55 | ### `merge --no-ff` 56 | - [:star2:] preserve feature commit history 57 | - [:star2:] easily revert a feature by reverting a single commit 58 | - [:exclamation:] git history isn't necessarily linear - feature branches can overlap on the mainline 59 | 60 | ### `rebase && merge --no-ff` 61 | - [:star2:] preserve feature commit history 62 | - [:star2:] easily revert a feature by reverting a single commit 63 | - [:star2:] ensure git history is linear 64 | 65 | ## Closing thoughts 66 | 67 | If you're currently using `squash`: 68 | The `rebase && merge --no-ff` workflow is completely optional, and can coexist with the `squash` workflow - if you'd rather squash, you can keep doing so. They’re equivalent on the mainline! 69 | 70 | If you're currently using `rebase`: 71 | `rebase && merge --no-ff` can give you the same clean, linear history, but with the added benefit of being able to revert a feature without much context. 72 | 73 | If you're currently using `merge`: 74 | `merge --no-ff` and `rebase && merge --no-ff` give you the same benefits without the drawbacks. There's very little switching cost! 75 | -------------------------------------------------------------------------------- /file1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jklmli/git-workflow-strategies-example/9af28502c4368c4899117d97eb31f8bd7fafaedf/file1.txt -------------------------------------------------------------------------------- /file2.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jklmli/git-workflow-strategies-example/9af28502c4368c4899117d97eb31f8bd7fafaedf/file2.txt -------------------------------------------------------------------------------- /file3.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jklmli/git-workflow-strategies-example/9af28502c4368c4899117d97eb31f8bd7fafaedf/file3.txt -------------------------------------------------------------------------------- /images/commits-master.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jklmli/git-workflow-strategies-example/9af28502c4368c4899117d97eb31f8bd7fafaedf/images/commits-master.png -------------------------------------------------------------------------------- /images/commits-new-feature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jklmli/git-workflow-strategies-example/9af28502c4368c4899117d97eb31f8bd7fafaedf/images/commits-new-feature.png -------------------------------------------------------------------------------- /images/commits-using-merge-no-ff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jklmli/git-workflow-strategies-example/9af28502c4368c4899117d97eb31f8bd7fafaedf/images/commits-using-merge-no-ff.png -------------------------------------------------------------------------------- /images/commits-using-rebase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jklmli/git-workflow-strategies-example/9af28502c4368c4899117d97eb31f8bd7fafaedf/images/commits-using-rebase.png -------------------------------------------------------------------------------- /images/commits-using-squash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jklmli/git-workflow-strategies-example/9af28502c4368c4899117d97eb31f8bd7fafaedf/images/commits-using-squash.png -------------------------------------------------------------------------------- /images/diff-using-merge-no-ff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jklmli/git-workflow-strategies-example/9af28502c4368c4899117d97eb31f8bd7fafaedf/images/diff-using-merge-no-ff.png -------------------------------------------------------------------------------- /images/diff-using-squash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jklmli/git-workflow-strategies-example/9af28502c4368c4899117d97eb31f8bd7fafaedf/images/diff-using-squash.png -------------------------------------------------------------------------------- /images/network-graph-cachebusted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jklmli/git-workflow-strategies-example/9af28502c4368c4899117d97eb31f8bd7fafaedf/images/network-graph-cachebusted.png --------------------------------------------------------------------------------