[HN Gopher] Git Worktrees and GitButler
___________________________________________________________________
Git Worktrees and GitButler
Author : chmaynard
Score : 72 points
Date : 2024-03-04 18:38 UTC (4 hours ago)
(HTM) web link (blog.gitbutler.com)
(TXT) w3m dump (blog.gitbutler.com)
| Jenk wrote:
| I am struggling to appreciate how this is _not_ a completely
| flawed workflow. Are many teams doing this? What is the workflow
| like that benefits from this? What's the "shit not _that_
| branch":commit ratio?
| corytheboyd wrote:
| Personally this sounds completely reasonable to me (especially
| with lazygit)
|
| > You can stash everything, create a new branch, switch to it,
| fix the bug, commit and push it, then switch back to what you
| were working on.
|
| Also comfortable with fixing the bug on my current branch,
| leaving it there to validate as I complete the other stuff,
| then later pull commits out to new branches to create atomic
| PRs. Assuming the bug can wait that is, otherwise yep off to a
| new branch, and no amount of git magic prevents an interruption
| from being disruptive.
|
| I know worktrees are a thing but I feel like the branching
| model is enough complexity to both be useful and remember. No
| problem if people want to use a more complex workflow with a
| GUI, this is for them, not for me.
| dogleash wrote:
| What part exactly? The worktrees?
|
| If you're just talking worktrees, the benefit is what it says
| on the tin, multiple parallel working directories. They're a
| nice to have, not world-changing or anything.
|
| The example in the article is contrived to sell a separate kind
| of workflow tool in the product he's building. I don't use
| worktrees like that.
|
| My use case is heretical software development, but sometimes I
| work with projects that take a long time to build. And even
| longer to test. Worktrees let me minimize the amount of
| rebuilding when switching branches, or kick off a full test run
| and go work in another branch.
| neandrake wrote:
| We're not yet using git, but we have one repository with
| multiple branches, one for each released version (on-prem
| software, not a service run). We are in regular active
| development of ~3 branches at a time. Most developers dealing
| with this will have multiple clones checked out rather than
| switch branches in the same clone. The reason for doing so is
| we often make significant project structural changes in each
| release, and the act of switching branches updating thousands
| of files and lots of structural changes often causes most IDEs
| to combust. Pointing the IDE to a different cloned workspace
| works much more smoothly. After switching to git I anticipate
| it will include our own guides for using worktrees for such a
| purpose.
| numbsafari wrote:
| Another way to look at this, is that a working copy is a
| projection of the state of a repository at a particular commit.
| If you are working on or with a tool that utilizes the
| repository contents to drive it, then you can expose multiple
| instances or versions of the tool from those projections, while
| leveraging a shared content cache and upstream configuration.
|
| For example, let's say you are working on a static site (this
| is just an example). You can have a web server running that is
| serving the main worktree from https://mysite.local/, and any
| worktrees from https://mysite.local/~[worktree]/. Then, if you
| want to make a particular branch available for someone to
| review in their browser to provide feedback, you can direct
| them to the worktree URL while you continue doing other work
| either in your main branch, or on other branches altogether.
| renewiltord wrote:
| I use worktrees very rarely. For instance, if I'm trying to
| figure out some very hard to reproduce bug that disappeared
| between one revision and another and I need to examine the code.
| I can't just bisect if the feature set or interfaces are
| different so I need both versions of the code. Sometimes I
| cherry-pick around to restore some similarity.
|
| But for this two-patch flow, I'm just going to use two branches.
| frfl wrote:
| Being able to explore code from another revision in your
| regular editor side by side with your other branch (two editor
| instances) is a big plus of worktrees. Maybe it wouldn't work
| if you use some heavyweight editor like Visual Studio or
| Eclipse or if your project is massive and IDE intelligence
| features take up a lot of RAM, but for lightweight editors,
| small-medium projects it works great.
|
| I personally use it all the time on longer lived projects.
| Stashing/unstashing is fine, but I like really like the
| flexibility of just being able to switch over to another branch
| to fix something, review a PR, or anything else with zero
| mental overhead of keeping track of stashed work.
| renewiltord wrote:
| I used to use it myself. Not too much of problem. I just
| didn't use it enough to warrant use. Was quite
| straightforward to just switch up. But I shall try it again
| and see.
| chatmasta wrote:
| The thing that annoys me about worktrees is that you can't
| have two checked out to the same commit at the same time [0],
| which can be inconvenient when I forget about one that I had
| open a few days ago. This also breaks the feeling of
| isolation, since the issue doesn't exist if I do a fresh `git
| clone` to another directory, so it begs the question of what
| fragility is present in worktrees that isn't in a fresh
| clone.
|
| In other words, I wouldn't feel safe doing weird Git
| operations in a worktree, so why use one in the first place?
| If I want a true throwaway repo, I'll just re-clone in
| another directory (and make sure to remove the remote, to be
| extra safe in case I accidentally push).
|
| Also, git worktree requires me to make a new directory
| anyway! So why would I not just put a fresh clone in there?
| To save a few megabytes of the .git index?
|
| [0] https://stackoverflow.com/a/39668520
| frfl wrote:
| You can make a temporary branch if you want an identical
| copy of a revision. For example, `git checkout feature-x`
| (on worktree 1), then `git checkout -b feature-x-temp
| feature-x` (on worktree 2).
|
| I've never had any issues with worktrees after heavy and
| extensive worktree usage since 2017. I use it regularly on
| big projects. The only time I had an issue with git was
| actually on a small non-worktree repo after 10 years of
| using git. The .git/ metadata got corrupted somehow, never
| understood why or how.
|
| When you have a worktree based workflow, you're getting the
| benefit of a single .git/ database managing them. Whereas
| if you have multiple standalone clones, you have to
| manually keep them up to date. With worktrees doing a `git
| fetch` will ensure all worktrees are able to do git
| operations on the most update to date git data. Whereas
| separate clones means you need to do N git fetches.
|
| Saving a few megabytes literally doesn't matter. Your
| project dependencies will likely use up tens if not
| hundreds of megabytes when you install them (pip packages,
| node modules, etc). Disk space is never really an issue
| anymore is it? Disks are hundreds of GBs now even on low
| end machines. I probably have multiple GBs of redundant
| disk usage because of my worktrees, but the convenience is
| well worth it IMO.
| jrockway wrote:
| I find this very annoying. I don't know if this is the
| right thing or not, but my workflow for starting new work
| is "git checkout master; git pull; git checkout -b
| new_work". This is all fun and games until you have a
| worktree that's sitting at master. Now you have to figure
| out where that is, chdir there, and update that branch from
| the remote. It's actually extremely annoying.
|
| Maybe there is "git update master from remote and throw
| away my local changes because if i committed something
| there that was a mistake". But probably not, sadly, because
| technically the "git pull" operation to pull origin/master
| into the local tracking branch master updates the working
| copy where master is checked out, and that's what worktree
| is trying to avoid (changing a directory you're not in). I
| guess "git pull; git checkout -b new_branch
| remotes/origin/master" is what I should write, but that's
| more bytes to type.
| wiredfool wrote:
| git fetch origin git checkout -b new_work
| origin/master
|
| Git pull is essentially git fetch + merge. You don't have
| to update your local master branch to check out a new
| branch based on it.
| frfl wrote:
| I get around this by having "wt-" (wt-1, wt-2, etc)
| branches that act as my main/master branch per work tree.
|
| As long as you set the upstream to master, it works fine,
| you can pull in upstream main/master changes without
| issue. It does mean you have these "wt-" branches, but it
| works well enough for me, never got in my way or caused
| me to stop using worktrees.
|
| My workflow is like this: For each worktree I add,
| checkout a new branch, wt-N, with it's upstream set to
| main/master. If I need to checkout some other branch in a
| worktree, do it, finish up, check out wt-N again, git
| pull to bring it up to date with upstream. Leaving idle
| worktrees on a "wt-" branch so I don't have the issue of
| "branch X is already checked out out worktree Y"
| avgcorrection wrote:
| > The thing that annoys me about worktrees is that you
| can't have two checked out to the same commit at the same
| time
|
| Checking out the same _commit_ is fine. You just can't
| checkout the same branch.
|
| This is not a problem for me because I either use detached
| head (if say I'm going to build from some commit) or
| restrict branches to certain worktrees naturally (if I am
| backporting to `old-version` on a worktree then... it's not
| like that backport branch is ever going to be relevant in
| my main worktree where I do most of my work).
| jfultz wrote:
| They can absolutely be checked out to the same commit. They
| cannot be checked out to the same local branch. But
| checking out to the same SHA, or even the same remote
| branch (which, really, is just another name for a given
| SHA) is just fine.
|
| I have two workflows heavily benefitting from worktrees.
| The first is simple enough...code archeology. I want to do
| it without disturbing my working tree. Yes, this could have
| also been done with a separate clone, but I'd have to fetch
| in that clone, and that's just extra keystrokes.
|
| The second is that I like to have a "synthesis"
| worktree...one that includes PRs I'm reviewing and ones I'm
| creating. Both benefit from my regularly building and using
| them (e.g., I might incidentally find bugs through usage I
| otherwise missed through testing or code-reading, or I
| might spot pending integration problems). I don't want
| multiple build locations...I want to run everything in one
| build.
|
| But when I've finished developing a PR, need to create a
| clean version of it from the master branch. In my secondary
| worktree, I can just cherry-pick commits from my synthesis
| worktree to produce a clean branch to push. I could do this
| in the synthesis worktree, too, but at the cost of almost
| certainly forcing a complete rebuild. In a second clone,
| I'd have to create patch files because the clone wouldn't
| have access to the commit objects for cherry-picking.
| azemetre wrote:
| I use to work at a company whose product had multiple versions
| of Java over its existence (literally think from Java 5 to Java
| 18), depending on the major product support versions (yes we
| had clients still on 1.0 system from the early 2000s, they were
| mostly regional banks).
|
| Worktrees made working on that monolith easier to do my types
| of tasks (updating tests that were specific to the UI team I
| was on, also the only person willing to touch java code).
|
| It sounds kinda backwards, but if you have to support multiple
| versions of a product that still brings in hundreds of millions
| in revenue it's nice to use git worktree to basically check out
| different versions of your product in time.
| coldtea wrote:
| > _Let 's take an example. Let's say you are working on a
| feature. Your boss interrupts you (, bosses, right?) and says "we
| need this bug fixed". Or maybe you just notice the bug as you're
| working. Whatever it is, you need to switch contexts while in the
| middle of something. Now you have a few options.
|
| - You can fix the bug and commit it into your feature branch and
| try to get them both deployed together.
|
| - You can stash everything, create a new branch, switch to it,
| fix the bug, commit and push it, then switch back to what you
| were working on.
|
| - You could locally clone the repository to a new directory and
| work on the other branch there, then push it back into the main
| one and then push from there back upstream. _
|
| --
|
| What?
|
| Why couldn't you, like, create another branch off of main, switch
| to that, and fix the bug there, then resume working on your
| feature branch?
|
| Then when one of the two (bugfix or feature branch) is merged
| into main, you rebase main into the other.
| corytheboyd wrote:
| Completely agreed, this is pretty basic git that I would expect
| experienced developers to have down. This isn't even a UI vs
| CLI argument, it's the same idea in either case.
|
| I'll keep an open mind, but if you're going to shake up a
| workflow as solid and well understood as git, you better have a
| damn good offering.
| Huggernaut wrote:
| I don't particularly care either way but how is that
| meaningfully different from option 2? You need to do something
| with your WIP in either case before switching to the new
| branch.
| coldtea wrote:
| Well, stashing is ok for short term context switch, but it's
| not that organized or neat.
|
| You're supposed to be working in a feature branch when your
| were given the bug to fix. Would you not have commited (or,
| worse, pushed) anything to it already? Most times you already
| have, so it's not like stashing your latest changes and
| getting to the latest state of the feature branch is neutral.
|
| Plus, what big benefit would stashing have, since you'll
| still need to create a separate bugfix branch to create the
| bugfix PR off anyway? If you get to context switch while you
| were working on my-feature-branch, you still need to create
| my-bugfix-branch and push the fix from that, no?
|
| Besides nobody guarantees you ahead of time that the bug will
| just be solved fast and they get to return to working on the
| feature pronto. So a bugfix branch makes more sense than a
| stash, which is more of a short term thing.
|
| Just making a bugfix branch is neater.
| robertlagrant wrote:
| You might not be in a position to commit. You could commit
| just what you have, make a bugfix branch, and then when you
| return to your original branch do a git reset later back to
| the uncommitted state. It just isn't that neat.
| coldtea wrote:
| > _You might not be in a position to commit_
|
| So? That's what amend and/or squash is for. So that you
| don't need to be in some "perfect" condition to commit.
| Or you can do a soft git reset as you say.
|
| But sure, even git stash would make more sense than
| worktrees for the TFA scenario I quoted.
| marcellus23 wrote:
| > You need to do something with your WIP in either case
|
| Why not just commit it?
| tomjakubowski wrote:
| I've been where OP is. For me it's because, for whatever
| reason, I don't want to commit my shitty in progress work right
| now to go work on this interrupt. And if I stash it, I will
| likely forget I did that, and waste time looking for it again.
|
| Worktrees are a different way of managing multiple branches in
| flight. They're not hard to use, and using them doesn't imply
| any incompetency with git. They are likely being used to cope
| with another kind of problem.
| nerdponx wrote:
| There are many legitimate use cases for multiple worktrees.
|
| Here's one example:
|
| I'm working on a trunk-based project, and I am upgrading a
| core framework like Angular or Django. That's a fairly
| intrusive change that involves modifying some untracked on-
| disk artifacts, like node_modules/ and package lockfiles.
| Switching branches doesn't help me manage those artifacts,
| and might require a full teardown and rebuild of my dev
| environment each switch. Even if I maintain a meticulous
| commit history and never have uncommitted "WIP stuff" that I
| need to stash, switching from my branch to fix a bug in the
| main branch could be annoying and intrusive just because of
| the dev environment. This is a great case for a separate
| worktree.
| tomjakubowski wrote:
| Yep, just added similar thoughts myself.
| tomjakubowski wrote:
| There's another case where worktrees are great, whether or
| not you are compensating for other problems. If your
| project's build takes forever, but incremental builds are
| relatively fast, worktrees are a good way to preserve build
| state on the file system between branch switches.
| coldtea wrote:
| > _worktrees are a good way to preserve build state on the
| file system between branch switches._
|
| Why would you ever have build artifacts, incremental or
| not, as part of your code repo?
| neandrake wrote:
| It's probably not part of their repo, but exists
| alongside, possibly gitignored. Either way in the same
| workspace the IDE will have to invalidate a lot of it
| when you switch, and again so when switching back.
| Separate clones, or worktrees, mean they don't get
| invalidated and your IDE just sees them as a separate
| project altogether, each with its own cache, regardless
| of where that cache exists.
| kraftman wrote:
| Why not commit what you have and ammend / rebase it later?
| mabster wrote:
| I picked up my git workflow from a very commit-centric
| workplace.
|
| Instead of stashing or anything like that we would commit
| with the text "WIP". Every time we had a stable point, we
| would make a "WIP" commit and then continue, giving us a good
| point to revert to if the next steps don't work.
|
| If someone interrupts me I would make a WIP commit and then
| revert it and then make the change they're asking about.
|
| Later on we would squash the WIP commits and make real
| commits out of the changes.
|
| Another nice thing about this workflow is that everything
| goes into the reflog.
| txg wrote:
| If you want to run some time-consuming tests prior to pushing
| your bug fix it's convenient to run those in your separate
| worktree. You can continue working on your original branch
| while the tests run.
| XorNot wrote:
| The article starts out by talking about the built in git
| worktree command which will do this for you, and is built
| into git already.[1]
|
| The question is, if that exists why are the virtual trees
| meaningfully different?
|
| [1] https://git-scm.com/docs/git-worktree
| MaulingMonkey wrote:
| > Why couldn't you, like, create another branch off of main,
| switch to that, and fix the bug there, then resume working on
| your feature branch?
|
| I mean, that's option 2 of what you're quoting, and _is_
| generally my preferred option. That said, there are cases where
| it breaks down and I end up wanting worktrees or independent
| clones, generally involving [?]`.gitignore`d local data:
|
| * A newly created bugfixing branch may take awhile (minutes?
| hours?) for me to build. I can either go on a coffee break
| while waiting for it (since I can't very well test my ability
| to reproduce the bug, nor verify my bugfixes work, without
| having said branch in a compiling state), or I can have said
| build occur in a secondary worktree - and continue working on
| my original task in my primary worktree while waiting for it to
| build.
|
| * If I've got a lot of `.gitignore`d config file tweaks related
| to my major feature work / profiling / debugging / ???, I might
| not want to bring those over to my bugfixing session. `git
| stash push --all` is an option, but that will get build
| artifacts - which will take awhile to stash - not just my local
| config files. A fun ancedote: I once wasted an hour failing to
| reproduce a game's audio crash because a local `user.ini` file
| had the entire audio system disabled via feature flag. (I now
| prefer setting the volume to 0 instead.)
|
| * I do a bunch of porting and cross-platform abstraction stuff,
| which makes my build times even worse than usual by having to
| repeat builds N times. I might not want to invalidate by main
| build cache for all that, when my bugfixing branch might only
| reasonably need local testing on 1 platform, and gain next to
| no benefit from the previous cache as is (depending on how far
| diverged the branch I was working from and the branch where the
| bugfix should occur are.)
| coldtea wrote:
| > _I mean, that 's option 2 of what you're quoting,_
|
| No, option 2 is to stash.
|
| Their three weird options are:
|
| (a) fix the bug on the feature branch and "try to get them
| both deployed together"
|
| (b) git stash, fix the bug in a new branch, come back
|
| (c) locally clone the repository
|
| At least (b) is somewhat sane, but it's still weird that the
| even more code-managementy option of "commit your halfway
| feature work/create new branch for the bugfix/come back to
| the feature and amend/squash/soft reset" isn't even
| mentioned.
|
| Your cases are more involved than the contrived scenario you
| give, where their weird options don't make sense.
| majikandy wrote:
| This is word for word what I was about to write. Totally agree
| as I had a very puzzled face when that option didn't appear in
| the list. And even more puzzled when (what I thought was the
| throwaway flippant idea of) 'cloning the repo into another
| folder' was in the list. I can safely say, I've never done this
| and can't think of a scenario where I would need to.
| opium_tea wrote:
| How is it different from the second bullet point? Except the
| second bullet point is more specific in that it says what to
| do with uncommitted changes.
| coldtea wrote:
| > _How is it different from the second bullet point?_
|
| How "cloning the repo on a different folder on the same
| machine just to work on a bug fix" is different from the
| main way Git is supposed to be used (creating a new branch
| for the bug fix and working there, then checking out your
| feature branch again)?
|
| In that it doesn't make sense.
|
| They might as well go full 1980s style "codebase_1/
| codebase_2/ codebase_3/ codebase_3.bak/" and so on copies,
| and skip code management altogether...
| thisisthenewme wrote:
| I've started using worktrees recently and I have nothing but
| praise for it. It's especially useful to me because I work on
| multiple features and want to reduce friction from context
| switching. I basically have a structure like
| `/worktrees/<project>/<worktree>`. I use it alongside direnv and
| have my .envrc in the top-level project. That essentially allows
| me to set up project-specific environments for all of my
| worktrees. This works neatly with emacs projectile mode and lets
| me switch between different projects/features seamlessly. My head
| feels a lot lighter not having to worry about my git branch
| state, stashing changes, and all that jazz. I think it's a great
| tool to have in your repertoire and to use depending on your
| needs.
| avgcorrection wrote:
| The first big thread on GitButler (recent) left me scratching my
| head. All the material with jargon like "virtual branches" just
| left me confused. It was like jumping into a how-to on why choose
| a Ferrari without first understanding what a car is. This clears
| things up nicely.
|
| I mostly use worktrees for very separate things. Like: long-
| running build, way old or way new versions of the app. Then it
| doesn't make sense to mix-and-match virtual branches. So when I
| want to build the app for deployment I don't want to worry about
| whatever other changes getting in the way. Git worktrees doesn't
| solve the same problem as what GitButler does. Worktrees is a
| streamlined way of the manual separate-clone workflow for the
| same repository. (Technically they are all distinct repositories
| once you clone them but ya know.)
|
| I do have use for separate-from-branch files. Like notes to
| myself and test scripts that aren't going into the branch.
| Crucially these files have nothing to do with the main work: the
| files themselves are not involved, so there can never be merge
| conflicts.
|
| This GitButler workflow makes sense for things that (1) won't
| cause merge conflicts and (2) which won't step on each others'
| toes. The example about a translation and code change is nice.
| Doing a translation at the same time as a code change is not
| likely to "break the build".
| ratrocket wrote:
| For reference & context, I believe (I hope!) this is the "first
| big thread on GitButler":
|
| https://news.ycombinator.com/item?id=39356042
| zamalek wrote:
| Worktrees have been a massive savior for me when using Rider
| (which is all that I use them for). Previously, switching
| branches would cause the cache to become out-of-date; which would
| be fine if amending it didn't break 80% of the time (causing
| spurious code issues etc.).
|
| It works just fine with Sublime Merge, so long as you add the
| worktree as the repo - not the bare repo. I do wish there was
| better integration.
___________________________________________________________________
(page generated 2024-03-04 23:00 UTC)