Comment by lalaithion

Comment by lalaithion 6 months ago

36 replies

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~
Certhas 6 months ago

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.

  • lalaithion 6 months ago

    None of these seem to preclude a command to make an arbitrary branch point to an arbitrary commit without changing anything else.

    • karatinversion 6 months ago

      You are looking for

        git update-ref <branch-name> <commit-sha>
      • DiggyJohnson 6 months ago

        Wouldn't the fail or break under any circumstance where they don't immediately share a history?

        • karatinversion 6 months ago

          I just tested it by creating a repo with two branches without a common ancestor, and I was able to move a branch pointer to either history with update-ref, so no, I don't think so

    • fragmede 6 months ago

      This works if the branch exists or creates it if it doesn't exist, but not if it's checked out.

          git branch -f branch_name commit
      
      if it's checked out:

          git reset --hard commit
      • seba_dos1 6 months ago

        > but not if it's checked out

        ...and for a good reason that should be apparent to anyone who understands git's model (HEAD points to a ref in this case, so if you suddenly change what that ref points to without updating the working tree you create an inconsistency).

        You can do that manually of course (with `git update-ref` or even a text editor), but then you get to clean up the mess yourself.

neild 6 months ago

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".

  • rav 6 months ago

    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

  • lalaithion 6 months ago

    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.

Terr_ 6 months 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

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.

  • JadeNB 6 months ago

    > 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.

    • dragonwriter 6 months ago

      > 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.

pitaj 6 months ago

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~
  • DangitBobby 6 months ago

    You should also be able to do

      git branch -f master origin/master
    • pitaj 6 months ago

      This doesn't work if your local master was already ahead of origin

      • DangitBobby 6 months ago

        Indeed, as with all of these examples exceptions will apply and, it's a good idea to check the log before taking any such action. I believe your example also depends on exactly how many commits you've made that need to be moved. In any case, it depends on me remembering exactly what `~` signifies.

jimbokun 6 months ago

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

  • dalia-reds 6 months ago

    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

  • maleldil 6 months ago

    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_.

  • stouset 6 months ago

    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.

lilyball 6 months ago

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.

  • zahlman 6 months ago

    > 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?

    • lilyball 6 months ago

      "move a branch" means changing the commit the branch points to. `git push . $sha:$branch` will update $branch to point to $sha (you'll probably want to force this, unless you're just fast-forwarding the branch).

assbuttbuttass 6 months ago

> 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~

rav 6 months ago

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.