← Back to context

Comment by lalaithion

2 days ago

The disconnect between git's beautiful internal model of blobs, a tree of commits, and pointers to commits, and the command line interface is so wild. All of these recipes are unintuitive even if you have a firm grasp of git's model; you also need to know the quirks of the commands! To just look at the first one... wouldn't it be more intuitive for the command line interface to be:

    # this command exists already;
    $ git switch -c some-new-branch-name
    # is there a command that simply moves a branch from one commit to another without changing anything else? It feels like it should be possible given how git works.
    $ git move-branch master HEAD~

The real "internal model" of git contains much more data/moving parts.

There isn't one tree of commits, there are typically at least two: local and remote

Branches are not just pointers to commits, but also possibly related to pointers in the other tree via tracking.

Stash and index and the actual contents of the working directory are additional data that live outside the tree of commits. When op says "avoid git reset hard" it's because of how all these interact.

Files can be tracked, untracked and ignored not ignored. All four combinations are possible.

The "move a branch from one commit to another without changing anything" command is "git reset".

"git reset --hard" is "...and also change all the files in the working directory to match the new branch commit".

"git reset --soft" is "...but leave the working directory alone".

  • Actually, "git reset --soft" moves the current branch to another commit, without moving the index (aka staging area) along with it, whereas "git reset" (aka "git reset --mixed") moves the current branch AND the index to another commit. I really couldn't wrap my head around it before I had gone through "Reset demystified" [1] a couple times - it's not a quick read but I can strongly recommend it.

    [1] https://git-scm.com/book/en/v2/Git-Tools-Reset-Demystified

  • git reset only works if you're on the branch you want to move, which is why every one of these example flows has you create your new branch, then do the reset, then switch to the new branch, instead of just allowing you to move a branch you're not on.

> The disconnect between git's beautiful internal model of blobs, a tree of commits, and pointers to commits, and the command line interface is so wild

Something I heard somewhere that stuck with me: git is less less of a Version Control System, and more of a toolkit for assembling your own flavor of one.

  • > Something I heard somewhere that stuck with me: git is less less of a Version Control System, and more of a toolkit for assembling your own flavor of one.

    That's how it is in principle, but it seems to me that there aren't that many different CLI "porcelains" in practice. Kind of like how Knuth figured people would essentially write their DSLs on top of plain TeX, not spend most of their time in giant macro packages like LaTeX.

    • > That's how it is in principle, but it seems to me that there aren't that many different CLI "porcelains" in practice.

      I think that's because most of the people that make custom tooling to support particular workflows build it into graphical (including IDE extensions, web-based. etc.) frontends, not CLIs.

I prefer just using `git switch` because it's easy to remember the flags (and the position of arguments), but you're right, there is a simpler way:

    git switch -c some-new-branch-name
    git branch -f master HEAD~

Are there alternative git command lines that keep the beautiful internals, but implement a more elegant and intuitive set of commands to manage it?

  • Check out jujutsu or jj (same thing). It's its own VCS, but it uses git as a backend, so it works with GitHub and other git integrations

  • Another vote for jujutsu. No one else needs to know you're using it. You can think of it as just a different CLI for git (although you shouldn't mix them). I used to use third-party interfaces like lazygit, but I don't need them anymore because jujutsu _just makes sense_.

  • Seconded jujutsu. It's 100% git-compatible and one of those rare birds that is both more powerful and simpler to use in practice due to rethinking some of the core ideas.

The "move a branch" command is `git push .`. Yes, you can push to the current repo. I have a script called git-update-branch which just does some preflight checks and then runs `git push --no-verify . +$branch@{upstream}:$branch` to reset a branch back to its upstream version.

  • > The "move a branch" command is `git push .`. Yes, you can push to the current repo.

    Wouldn't that copy a branch rather than moving it?

For move-branch: Use `git branch -f master HEAD~` if you're currently on another branch, or `git reset --soft HEAD~` if you're currently on master.

> is there a command that simply moves a branch from one commit to another without changing anything else? It feels like it should be possible given how git works.

git switch -C master HEAD~