[HN Gopher] A Git client for simultaneous branches on top of you...
___________________________________________________________________
A Git client for simultaneous branches on top of your existing
workflow
Author : sandgiant
Score : 164 points
Date : 2024-02-13 12:41 UTC (10 hours ago)
(HTM) web link (gitbutler.com)
(TXT) w3m dump (gitbutler.com)
| baq wrote:
| I'm using git-branchless today, with mixed results (have a large-
| ish monorepo, it's slow when every commit is tagged by CI and
| restacking broke and only works on a single branch) - I could
| really use an ascinema recording of a typical two-or-three-
| branches-in-parallel workflow which need daily rebasing.
| schacon wrote:
| Nice, I've never used git-branchless. GitButler is only a GUI
| for now, but a CLI/TUI would be very cool and we would love to
| get there.
| catapart wrote:
| On the positive side, I really like that someone else out there
| understands that git is mostly simple to understand, so long as
| you can see all of the concepts laid out for you, and that no
| kind of node-line timeline or branching tree structure is ever
| going to be a replacement for a graphical layout of all of the
| git features. For some time, I thought I was going insane because
| every time I said "working with git sucks; I wish someone would
| make a decent GUI", people would insist that 'actually, you
| should try [x,y,z]; they're really good", only to find out that
| it's the same command-line-headed nonsense that every other git
| client thinks is UI. The only thing that saved me is when I
| started to explain to people what I mean, they would go
| "oh...yeah...I guess that would help? If you didn't know git?
| Whatever. Seems like a lot of work for nothing."
|
| And...yeah. That's always been my impression of it. That if you
| know git well enough to build an app that makes it simple for new
| users, you might not understand why a new user would need it
| 'simplified' in the first place. So far, I've chalked it up to
| that being a frustrating but unavoidable outcome.
|
| So all of that is to say, I'm glad someone built something that
| treats branches like a "thing" I can "mess with", instead of a
| timeline that I can indirectly affect through various
| incantations. And seems to understand the utility of (if not the
| necessity for) obfuscating a lot of
| 'stash->checkout->unstash'-type silliness, and all of the branch
| acrobatics you have to go through to do something as simple as
| "uncommit a change", or "move this commit's changes over to this
| other branch". This seems, to me, like a very good idea.
|
| On the other hand, what I'm not particularly thrilled about is
| the apparent ownership this app takes? Not having
| interoperability with standard git is a non-starter for me. I'm
| not going to deal with anyone's proprietary "virtual" anything;
| there's not nearly enough utility that can be supplied in a VCS
| that would make lock-in seem valuable to me.
|
| And of course, everything that flows from this mindset is just as
| tainted. If I have to use your app to create the branch that can
| be visually controlled, then I won't use your app. It has to work
| with repos I'm already using. If I have to learn additional
| configuration or introduce any non-standard areas into my
| codebase that will cause interactions with other packaging
| systems to break, that's a no-go.
|
| Git is good. Git works. Don't try to be "better than" git. At
| least not right out of the gate. Figure out how to make an over-
| the-top git UI and you've got yourself a customer. But I can't
| figure how anyone could make an 'embedded' git UI (that changes
| the way I use version control) that would be useful to me, in any
| way.
| schacon wrote:
| There are some good points, but there are things I want in my
| tooling that git just cant do. We need to keep some external
| data structures. However, we are as compatible as we can be. We
| keep the index in a proper state (union of all the head commits
| on all applied branches) and we write out refs for each branch
| (to refs/gitbutler so as not to clobber stuff in the normal
| namespace). You can still do whatever - base worktrees off the
| branches we write, etc.
| catapart wrote:
| Yeah, but if I have work in the virtual branches, how is that
| represented to git? Does it even exist in my local repo, or
| not, until it has been "handled"?
|
| And 'handled' brings up another point because I don't even
| know what to call this intermediate state of existence that
| isn't a domain of git. When I'm done with a virtual branch do
| I... commit to it? Collapse it into a real branch? It's new
| terminology, new concepts, new X to deal with something that
| COULD have been handled with just git. Messier, sure, but
| messy is fine, under the hood. So long as the UI treats it
| the same, who cares how nasty the git had to get?
|
| I'm sure there are things that you simply couldn't have done
| with pure git. But what I'm less sure of is how useful any of
| those things are. Let me tell you this: auto branch naming?
| That's not only not something I would use, but something I
| would consider an anti-pattern. I never want my utilities
| doing my work for me. I _only_ want my utilities making my
| work simple and seamless. It 's the focus on unimportant and
| anti-useful stuff like that which makes me concerned about my
| experience with the product overall.
|
| Again - very happy that you all have put this together! It's
| a great implementation for the things you are wanting to do,
| as far as I can tell. It definitely feels, to me, like you've
| got a winning product on your hands and that my complaints
| aren't relevant. I'm just not in your demographic. But if
| there is any question about "who" you are losing, I can at
| least give you one point of data to say that I'm averse to
| any productivity app that doesn't trust me more than the app.
| And I think your app makes some strong assumptions about what
| it "needs" and that is a detriment to the end user, no matter
| how "necessary" it feels.
| schacon wrote:
| > Does it even exist in my local repo, or not, until it has
| been "handled"?
|
| Yes, we write the virtual branch states into your git
| repository under refs/gitbutler/[branch] so you have them
| represented as real git references.
|
| There is not a lot of new terminology. You commit to your
| branches, push them to GitHub, open PRs, track upstream
| work. It's really just that you can have more than one of
| them applied to the same working directory at the same
| time.
|
| > auto branch naming?
|
| This is only done if you have the AI tools turned on and
| it's often just a placeholder. We basically want you to be
| able to use anonymous branches - not having to name them to
| start them. Think of it as a temporary description if you
| want, you can as easily rename them as name them in the
| first place. Also, I rather like them, they're often more
| descriptively correct than I would have done in the first
| place.
|
| > doesn't trust me more than the app
|
| This isn't our goal. Our goal is to let you delegate things
| you don't want to do to us in some cases and make cool
| things a little simpler so that more people can utilize
| them. But you have to _opt in_ to the AI stuff, it's not
| the default. Otherwise they're just called "Virtual Branch
| 1", etc until you rename them.
| aeonik wrote:
| I started using this yesterday.
|
| I'm a little unsure how to pull multiple real branches into my
| workspace.
|
| It'd be nice if I could have a virtual branch overlay a real
| branch so that I could continue working with both the advantage
| of GitButler and maintain productivity with colleagues that don't
| use it.
|
| As it stands right now, I'm a bit unclear on how to operate with
| others who use the old fashioned branching strategy
|
| I really love to tool though. I watched a talk from Scott
| yesterday, and he has so many good ideas of where to take Git
| next.
|
| I also LOVE the "CRDT all the changes" idea, and have tried to
| implement similar solutions using inotify and a "stack of
| patches" and BTRFs CoW for space saving, with limited success.
|
| https://news.ycombinator.com/item?id=39356042
| schacon wrote:
| You should be able to do this pretty easily. We list your other
| local and remote branches in the sidebar. If you apply them,
| they are essentially converted into virtual branches (remote
| targets too, I believe)
| aeonik wrote:
| I think I got it working today. Not sure what I was doing
| wrong before.
|
| Also, you I recommend that you add the capability to pull
| different hunks out of the commit, and separate/unsquash
| them, or even move them to different branches. The UI is
| begging me to drag parts of the commit out. I can squash
| commits, but it doesn't seem like I can do the opposite.
|
| Use Case: Just now I made a change to a config file, and a
| build tool also updated the version in this same config file.
| I want to split the change out.
|
| Using the CLI to split the commit, I would do this:
| git add -p <file>. # partial stage s
| # for each hunk
|
| For splitting the hunk into a branch, I would do this:
| git checkout -b new-branch-name git reset HEAD^
| # if I already staged it git add -p s
| # for each hunk
|
| Using Magit, splitting out the commit: M-x
| magit-status TAB # on the
| file diff s # per hunk
| e # when I need to refine it
| manually
|
| Using Magit, to move hunks to a different branch:
| M-x magit-status TAB # on
| the file diff b c # new
| branch <same process as before> b b
| # as needed, switch branches
|
| Again, want to thank you for you work here. It's _really
| nice_ , I already am getting my team onboarded to it.
| schacon wrote:
| So, yes and no. You should be able to do `reset HEAD^`
| equivalent by just hitting 'undo', which is essentially
| what that does. You can then re-commit stuff or drag the
| newly uncommitted hunks to other branches.
|
| But it's only for the last commit, so something in the
| middle you can only squash, you can't unfold into two
| commits or something. Totally doable, but not in our UI
| yet. (we will do that someday)
| schacon wrote:
| The CRDT thing happens in GitButler, but we hid the UI to
| search and find old changes for now (you can see what it looked
| like here: https://docs.gitbutler.com/features/timeline). We
| still record the data locally and could theoretically recreate
| the state of your working directory and branches at any time.
| If you're curious, join our Discord and I can show you how
| we're storing it, you could easily use it in interesting ways
| until we resurrect a UI for it.
| osrec wrote:
| The little demo videos embedded on the home page are really
| sharp! How were they made?
| videlov wrote:
| We used an app called Screen Studio.
| ckolkey wrote:
| After watching your (very enjoyable) talk in the other thread,
| schacon, one thing struck me - there _is_ a way to work on
| multiple branches at the same time: worktrees.
|
| What's the advantage of a tool like this over that?
| frizlab wrote:
| Ha! That's exactly what I do personally, and I had the same
| reaction. Worktrees are awesome.
| schacon wrote:
| It's a good question, I was just working on a blog post - both
| because worktrees are very cool and also because I think we
| have a nice alternative to a similar issue.
|
| With worktrees you can have different branches in different
| working directories and work on them at the same time. But
| practically, there is very little difference to just cloning
| the repo twice and working on different branches in the two
| checkouts.
|
| The way we're doing it, the branches are both in the same
| working directory at the same time.
|
| This can have a couple of advantages - one is if you have an
| annoying bugfix and you need it in order to keep working on
| your feature, you can have them both applied but you don't have
| to commit one into the history of the other. You cannot do this
| with worktrees.
|
| Another is trying out multiple branches together. If they don't
| conflict, you can essentially get the result of a multi-way
| merge without creating any merge artifacts. Say you merge three
| work in progress branches to test them together, then keep
| working on each independently. Also, in this case, you _know_
| that you can merge them in any order and the result will work
| because you've actually worked on the merged tree.
| Tempest1981 wrote:
| Reminded me of a product called Rational ClearCase:
|
| > It uses the MultiVersion File System (MVFS) which is a
| virtual file system that displays specific versions of data
| stored. In particular, it supports dynamic views which can
| show an arbitrary combination of local and remote files
|
| https://en.m.wikipedia.org/wiki/Rational_ClearCase
| devsda wrote:
| Yeah, this sounds very much like a 'view' in clearcase.
|
| It's a good feature especially with big monorepos where it
| is helpful to pick and choose a known good version of
| specific module or subset of files without polluting the
| history.
|
| Tracking good versions of every single file with huge
| number of files changing was a pain though.
| schacon wrote:
| I haven't used ClearCase, but from the description, I
| don't think it's much like a view. It's closer to
| IntelliJ's changelists, but without many of the
| limitations.
|
| It's effectively a way to take changes you're making and
| pretend like you did them in worktrees without having to
| switch back and forth.
|
| Or maybe, to work on several branches, achieve the merged
| state you want, then split the work up into reviewable
| branches that can be merged in any order to achieve that
| final state.
| devsda wrote:
| Typical use case for worktree(s) is to work on
| feature/hotfix branch without touching the current
| master/wip branch.
|
| So, in the context of worktrees I read _"The way we're
| doing it, the branches are both in the same working
| directory at the same time"_ as having both the branch's
| files (not changes) available in the same worktree.
|
| Thanks for clarifying that it is similar to changelists.
| adrianmonk wrote:
| Don't worktrees require more work to actually run your code
| because of multiple working directories?
|
| If you're using an IDE, you'll probably need to create a
| project for each working directory. Or maybe you can change the
| path within the IDE's project.
|
| If you're running a local web server, it will need to be
| configured for each working directory, again either multiple
| instances or one where you keep updating the path.
|
| For compiled languages, the builds will take longer. When you
| create a new worktree, you may have to do a full build. Even if
| you have incremental builds, as you pull in upstream changes,
| you'll have to do N incremental builds instead of 1 incremental
| build.
|
| It's not the end of the world, but it's a bit of hassle and
| extra computation that isn't needed with just one working copy.
| reyqn wrote:
| Maybe I don't understand how gitbutler works, but the main
| reason I use worktrees is actually to keep my IDE and
| incremental builds getting confused when I switch branches,
| on branches that have lots of differences. It works really
| well, and I can fix stuff on old versions of our app in a
| jiffy.
|
| I wouldn't use gitbutler for what I use worktrees, and I
| wouldn't use worktrees for what I think gitbutler is aiming
| at.
| schacon wrote:
| Since we write out our virtual branch artifacts into
| refs/gitbutler/X, you could actually pretty easily setup
| worktrees from each of them to do this type of work on.
| Perhaps setup a post-commit hook to update any active
| worktrees automatically too. Might be an interesting way to
| effectively work on several worktrees from a single working
| directory.
| reyqn wrote:
| That's interesting. I'll try this out when windows
| support ships and keep an eye on it in the meantime.
| XorNot wrote:
| I'm very unclear why this "takes over" the git repository to
| work. Like even if that was absolutely necessary - and who am I
| to say it's not - ...surely the "actual" repository can simply
| live somewhere else and be manipulated?
|
| How do virtual branches differ so completely that managing them
| isn't just a bunch of behind the scenes checkout/commit commands
| being issued to another repository somewhere, even if the local
| worktree needs to be separate?
|
| Particularly because the idea of their being a base branch for
| virtual branches is pretty much the git branching model. Git and
| it's tools understand this perfectly fine provided you're using
| branches, and moving things between branches is supported via
| cherry-pick.
| schacon wrote:
| There's a lot here that is a little confusing, because I don't
| think almost any of what you're saying is what we're doing.
|
| It does not take over your repository. We try pretty hard to
| not touch as much as possible. We do have a second place where
| we store metadata on everything we're doing, but it's not in
| your git repo. If you delete us, your repo is only very
| minimally added to.
|
| We can't do virtual branches by wrapping git concepts and data
| structures, because there is just no concept of more than one
| applied branch. HEAD can only point to one thing, we're trying
| to make the concept of HEAD be able to be more than one thing
| and land changes on any of them depending on where you want
| each hunk to go. Git just cannot do this. It has one HEAD and
| one index and we need multiple of each for what we're trying to
| accomplish. We try to be tool-compatible and touch as little as
| possible. We update the index to match what is likely expected
| - ie, make a normal `git status` match the list of files you
| see in your GitButler workspace as changed, etc. We write out
| `refs/gitbutler/[branch]` for each virtual branch you manage,
| so you have real git refs to work with. But there are lots of
| things we want to do that Git simply does not have data
| structures for.
|
| Finally, Git does not have a concept of a base or default
| branch. There is no special branch in Git. You have HEAD, but
| that changes. You have tracking branches, but that's per
| branch. Nothing in Git proper says 'origin/master' is special
| in some way except convention. The tool itself has no idea what
| is the "main" branch. GitHub does, with it's "default" branch,
| but Git does not.
| Nullabillity wrote:
| This seems like an incredibly poor idea to me. You now have the
| problems of rebase (your commits no longer represent a consistent
| repo snapshot), but even worse (your commits _never_ represented
| a consistent repo snapshot!).
|
| Is there a way to identify commits made by GitButler? Can I
| configure my host to reject them automatically?
|
| And the "generate a commit message for me" button really nails
| the kind of poor decisions that lead here.
| videlov wrote:
| I think there may be a misunderstanding here. While the tool
| does something unorthodox locally, the output that it generates
| is plan Git trees that do represent a consistent snapshot. It
| is the process of arriving at those snapshots (locally) that we
| feel we can make more ergonomic. Disclaimer: I am a Co-founder
| Nullabillity wrote:
| > While the tool does something unorthodox locally, the
| output that it generates is plan Git trees that do represent
| a consistent snapshot.
|
| Yes, they're snapshots of _something_ , but they're not
| snapshots of states that you were ever in when developing or
| testing.
|
| If you develop PRs A and B together then you have no idea if
| there's a hidden dependency between them, because you only
| ever tested (A+B). This is risky but manageable if they are
| actually related changes. Things get worse with the suggested
| workflow of "work on A, find bug, create B and submit bugfix,
| then go back to A".
|
| And worse, you don't even have a clue which point in A
| matches up to which point in B. The history that you do
| generate ends up being effectively worthless.
|
| Saying that you generate snapshots is like saying that RAM is
| just a linear array. Sure, you _can_ read it, but the
| _meaning_ is completely lost without context.
| schacon wrote:
| GitButler creates normal Git commits and trees with libgit2.
| It's not dissimilar to using a tool that implements it's own
| interactive adding or something. The point is that Git is the
| database and Git hosts (such as GitHub) matter in our
| workflows, but actually creating the trees that represent the
| trees you want to share can happen in any way. We are changing
| how to conceptualize and execute how to get the trees you want,
| but the output is Git objects.
| Nullabillity wrote:
| Sure, you can do stupid crap with the git CLI.
|
| But scaling up the magnitude of that crap, turning into it
| into a headline feature, and making it easier to forget that
| you're doing it in the first place doesn't address _why_ it
| 's a bad idea or help the situation at all.
|
| Or to relate it to another feature: yes, git supports
| rebasing. That doesn't mean that it should be a regular part
| of your workflow, or that it would be a good idea to build a
| new UI on top that focuses on it.
| seba_dos1 wrote:
| Commits _always_ represent a consistent repo snapshot (it 's
| what they actually are technically), and they don't have to
| represent a snapshot of your worktree at all even when using
| plain standard client with `git add`, so not sure what's your
| point there.
| eitland wrote:
| Edit: I now realize much (most?) of this already exist as part of
| the temporarily hidden Timeline feature in GitButler:
| https://docs.gitbutler.com/features/timeline
|
| Now we just need automatic (possibly virtual[1]]) commits that
| get created whenever one does a refactoring or other wide,
| sweeping tool assisted changes and soon my skills as dev
| archeologist would either become less valuable (because everyone
| can see what happened[2]) or more valuable (because just like no
| one I know except me uses bisect no one will use this and it will
| simplify my job.)
|
| This is (in my opinion) a great idea for a paid IntelliJ/VS
| Code/etc plug in but I realize I won't have time to do it.
|
| [1]: "virtual commits" is just my marketing speak for adding
| extra metadata without polluting the branches we usually relate
| to. One possible way are branches named virtual-<uuid> and IDEs
| that support them will hide them by default. The plug in commits
| automatically on the hidden branch while keeping the normal user
| facing branch at its current commit, and everytime one commits
| the normal user facing branch the metadata branch is merged as a
| formality.
|
| [2]: Maybe when I hover over a function I see its refactor
| history? Maybe I can click on a commit to expand it and see 4
| virtual commits:
|
| - edit + successful test,
|
| - refactor + successful test,
|
| - edit + test fail,
|
| - edit + successful test
| robertlagrant wrote:
| I think GitLab does something like this behind the scenes when
| you tick the Merged results[0] box. Fake commit to run CI
| against.
|
| [0]
| https://docs.gitlab.com/ee/ci/pipelines/merged_results_pipel...
| eitland wrote:
| Thanks!
| okl wrote:
| Don't know if that's what you're looking for but JetBrains
| tools have a Local History that saves all your local edits. It
| also notes whether unit tests where passing/failing at that
| point and let's you revert separate hunks.
| OJFord wrote:
| 'Virtual branches' is a nice idea, but I think the 'don't use
| other tooling' caveat probably makes it a no-go for me.
|
| It's not _exactly_ the same, but since it 's how I achieve the
| same goal currently, I wish it were implemented instead as
| automatic management of fork-points & rebasing the currently
| 'active' branch; even if there isn't one you consider 'active' so
| you want this merge, there still effectively is as soon as you
| _do_ anything, so it could just choose arbitrarily or create some
| (possibly temporary) other branch name to show.
|
| Because while I'm a proponent of rebasing, apparently the 'go-to'
| guy for git on the team, etc. I won't pretend it's _trivial_ to
| maintain a chain of dependent branches, or not annoying. A couple
| more aliases than I currently have would probably _help_ , but
| it'd never be as good as something like OP just clicking to
| 'rebase all on master', or where x-->Y-->Z(HEAD) 'switch to work
| on Y but keep Z', etc,=.
| schacon wrote:
| Im not following you. What do you mean by "don't use other
| tooling"?
| bdcravens wrote:
| I think many developers don't want to have to jump to another
| tool. Myself, I've found the VS Code git client "good enough"
| (even though I have a Git Tower license). Are there plans for
| extensions for the tools that support them?
| schacon wrote:
| Maybe, but I think most developers are fine jumping to
| another tool if it makes what they do every day easier or
| faster.
|
| Our goal isn't to replicate all the commands git can do.
| It's to try to create an interface for writing Git
| commits/trees that fits a more typical workflow. The UI is
| pretty different, but the end result is the same. If we can
| do that _faster_ than you can do on the command line or
| with another GUI, most developers are pretty pragmatic and
| will consider it. That is to say, if we do it well.
| OJFord wrote:
| > Quick warning. You cannot use both GitButler virtual
| branches and normal Git branching commands at the same time,
| you will have to "commit" to one approach or the other.
|
| & see the description of 'the integration commit' for some
| detail of how they make it work:
|
| https://docs.gitbutler.com/features/virtual-
| branches/integra...
|
| Basically anything you do with `git` (or another GUI) will
| get blown away by GitButler; or if you change branch away
| from it then _it_ will stop working.
| schacon wrote:
| You can switch back and forth rather easily. But if you
| have multiple branches applied, some git commands wont make
| any sense.
| OJFord wrote:
| Sorry I didn't realise you worked on GitButler when you
| asked.
|
| > But if you have multiple branches applied, some git
| commands wont make any sense.
|
| Sure, because of the way it's implemented. But that's
| just a variation on what I said really - I like the idea,
| see value in automating some management so that I can see
| & work on something that's the amalgamation of multiple
| branches, but I don't want to (have to) go 'all in' on
| some third-party tool, it needs to work like an extension
| of vanilla git for me, so everything else can keep
| working as normal.
|
| It needs to be implemented in 2D so that git CLI can
| still understand, to put it in the docs' terms.
| schacon wrote:
| Just to clarify, yes, in order to work on multiple
| simultaneous branches, you need a tool that knows what
| that means, which git does not really. But you can also
| very easily run 'git checkout main' and do whatever and
| then go back to GitButler and restore state. It's pretty
| good at knowing what mode you're in.
|
| Basically all your virtual branches also live in
| refs/gitbutler/[name] and GB won't touch stuff in
| refs/heads/ because we like the idea of making sure we're
| not clobbering things you don't expect us to. You can
| just think of GitButler as a tool for managing
| refs/gitbutler branches.
|
| All git tooling work totally fine, even generally if
| you're using something (like commit) that doesn't really
| make sense in the context of multiple branches.
| videlov wrote:
| This limitation stems from the fact that GB introduces an
| additional dimension of versioning on top of Git. One way
| of thinking of what it does with virtual branches is like
| "multiplexing" multiple branches onto the same working
| directory. On the way out they get "demuxed" into plain git
| trees.
|
| With that said, the tool is very cautious _not_ to mess
| with any existing branches. This is the very reason it
| operates on a separate integration branch. Switching
| between the "special/integration" branch and any other
| branch is also not an issue.
| OJFord wrote:
| Maybe the problem is that the docs aren't selling me
| anything that I don't currently achieve by (manually)
| rebasing? So I'm left thinking I'd rather stick with my
| current workflow (or maybe motivated to alias it up a bit
| more) than adopt this limitation.
| schacon wrote:
| Well, one thing we can do is "merge" together multiple
| non-conflicting branches in your working directory
| without creating (or, I suppose, having to undo) merge
| artifacts.
|
| Like if you had three branches and you wanted to see how
| they will work when they're all merged, and you find an
| issue in one and want to fix it, GB makes this really
| simple. You just apply them all and then fix something
| and make sure it's on the right branch and commit there
| (and push again if you want).
|
| With Git, you would have to actually do either two merges
| or a three-head merge, see how it goes, undo the merge,
| switch to the target branch, commit a fix there, re-merge
| them all to see if that worked, etc. In Git there isn't a
| _real_ great way to combine trees without modifying
| history. We make those things a little more orthogonal.
| OJFord wrote:
| Let me first of all be clear that I believe you have good
| reasons, and reiterate that I think it's great and want
| the features, I'm just trying to understand why it
| doesn't/can't work with my current rebasing workflow.
|
| Take my example before, X-->Y-->Z(HEAD) where each of
| X,Y,Z here are (at least potentially) multi-commit
| branches, not just commits; based on top of each other.
|
| If I need to fix something in the 'merged' (I said
| 'amalgamated' previously exactly to avoid that, but we
| can stick with scarequotes if you like ;)) result, and it
| belongs say with Y, then I will either:
| git fixup <some-commit-between-X-and-Y> #
| fixup = my alias for: "!f(){ target=\"$(test -n
| \"$1\" && git rev-parse \"$1\" || git fzsha rev-parse)\";
| git commit --fixup=\"$target\" ${@:2} && EDITOR=true git
| rebase -i --autostash --autosquash \"$target^\"; }; f"
| # basically commit -m'!fixup <target-commit>', and then
| rebase target-commit^ to apply it
|
| or if it belongs as a standalone commit on that branch:
| git commit -m 'whatever' git rebase -i Y # and
| move it to the top so the Z stuff follows it
|
| and then in either case: git branch -f
| Y <new-location> # if I'm doing this
| repeatedly, <new-location> is often HEAD~N, for however
| many N commits on Z.
|
| For this feature at least, it seems like GB could be
| implemented as automation/abstraction of that workflow,
| which (clearly) is perfectly penetrable to git.
|
| I've been working like this for more than a decade, so
| even if GB's better & easier it's a tough sell to
| partially break interoperability; to _need_ to use GB GUI
| to manage the situation rather than have it as a choice.
| videlov wrote:
| I think you are right that our documentation does not
| sufficiently communicate what the application does
| especially in various corner cases.
|
| For my own sake, allow me to articulate the core value
| proposition once more. GitButler's virtual branches
| permit two novel use cases:
|
| - A developer can lazily assign diffs/changes to belong
| to separate logical branches while maintaining their
| content within the same working dir. Those logical
| branches can be converted to plain git trees at any time.
| The canonical use case here is doing a bugfix while
| working on an unrelated feature - with the proposed
| workflow one can separate those contributions into
| discrete PRs while still having the content of both
| within the working dir.
|
| - A developer can apply and unapply the content of remote
| branches to their working directory for the purpose of
| testing & review. This is distinct from rebasing and
| merging because it does not introduce merging or rebasing
| into the branch that the developer was originally working
| on.
|
| In any case, we will work on communicating and
| documenting the tool better.
| jcrben wrote:
| This convinced me that it's maybe worth trying. Given the
| number of tools out there and how easy git feels for me
| right now (pretty easy), that's a highish bar
| shp0ngle wrote:
| GitButler will literally not work when you use raw git, or
| other tools.
| schacon wrote:
| It works fine. The only thing that is a problem is "branch"
| and "commit", things that use the index. But that makes
| sense. If you have setup two virtual branches and then from
| the cli run "git commit", which branch do we commit to?
| OJFord wrote:
| If it were implemented as a 'rebased chain' as I
| described rather than a multi-head merge: the active one,
| just as vanilla `git commit` would.
| trws wrote:
| This makes sense, but I have to say I would find it much
| less concerning if doing "commit" would apply to some
| well-defined selection. Either something marked in the UI
| as "default" or "first" or something so that normal git
| operations wouldn't cause failure. I realize it makes one
| of the virtuals special, which is unfortunate, but it
| means normal command line workflows would be _safe_ even
| if they wouldn't always provide all the options.
| schacon wrote:
| Two things. One is there is a commit dialog per virtual
| branch. You do a commit on a branch. The selection is all
| the files/hunks you see on that branch, or you can
| selectively commit parts of those changes (such as a add
| -i type commit). It's pretty much identical to how Git
| (or any other Git GUI) handles commit selection, except
| you can have more than one branch that you're working
| with, applied, at a time.
|
| Also, GitButler, as far as I can think of, never causes
| normal git operations to fail. It doesn't put the project
| git repo in a state where git can't operate.
| trws wrote:
| That's great news, and I'll play with it some more to see
| if I misunderstood the docs. If so there may well be no
| issue here. Thank you for clarifying.
| alexeiz wrote:
| > The only thing that is a problem is "branch" and
| "commit", things that use the index.
|
| A git branching tool that breaks git branch and commit -
| what a novel concept!
| schacon wrote:
| But even if you do run "checkout" or "commit", we still
| notice that you did that and try to put you in the state
| you want.
| charles_f wrote:
| There's a very similar feature which allows you to checkout
| multiple branches from a single repo, and comes out of the box
| with git.
|
| Try 'git worktree'
|
| https://git-scm.com/docs/git-worktree
| schacon wrote:
| One of the first comments was this, and there is a longer
| answer to why this is different if you're interested.
|
| Short answer is that worktrees dont exist in the same working
| directory like virtual branches do.
| motoboi wrote:
| Intellij IDE support this through changelists.
|
| UPDATE: add link to documentation
| https://www.jetbrains.com/help/idea/work-on-several-features...
| schacon wrote:
| Sort of, but it's honestly closer to normal git branches. If
| I'm not mistaken, you can't have multiple changelists applied
| simultaneously. You still have to switch between active groups
| of changes, no?
| sesm wrote:
| Changelists are more like splitting your local changes into
| several groups. The idea is that you usually want to commit
| only one group of changes while leaving others uncommitted.
| For every changed line the editor will show you whether it's
| modified in scope of your active changelist or in scope of a
| non-active one. Also, you can easily 'shelf' and 'unshelf' a
| changelist.
|
| I was actively using changelists for some time, but later
| switched to git worktrees instead. Now I usually have only 2
| changelists: 'Default' and 'Hacks - don't commit!'.
| motoboi wrote:
| you can have any number of changelists (or changesets) active
| at once.
|
| You can actually have changes in the same file with some
| lines in different changesets.
|
| When needed, you can commit changes to git (when you finish
| doing work) to make them visible to coworkers.
| schacon wrote:
| I see. Well then there are some similarities. Though you
| can use ours with any editor :)
| motoboi wrote:
| yours is GREAT!
| radarsat1 wrote:
| Not sure this is something I need. One thing I do need though,
| maybe someone knows a solution:
|
| Often I find myself maintaining a handful of "local" changes. I
| make some changes that only make sense in my local environment,
| that I don't want to push.
|
| What I end up doing is maintaining these changes as a commit,
| committing on top of them, and using `git rebase -i` to
| periodically move them up. Then before I push, I have to
| temporarily rewind the branch to remove them, push, then cherry-
| pick them again.
|
| It's all a bit awkward and I would love a tool that maintains a
| kind of "virtual branch" that isn't shown but is automatically
| re-based on top every time I make a commit, maybe letting me
| resolve conflicts or even telling me ahead of time if I've
| created one, before committing.
|
| Someone must have already solved this, or am I doing it all
| wrong?
| jxcl wrote:
| It depends on the kinds of changes you mean. The kinds of local
| changes I have are just env variables that allow the software
| to run locally a little bit differently than in the production
| environment. I'm able to do this by using a .env file and a
| library for my languages of choice that read a .env file if
| it's there, but use defaults that make sense for prod when it's
| not there. Then the .env file is gitignored so it doesn't make
| its way over to the production environment.
|
| Each developer can modify their .env however they want without
| having to make any changes visible to git.
| thfuran wrote:
| It sounds like a project structure issue where local
| environment config that shouldn't be tracked is (or a lack of
| optional additional config that could be specified locally). Or
| maybe I'm misunderstanding the sort of change you're talking
| about.
| gregmac wrote:
| This is what I thought, too.
|
| I generally try to have support for this in the application,
| using a `.local` config file override which is in .gitignore.
| If one person has this trouble, chances are the rest of the
| team does too, and making this change is usually less time
| than the team collectively spends messing with it in a week.
|
| Sometimes there's an existing workflow everyone just accepts
| (project in a specific path; some dependency manually
| installed locally; specific HOSTS alias to the database
| server IP; etc) but so long as you don't break that while
| also making it better/easier, I've found it's usually
| accepted.
| o11c wrote:
| Specifically, the fix is usually:
|
| * Rename the configuration file in the repository to add an
| `.example` suffix (preferably, to an empty `-local` file if
| includes exist)
|
| * As part of the build system, copy all `.example` files to
| remove the suffix, but only if they don't already exist.
| nodogoto wrote:
| I also have this issue. What I do is work in a separate branch
| and cherry pick commits over to main then rebase.
|
| I bet that what you describe could be implemented with stashes
| and scripts run on hooks, but that feels like fighting the tool
| too much. There's probably something we're missing?
| fmbb wrote:
| I do this all the time in almost any repo I work in. I usually
| just keep the difference as "junk" in my local work tree.
|
| I never git commit -a anyway, my commits are always kinda
| prepared and meticulous.
|
| I just live with the spurious diff and extra stashing. All
| alternatives seem to complicated. I do love the way Jetbrains
| IDEs manage changesets though. It is a big improvement over
| only (not) using git for this, but I rarely use their IDEs
| nowadays.
| elzbardico wrote:
| I think those local changes should be treated as config that
| can be overriden in the local environment through a combination
| of files that are listed on .gitignore and environment
| variables.
| nhellman wrote:
| > Then before I push, I have to temporarily rewind the branch
| to remove them, push, then cherry-pick them again.
|
| You do not necessarily need to modify and restore your branch
| head just to push in this case. If you have e.g. two temporary
| commits at the top of the branch, you can use e.g. "git push
| origin HEAD~2:master" to skip those commits when pushing.
| arjie wrote:
| I have the same problem, but I haven't found a convenient
| solution yet.
|
| Though stated differently, it's exactly the same thing:
| https://stackoverflow.com/questions/76717374/how-do-i-keep-a...
|
| A somewhat related but inexact match is when you want to ignore
| changes to some files (e.g. I sometimes vary some test code
| locally and don't really want that to be exposed to others) for
| which I found a convenient fix on SO and compiled into an
| answer https://stackoverflow.com/a/70075113/3858681
| OJFord wrote:
| I think this tool _would_ help that, because you 'd stick your
| only-makes-sense-for-you changes on `my-eyes-only` branch; that
| branch would always be in your 'integration' commit, along with
| whatever other 1+ branch(es) you were working, and you'd just
| only push the latter.
|
| That said even without GitButler you can improve it a bit: you
| can `git push <remote> HEAD^:<branch>` rather than 'temporarily
| rewinding the branch to remove them'. You could also consider
| just never committing it, stashing the changes if you really
| needed them out of the worktree, and if they're whole new files
| adding to `.gitignore`.
| schacon wrote:
| This is true. For the use case you're talking about, just
| sticking some changes in a semi-ignored virtual branch
| basically will do what you are talking about. :)
| alexeiz wrote:
| If you need a lot of local changes that you can't commit, then
| either your repository or your project (or both) are organized
| improperly. You must have committed files to git that should
| never be committed or you project doesn't allow you to adapt to
| the local environment without changing files committed to git.
|
| For example, you may have hardcoded paths to tools and
| compilers, but in your local environment those tools have
| different paths. This is a problem with the project
| organization.
|
| Another example, you may have something like VSCode settings
| file which somebody decided to commit to git, but those
| settings only make sense in his environment and not anybody
| else's environment.
|
| Instead of searching for a workaround to these problems like
| virtual branches, you should push for fixing your project
| organization and you git repository.
| weinzierl wrote:
| Not a complete solution to the more generic problem you
| described, but enough for me most of the time:
|
| 1. I try to arrange things in a way that I can keep my local
| changes in files that are not in the repo. For example: If your
| software loads a file from several places and the more specific
| one (current dir) wins over a more generic one ($HOME or /etc)
| you can try to keep a local one.
|
| All these files end up being .gitignored from a place that is
| not in the repo itself and not shared: Either in the global
| .gitignore or .git/info/exclude. If the files don't have to be
| in a specific place I put them in a subdir called aux, which
| also contains a .gitignore with just an asterisk (*) on the
| first line. That way it never gets added and it doesn't leave a
| trace in any .gitignore outside of aux.
|
| 2. Files that are checked in but need local modifications are
| marked with `git update-index --assume-unchanged`
| Night_Thastus wrote:
| Stash?
|
| That's what I do, in any case. If I have a few various small
| configurations I want to carry around, I stash them when I need
| to swap branches and then pop them off.
|
| That way it's never in the history and there's none of that
| nonsense to deal with.
| steveklabnik wrote:
| Very cool, congrats Scott!
|
| I have been getting very into jj over the last few weeks, which
| is larger in scope, but also currently uses git as a backend of
| sorts and supports similar workflows. Do you have any
| thoughts/opinions/comparisons to it?
|
| EDIT: I just mentioned this in the jj Discord and schacon is
| already in there, I didn't realize!
| schacon wrote:
| I think jj is super cool. But I like the idea of a killer GUI
| that makes so many things so fast and easy to do that I would
| use it instead of the cli. Ive never used a git gui for more
| than a day. Ive used GB daily for months and I love my dogfood.
|
| But re:jj, Im pro anyone trying to do interesting things around
| VCS/Git. Its been a very long time of general complacency.
| steveklabnik wrote:
| Cool, thanks! I am also in the "never used a git gui for more
| than a day" camp, so that makes a ton of sense! Looking
| forward to giving this a shot.
| shp0ngle wrote:
| I have to say- I'm just deadly scared to do this
|
| I'm already quite confused when something wrong happens and I get
| confusing git messages. To add some more abstractions on top of
| that... just so scary.
| elzbardico wrote:
| Oh this is very very cool! Way better than dealing with change
| lists on IntelliJ.
| jupp0r wrote:
| Slightly offtopic, but because this was prominently featured in
| their demo:
|
| AI generated commit messages are horrible. I'm not opposed to
| them in principle, but currently every implementation I have seen
| uses the code change itself as the context to generate the
| message. This is just wrong. Commit messages are there to convey
| information that's explicitly not contained in the code itself
| and shouldn't just repeat or summarize the code changes (that's
| what the diff is for). They should contain the reason why the
| code change was made in the first place, approaches that were
| considered and did not make it into the code change, etc. Short
| everything that's not contained in the diff.
|
| AI commit message generators incentivize people to write the
| wrong type of commit message.
| schacon wrote:
| Well, two things. They're not on by default, you have to enable
| them. Both because it means we have to send your code diffs
| outside your machine, but mostly because lots of people don't
| want them.
|
| The other is that the point is not to take away writing good
| commit messages. The opposite. One item on our task list is to
| create the _best_ commit message editor we can think of. One
| that Linux kernel hackers would want to use to craft a great
| commit. But that's for a bit later.
|
| The AI thing, in our minds, is to eliminate "fixed some stuff"
| messages that have zero semantic value. If you're committing
| just to push, you might as well have AI give you some
| searchable text in less time than it would take you to write a
| crappy commit message.
| jupp0r wrote:
| I totally see where you are coming from, I've just seen the
| fallout from more and more tools adding similar features and
| the net benefit has not been there, quite the opposite. It
| makes it easy to do the wrong thing.
|
| That being said, with enough context (slack threads about the
| feature, zoom meeting transcripts, design docs, etc) I could
| totally see AI commit message generation being great in the
| future, but that's obviously a harder problem to solve than
| just piping the diff to OpenAI or similar.
| ratherbefuddled wrote:
| An aside but this is giving me ClearCase config_spec vibes.
|
| Memory is hazy - possibly due to trauma - but you could create a
| config_spec which would make your view (roughly: working tree)
| pick up different bits of the filesystem from different branches.
| Branches might only exist on some of the filesystem, depending on
| how you set up auto branching and what you checked out, and you
| had to know what the config_spec rules everyone was working to
| were or the whole repository was effectively broken.
|
| It was very complicated to get your head around - I guess not
| helped by the codebase I was working on at the time which tended
| to concentrate change in the same 20 files.
| bvrmn wrote:
| Spec was a huge pain. For our project it contained around 50
| directories to fetch from. Directory per component with own
| versioning. Only a few persons know working combinations you
| could use.
| bvrmn wrote:
| As usual couldn't recommend stacked git [1] enough. If you prefer
| CLI it greatly reduces cognitive load about branches/rebases
| especially if your work with review server allowing to see
| changes between force pushes (gitlab, gerrit).
|
| [1]: https://stacked-git.github.io/
| swozey wrote:
| The idea with this seems to be - I'm working on a branch and
| can't easily switch right now to put out a patch, or MY current
| branch HAS the patch and I want to push that out before the
| entire branch is ready so I make the stg commits and pushes and
| that patch is released.
|
| In their example [1] at the very end they commit --all of their
| patches into the main patch and I guess merge that into main.
|
| I'm kind of confused on the commit --all part. If you're
| putting out single patches like stg committing your current
| file, what benefit do you get by making multiple patches, not
| pushing them to main immediately, but holding them as a patch
| series until you're done to then merge all together? Is it
| mostly for the developer to keep track of X change = Y patch
| while you're fixing multiple things?
|
| Like, I'm in a branch right now that unfortunately I've got 3
| patches doing different unrelated things. But the changes are
| in a lot of the same files, so to use this patching I would
| have to pull out the other 2 patches in the same file, stg
| commit the one patch, then add patch 2, stg commit patch 2, etc
| that eventually just commit --all into the same file but are
| now identifiable by X change = Y patch?
|
| Definitely a lot easier if had I had started out using this and
| stg committed patch 1 before writing patch 2 and 3.
|
| Am I right about all of this or missing anything, or completely
| off? I wrote it in another comment but I deal with a lot of
| massive infrastructure changes where one push is deploying
| basically entire datacenters of load balancers and clusters,
| etc. a LOT of files are modified at once, so I'm in repos for a
| WHILE until I do my push and it makes it very hard for me to
| switch branches if I need to hop around real quick, I'm super
| apprehensive about stashing and changing branches in the middle
| of big PRs but I really couldn't explain why. I always worry
| I'm going to lose something or forget about the branch, etc.
|
| I could definitely patch on things like, "This patch adds the
| loadbalancers and routing, this patch adds the cluster, this
| patch adds the security."
|
| [1] https://stacked-git.github.io/guides/usage-example/
| bvrmn wrote:
| You are right about conflicting changes. They are hard to
| manage as order independent changes with any tool. In stgit
| context I often split big change into multiple ordered
| patches and yes I know what piece (incremental edit) should
| be added to what patch. Classic example: doing refactoring
| around main task. Refactoring goes into one patch, main
| changes into another. It allows greatly simplify review
| process.
|
| But common case for me is to have multiple independent
| patches for different features/bug fixes. For example one
| current project has 12 patches, another one has 5 patches.
| Two or three are dedicated to current work. Rest are drafts
| with ideas.
|
| > I'm kind of confused on the commit --all part.
|
| Me too. Almost always task is represented by a single patch.
| And I push changes on patch basis. One patch -> one review.
| swozey wrote:
| This looks awesome. I've used git for over a decade and I
| _always_ have to look up rebase, reset head, etc commands that I
| use maybe a few times a month.
|
| > Undo, squash and amend your work by just dragging and dropping.
| No need to wrestle with rebase -i.
|
| I think one thing that would be really cool to add to this and
| other git actions is to put the git commands that will be run
| based on the users inputs somewhere on the screen so the user
| doesn't COMPLETELY forget how to use git, and actually may get
| better at it with the additional help. Some may think that would
| lead people to not using gitbutler anymore, but no way, I am
| absolutely happy never having to write another git command beyond
| commit and push. Every time I have to do something I don't
| usually do in git it slows me down by 5+ minutes and it's super
| annoying and god forbid its a too-big PR that I have to cherry
| pick etc.
|
| Also, another thing I am DYING for, none of my IDEs
| (intellij/vscode) as far as I know have extensions/options for it
| - if I do a commit and push in a vscode session, COLOR THE FILES
| SO I STILL KNOW WHAT I EDITED. DON'T UNMARK THEM AS EDITED. Make
| them yellow or blue or something, I don't care, just color them.
| You can absolutely track git diff / cache etc against 1 or 2
| previous merges and base file colors on it. I do this all over my
| CI/CD scripts to determine whether or not we need to do a docker
| build/deploy etc or not based on files that diff from origin/main
| + your current branch 1 behind. If you didn't modify a
| Dockerfile, we don't do a docker buildx with your push.
|
| I HATE when I have to commit/stash a massive PR with 30+ files
| edited because I need someone else to pull it or whatever and
| then have NO easy way to see what I changed in the commit vs main
| in my UI because as soon as I commit and push all of the tracked
| files change color to the "im just a file" grey color.
|
| It causes me to commit a lot less when I should commit more. I'm
| pretty sure at some point I'll have to dive into js and write
| this myself. Probably just store modified files in a list and
| diff against main. I have an extension (wakatime) that tracks how
| long I've written code (not idle time at all, actual typing) in a
| BRANCH so you can definitely track a session based on something
| like your time writing code in a branch and cross reference that
| vs modified files.
|
| The current branch I'm in I've written 4 hours of code in over
| 1-2 weeks, so I just need to know in that 4 hours what files I
| touched.
| thesh4d0w wrote:
| It's really confusing to have a windows logo underneath the
| download link, but then no download for windows.
|
| I guess that windows icon is supposed to be grayed out, but all 3
| icons are shades of gray so it's not obvious at all until you
| mouse over it and see "coming soon".
| mfuzzey wrote:
| doesn't work with other tools + GUI only => non starter
___________________________________________________________________
(page generated 2024-02-13 23:00 UTC)