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