Live data from Hacker News

Git Branches: Intuition and Reality

jvns.ca

121–130 of 281 posts

Re: Git Branches: Intuition and Reality

#121

While the explanation is right in some sense, it misses a few points. Branches are pointers to a commit and that pointer is refreshed when a new commit is created. One could say they are a wandering tag (without explaining a tag for now). The actual chain of commits that represent what we see as branch comes from the commits themselves. Those commits point back to their parent commit. And then one can see why no bran…

For years I was deeply annoyed by the terrible name “branch” for something that acts more like a bookmark (or “wandering tag” indeed!). And then I learned that git branches are branches in exactly the same way that the first element of a linked list in C “is” the linked list. Git was made by C people and they’re used to referring to entire data structures by way of some root element. I mean that doesn’t make me disli…

When the entire structure of commits is called a tree, I find the name "branch" fitting. The branch is identified by its head commit, so the path from head to root is uniquely defined and that's the branch. (Disregarding merges for now.)

Re: Git Branches: Intuition and Reality

#122

tl;dr Please ignore, just me working through a Python+pygit2 problem. I solved it in a grandchild comment. I had so much trouble trying to map my intuited/mental model of git onto pygit2 that I gave up and just used the git module. I wanted to automate a fairly simple thing in Python as opposed to bash+commands. My reasoning being that I wanted to do it "right" and be a Big Programmer Real Boy(tm). I just wanted to c…

