logo

drewdevault.com

[mirror] blog and personal website of Drew DeVault git clone https://hacktivis.me/git/mirror/drewdevault.com.git

My-weird-branchless-git-workflow.md (5595B)


  1. ---
  2. date: 2020-04-06
  3. title: My unorthodox, branchless git workflow
  4. layout: post
  5. ---
  6. I have been using git for a while, and I took the time to learn about it in
  7. great detail. Equipped with an understanding of its internals and a comfortable
  8. familiarity with tools like [git rebase](https://git-rebase.io) — and a
  9. personal, intrinsic desire to strive for minimal and lightweight solutions
  10. — I have organically developed a workflow which is, admittedly, somewhat
  11. unorthodox.
  12. In short, I use git branches very rarely, preferring to work on my local master
  13. branch almost every time. When I want to work on multiple tasks in the same
  14. repository (i.e. often), I just... work on all of them on master. I waste no
  15. time creating a new branch, or switching to another branch to change contexts; I
  16. just start writing code and committing changes, all directly on master,
  17. intermixing different workstreams freely.[^1] This reduces my startup time to
  18. zero, both for starting new tasks and revisiting old work.
  19. [^1]: I will occasionally use `git add -p` or even just `git commit -p` to quickly separate any changes in my working directory into separate commits for their respective workstreams, to make my life easier later on. This is usually the case when, for example, I have to fix problem A before I can address problem B, and additional issues with problem A are revealed by my work on problem B. I just fix them right away, `git commit -p` the changes separately, then file each commit into their respective patchsets later.
  20. When I'm ready to present some or all of my changes to upstream, I grab git
  21. rebase and reorganize all of these into their respective features, bugfixes, and
  22. so on, forming a series of carefully organized, self-contained patchsets. When I
  23. receive feedback, I just start correcting the code right away, then fixup the
  24. old commits during the rebase. Often, I'll bring the particular patchset I'm
  25. ready to present upstream to the front of my master branch at the same time, for
  26. convenient access with [git send-email](https://git-send-email.io).
  27. I generally set my local master branch to track the remote master branch,[^2] so
  28. I can update my branch with `git pull --rebase`.[^3] Because all of my
  29. work-in-progress features are on the master branch, this allows me to quickly
  30. address any merge conflicts with upstream for *all* of my ongoing work at once.
  31. Additionally, by keeping them all on the same branch, I can be assured that my
  32. patches are mutually applicable and that there won't be any surprise conflicts
  33. in feature B after feature A is merged upstream.
  34. [^2]: "What?" Okay, so in git, you have *local* branches and *remote* branches. The default behavior is reasonably sane, so I would forgive you for not noticing. Your local branches can *track* remote branches, so that when you `git pull` it automatically updates any local *tracking branches*. `git pull` is actually equivalent to doing `git fetch` and then `git merge origin/master` assuming that the current branch (your *local* master) is *tracking* `origin/master`. `git pull --rebase` is the same thing, except it uses `git rebase` instead of `git merge` to update your local branch.
  35. [^3]: In fact, I have `pull.rebase = true` in my git config, which makes `--rebase` the default behavior.
  36. If I'm working on my own projects (where I can push to upstream master), I'll
  37. still be working on master. If I end up with a few commits queued up and I need
  38. to review some incoming patches, I'll just apply them to master, rebase them
  39. behind my WIP work, and then use `git push origin HEAD~5:refs/heads/master` to
  40. send them upstream, or something to that effect.[^4] Bonus: this instantly
  41. rebases my WIP work on top of the new master branch.
  42. [^4]: "What?" Okay, so `git push` is shorthand for `git push origin master`, if you have a tracking branch set up for your local master branch to `origin/master`. But this itself is also shorthand, for `git push <remote> <local>:<remote>`, where `<local>` is the local branch you want to push, and `<remote>` is the remote branch you want to update. But, remember that branches are just references to commits. In git, there are other ways to reference commits. `HEAD~5`, for example, gets the commit which is 5 commits earlier than `HEAD`, which is the commit you have checked out right now. So `git push origin HEAD~5:refs/for/master` updates the `origin`'s `refs/for/master` reference (i.e. the master branch) to the local commit at `HEAD~5`, pushing any commits that upstream master doesn't also have in the process.
  43. This workflow saves me time in several ways:
  44. - No time spent creating new branches for new features.
  45. - No time spent switching between branches to address feedback.
  46. - All of my features are guaranteed to be mutually applicable to master, saving
  47. me time addressing conflicts.
  48. - Any conflicts with upstream are addressed in all of my workstreams at once,
  49. without switching between branches or allowing any branch to get stale.
  50. I know that lightweight branches are one of git's flagship features, but I don't
  51. really use them. I know it's weird, sue me.
  52. Sometimes I do use branches, though, when I know that a workstream is going to
  53. be a lot of work &mdash; it involves lots of large-scale refactoring, or will
  54. take several weeks to complete. This isolates it from my normal workflow on
  55. small-to-medium patches, acknowledging that the large workstream is going to be
  56. more prone to conflicts. By addressing these separately, I don't waste my time
  57. fixing up the error-prone branch all the time while I'm working on my smaller
  58. workstreams.