[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)