'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,=.
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?
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.
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.
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?
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.
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.
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.
> 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.
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`.
What you are describing sounds very much like a common situation when working downstream of an open source project. I believe (and agree) the commonly accepted solution are patch queues, for which a number of tools exist: Andrew Morton's patch scripts, quilt, mercurial queues, guilt, stgit, to name just a few.
As you describe, just maintaining a branch that regularly gets rebased onto the latest upstream using only git's inbuilt tools also works. I do that, it's a good enough solution when keeping the history of old branches by merging them into a "patch queue history" branch before rebasing so they don't get lost.
Yet, I feel a much better gui tool to support this workflow is both possible and desirable. I totally agree with the venerable schacon in that regard. So whenever I learn of a new git gui tool I get excited and hopeful.
I actually started a tool some 15 years ago, with the vague hope I could one day open source it, or at least show it around so that others can take the good ideas and run with them to implement in their tools. Unfortunately I never had enough time to move it forward beside my day job.
15 years ago I built atop mercurial queues with a Mac gui. Of course a proper tool should be built on top of a cross-platform gui and libgit2. I can't do much at the moment, but I still hope one day someone will build on my ideas or come up with better ones.
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`
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.
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
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.
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?
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.
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!
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.
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.
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.
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).
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."
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:
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.
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.
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.
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)
> By default, everything in the GitButler client is free to use, but we credit ourselves as the committer in your virtual branch commits. Community members and supporters of GitButler can turn this off.
It’s hard to get sold on a Git workflow tool that proposes a new (or not supported by git(1)) way of working outside of a long-form article. I see a lot of little paragraphs here and there on the website, like “faq” answers about specific things. But I can’t just piece together “virtual branches” and “doing things at the same time” (or something) and get what this is about. The GitHub blog does as good job of presenting new git(1) features: (1) explains the problem or context, often using several paragraphs and then (2) presents how git(1) can now solve that, concretely.
I would need a concrete case. Maybe how I am working on five different “features” that all depend on each other, maybe serially. And how I can work with a pull request (forge) somewhere and get those merged and at the same time get those virtual branches updated and managed for me. Or at least that’s what I imagine that this is about. Also how existing solutions (there are many of them) fall short or have different tradeoffs.
PS: Including “git butler” in the title... would have been useful.
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.
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
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.
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.
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.
That sounds like a fail to me. The whole point of using Git commits is that once committed, it’s just there. And things can be shared. The point of collaborative version control.
I can manage multiple changes at the same time—that’s the index, e.g. selectively apply changes to commit in Magit. I can commit and shuffle things around—that’s rebase, cherry-pick. I can push and pop commits with some third-party stack management tool that integrates with Git.
In other words: I can use Git or I can use Git + whatever rinky-dink non-database patch management (?) tool that Intellij (IDEA) has implemented. One tool is simpler than two.
This feels like we’re back in 2002 and having to use a patch management system on top of the rigid centralized VCS in order to get some local freedom. But we already have that…
But Intellij’s thing is probably more nimble for managing a lot of unrelated things at the same time. Not that I want to do that though because of increased chances of merge conflict and just the sheer brain-overhead of it all.
If you need to change to an older commit and avoid the thrashy, unreliable indexing of Intellij (IDEA): use git worktrees.
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?
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.
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.
I feel like this is much different. We're not saying "patch these things together into a materialized view", we're saying "here is the diff between your working directory and origin/master, which branch owns which hunk" and keeping that mapping around.
We keep monitoring it until we see changes upstream (someone merged into master) and then we let you update the difference (merging or rebasing commits you made if needed)
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.
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.
I found the virtual branches compelling, but when I tried it out, I got myself into a mess.
I think that this could all be done using real branches, plus an integration branch with post-commit hooks to backport each commit onto the "default" branch and recreate the octopus merge "integration" commit each time. Would that work, or am I missing something?
Looks like a gimmick to me. You can achieve this easily by working on a scratch branch, and then, once ready (and with git commit -p), building the commits you want. Then, you cherry-pick them into other branches.
The rest of it is pure gimmick and would be force turned off in my team.
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".
I think "non-conflicting" is the crux here. I understand this is at the minimal viable product stage, but there are a number of ways to handle that already.
To me it only starts to get interesting when conflicts are involved.
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.
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.
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.
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.
OJFord|2 years ago
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|2 years ago
ckolkey|2 years ago
What's the advantage of a tool like this over that?
schacon|2 years ago
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.
frizlab|2 years ago
adrianmonk|2 years ago
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.
radarsat1|2 years ago
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?
thfuran|2 years ago
Night_Thastus|2 years ago
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.
fmbb|2 years ago
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.
nhellman|2 years ago
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.
OJFord|2 years ago
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`.
tera1|2 years ago
As you describe, just maintaining a branch that regularly gets rebased onto the latest upstream using only git's inbuilt tools also works. I do that, it's a good enough solution when keeping the history of old branches by merging them into a "patch queue history" branch before rebasing so they don't get lost.
Yet, I feel a much better gui tool to support this workflow is both possible and desirable. I totally agree with the venerable schacon in that regard. So whenever I learn of a new git gui tool I get excited and hopeful.
I actually started a tool some 15 years ago, with the vague hope I could one day open source it, or at least show it around so that others can take the good ideas and run with them to implement in their tools. Unfortunately I never had enough time to move it forward beside my day job.
15 years ago I built atop mercurial queues with a Mac gui. Of course a proper tool should be built on top of a cross-platform gui and libgit2. I can't do much at the moment, but I still hope one day someone will build on my ideas or come up with better ones.
weinzierl|2 years ago
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`
alexeiz|2 years ago
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.
arjie|2 years ago
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
jxcl|2 years ago
Each developer can modify their .env however they want without having to make any changes visible to git.
nodogoto|2 years ago
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?
elzbardico|2 years ago
keybored|2 years ago
charles_f|2 years ago
Try 'git worktree'
https://git-scm.com/docs/git-worktree
schacon|2 years ago
Short answer is that worktrees dont exist in the same working directory like virtual branches do.
steveklabnik|2 years ago
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|2 years ago
But re:jj, Im pro anyone trying to do interesting things around VCS/Git. Its been a very long time of general complacency.
XorNot|2 years ago
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|2 years ago
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.
bvrmn|2 years ago
[1]: https://stacked-git.github.io/
swozey|2 years ago
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/
eitland|2 years ago
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|2 years ago
[0] https://docs.gitlab.com/ee/ci/pipelines/merged_results_pipel...
okl|2 years ago
avgcorrection|2 years ago
aeonik|2 years ago
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|2 years ago
schacon|2 years ago
conradludgate|2 years ago
This is a non-starter, sorry.
noplacelikehome|2 years ago
avgcorrection|2 years ago
I would need a concrete case. Maybe how I am working on five different “features” that all depend on each other, maybe serially. And how I can work with a pull request (forge) somewhere and get those merged and at the same time get those virtual branches updated and managed for me. Or at least that’s what I imagine that this is about. Also how existing solutions (there are many of them) fall short or have different tradeoffs.
PS: Including “git butler” in the title... would have been useful.
Nullabillity|2 years ago
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|2 years ago
schacon|2 years ago
seba_dos1|2 years ago
baq|2 years ago
schacon|2 years ago
motoboi|2 years ago
UPDATE: add link to documentation https://www.jetbrains.com/help/idea/work-on-several-features...
avgcorrection|2 years ago
1. Not part of Git at all—not committed
2. Can’t be shared because, duh, not part of Git
That sounds like a fail to me. The whole point of using Git commits is that once committed, it’s just there. And things can be shared. The point of collaborative version control.
I can manage multiple changes at the same time—that’s the index, e.g. selectively apply changes to commit in Magit. I can commit and shuffle things around—that’s rebase, cherry-pick. I can push and pop commits with some third-party stack management tool that integrates with Git.
In other words: I can use Git or I can use Git + whatever rinky-dink non-database patch management (?) tool that Intellij (IDEA) has implemented. One tool is simpler than two.
This feels like we’re back in 2002 and having to use a patch management system on top of the rigid centralized VCS in order to get some local freedom. But we already have that…
But Intellij’s thing is probably more nimble for managing a lot of unrelated things at the same time. Not that I want to do that though because of increased chances of merge conflict and just the sheer brain-overhead of it all.
If you need to change to an older commit and avoid the thrashy, unreliable indexing of Intellij (IDEA): use git worktrees.
schacon|2 years ago
swozey|2 years ago
> 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.
ratherbefuddled|2 years ago
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.
schacon|2 years ago
We keep monitoring it until we see changes upstream (someone merged into master) and then we let you update the difference (merging or rebasing commits you made if needed)
We're not inventing massively new mindspaces.
bvrmn|2 years ago
shp0ngle|2 years ago
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.
alsuren|2 years ago
I think that this could all be done using real branches, plus an integration branch with post-commit hooks to backport each commit onto the "default" branch and recreate the octopus merge "integration" commit each time. Would that work, or am I missing something?
lionkor|2 years ago
The rest of it is pure gimmick and would be force turned off in my team.
thesh4d0w|2 years ago
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".
tera1|2 years ago
osrec|2 years ago
videlov|2 years ago
mfuzzey|2 years ago
jupp0r|2 years ago
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|2 years ago
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.
catapart|2 years ago
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|2 years ago
elzbardico|2 years ago
ohghiZai|2 years ago
[deleted]
notso411|2 years ago
[deleted]