This isn't asking someone else to make this work, it's more of a caution to convince folks like me to just use "import git" rather than pygit2: So something like this was what I expected to work, but leaves the repo in detached head state: import pygit2 def checkout_branch(path, branch_name): repo = pygit2.Repository(path) branch_ref = repo.lookup_reference(f"refs/remotes/origin/{branch_name}") print(f"{branch_ref.na…

Ha, and of course just messing around gets me something that actually works.

There always seems to be just one more stackoverflow thread to read that has the real answer: https://stackoverflow.com/questions/68435607/how-to-clone-ma... (found via Kagi which I wasn't using before, and the search "pygit2 detached head")

    def checkout_branch(path, branch_name):
        repo = pygit2.Repository(path)

        main_branch = repo.lookup_branch("main")
        print(f"Main branch upstream: {main_branch.upstream_name}")

        if branch_name not in repo.branches.local:
            print(f"Branch {branch_name} not found in local branches")
            remote_branch = "origin/" + branch_name
            if remote_branch not in repo.branches.remote:
                raise SystemExit(f"Branch {remote_branch} not found in remote branches")
            (commit, remote_ref) = repo.resolve_refish(remote_branch)
            repo.create_reference("refs/heads/" + branch_name, commit.hex)

        branch = repo.lookup_branch(branch_name)
        print(f"Branch name: {branch.name}")

        repo.checkout(branch)
        print(f"Is branch head? {branch.is_head()}")

        (commit, branch_remote) = repo.resolve_refish("origin/" + branch_name)
        print(f"Remote branch: {branch_remote.name}")
        branch.upstream = branch_remote
With git reflog telling me the right thing:

    d44aedc (HEAD -> test, origin/test) HEAD@{0}: checkout: moving from main to test
And git push has the remote branch already set.

I wish there was a pair programmer AI that you had to explain stuff to. That would enable the "by explaining it, I solved it" phenomenon.

Re: Git Branches: Intuition and Reality

#123
post #77

Earlier quoted context omitted.

> moving files isn't stored in git is there an intuitive and enlightening explanation as to why it is this way?

For the historical rationale see here: https://gist.github.com/borekb/3a548596ffd27ad6d948854751756... In short, Linus stance is that file renaming doesn’t matter, only the contents of files matter, and the moving of contents between files. Moved/renamed files then fall out as a special case of moving content. Personally, I think this is a case of the better being the enemy of the good, and his “clearly superior algo…

I don't think it's about having a stance, it's about git's architecture. From the commit graph point of view, there's no such things as moving anything at all, neither files nor content. Commits represent a whole new state of the repository, not a diff from the previous state. The only way a commit is linked to the previous state is via parent pointer, it can otherwise be completely unrelated (and you can simply change the parent pointer without changing anything else in the commit). Any diffs are calculated at runtime. The issue with renames is just a consequence of assuming such data model - you could try to plaster it over with some metadata, but ultimately you would still be fighting against the model rather than working with it.

Many people develop a bad mental model with commits as diffs, because that's what the UI makes them think commits are. It can work for a while, but inevitably leads to confusion later on.

Re: Git Branches: Intuition and Reality

#124

Earlier quoted context omitted.

Possibly `git branch NEWBRANCHNAME` instead of `git checkout -b NEWBRANCHNAME`. When I need to show git to someone in order for them to contribute to something, I give them only these incantations --- and instructions to ask me if weird git things happen.

It's `git switch [-c]` nowadays.

I've given up trying to configure git push the branch I'm on so I type this little dance each time :

    git switch -c foo
    git push 
    > did you mean git push --args-with-branch-name?
    sigh, copy, paste, enter

Re: Git Branches: Intuition and Reality

#125

I don't use git at work, but in my private hobby projects my friends usually get mad when they watch me juggle changes and branch pointers with git reset --hard and git stash... How do you undo a merge that you didn't mean to do/did wrongly? git reset --hard Have some cosmetic fixups on your local branch that really should go into main (or a separate branch) first before merging a bigger feature? git stash git checko…

> I'm working much more directly with the underlying abstraction

Your strategy of seeing things as they are is a useful general purpose life skill.

Re: Git Branches: Intuition and Reality

#127
I think that one way to "easily" understand the syntax of git is to remember that when you perform a command you "always" modify the current branch

for example: git merge my-branch will merge my-branch into the current one

while git rebase my-branch will rebase current one on top of my-branch

Re: Git Branches: Intuition and Reality

#128

Earlier quoted context omitted.

For years I was deeply annoyed by the terrible name “branch” for something that acts more like a bookmark (or “wandering tag” indeed!). And then I learned that git branches are branches in exactly the same way that the first element of a linked list in C “is” the linked list. Git was made by C people and they’re used to referring to entire data structures by way of some root element. I mean that doesn’t make me disli…

When the entire structure of commits is called a tree, I find the name "branch" fitting. The branch is identified by its head commit, so the path from head to root is uniquely defined and that's the branch. (Disregarding merges for now.)

> Disregarding merges for now.

Without disregarding them, it's not a tree, but a DAG.

Re: Git Branches: Intuition and Reality

#129
post #77

Earlier quoted context omitted.

For the historical rationale see here: https://gist.github.com/borekb/3a548596ffd27ad6d948854751756... In short, Linus stance is that file renaming doesn’t matter, only the contents of files matter, and the moving of contents between files. Moved/renamed files then fall out as a special case of moving content. Personally, I think this is a case of the better being the enemy of the good, and his “clearly superior algo…

I don't think it's about having a stance, it's about git's architecture. From the commit graph point of view, there's no such things as moving anything at all, neither files nor content. Commits represent a whole new state of the repository, not a diff from the previous state. The only way a commit is linked to the previous state is via parent pointer, it can otherwise be completely unrelated (and you can simply chan…

As you say, commits link to their parent(s), and those links effectively represent the edges of the commit graph. It makes perfectly sense to record moves on those edges. That’s how other VCSs do it. There is no conflict with the commit model.

Viewing the commit graph in terms of nodes (commits) or edges (diffs) is equivalent, these are dual views you can easily convert between. The internal representation is independent from that. Some VCSs use a mix of diffs and full revisions internally. Even Git uses delta compression when packing objects.

Re: Git Branches: Intuition and Reality

#130

While the explanation is right in some sense, it misses a few points. Branches are pointers to a commit and that pointer is refreshed when a new commit is created. One could say they are a wandering tag (without explaining a tag for now). The actual chain of commits that represent what we see as branch comes from the commits themselves. Those commits point back to their parent commit. And then one can see why no bran…

For years I was deeply annoyed by the terrible name “branch” for something that acts more like a bookmark (or “wandering tag” indeed!). And then I learned that git branches are branches in exactly the same way that the first element of a linked list in C “is” the linked list. Git was made by C people and they’re used to referring to entire data structures by way of some root element. I mean that doesn’t make me disli…

> Git was made by C people and they’re used to referring to entire data structures by way of some root element.

FWIW this is actually backwards. The word "branch" was already in common use (to refer to the same basic idea) in SCM systems going back decades, and in almost all of those a "branch" was indeed a first class object with its own data that acted as a "container" for commits, both semantically and physically.

The fact that a "branch" is just a pointer is in fact a git innovation on top of the former idea.

Post reply on HN