Live data from Hacker News

Git Branches: Intuition and Reality

jvns.ca

51–60 of 281 posts

Re: Git Branches: Intuition and Reality

#51
post #8

A lot of things in git are just pointers to commits, and then the git implementation handles them under the covers in some way that usually makes sense but not always. One example that also bites people: moving files isn't stored in git - if you move files (even with `git mv`) and create a new commit, the moves aren't stored, but this is reconstructed later by the client based on similarity, which comes from the diff…

> moving files isn't stored in git

is there an intuitive and enlightening explanation as to why it is this way?

Re: Git Branches: Intuition and Reality

#54

Earlier quoted context omitted.

Yup. “Storing moves” is the kind of thing that might sound intuitively obvious but then gets gnarly and non-obvious when you think about it for five minutes. And so something that might be “obvious” to do then turns out to be so non-obvious—how to catch all file moves (intent) outside of simple identitical content cases, and how do you represent them internally?—that you realize that just using snapshots is really th…

BitKeeper already did it.

I think this is the one thing I feel BitKeeper does better than Git. Git can get confused about where a file came from, for moves but especially for copies, and so the version history ends, even if you ask it to try and follow along. BitKeeper, on the other hand, keeps the moves and copies as part of the history, so you can always trace it through to the origin of the file, no matter how circuitous.

Re: Git Branches: Intuition and Reality

#55
post #3

I cannot be the only one that gets away with only knowing: git pull git merge x git checkout [-b] foo git commit git push

that's my list too, pretty much (+ the obvious `git add` and `git status` others have mentioned)

I throw in a `git reflog` once in a while, and the other day I patted myself on the back for my first use of `git tag -a mytag -m "This is my first tag!"` followed by `git push origin mytag`. I felt like god

now as much as I hate Xcode, its UI for looking at all my current changes and staging them by blocks of line for commit is like a superpower. as a solo developer working on brand new code, not all of my lines of thinking follow very atomic git commits, so it's nice to separate, say, some refactoring code from some actually new functionality when committing changes

Re: Git Branches: Intuition and Reality

#57
post #8

A lot of things in git are just pointers to commits, and then the git implementation handles them under the covers in some way that usually makes sense but not always. One example that also bites people: moving files isn't stored in git - if you move files (even with `git mv`) and create a new commit, the moves aren't stored, but this is reconstructed later by the client based on similarity, which comes from the diff…

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

Git stores snapshots and that’s it. The whole tree, not per-file.

As to why Linus doesn’t like storing file moves: https://public-inbox.org/git/Pine.LNX.4.58.0504150753440.721...

Re: Git Branches: Intuition and Reality

#58
post #18

Git doesn't have the concept of "main is special", but at least tools like Gitlab have protected branches to stop you screwing up too much. Some concept of "parent" and "child" branches would actually be pretty interesting. You do have to support multiple "parent" branches though for long term support branches.

> Git doesn't have the concept of "main is special"

Technically, there is special handling for both "master" and "main" in Git in fairly obvious, but I'd argue in a not very important way. When you merge two regular branches, the commit message is `Merge branch 'source' into destination`. But not if destination is `master` or `main` – the `into ...` part is omitted for those merge commits.

But this is just for backward compatibility. Git is very conservative in changing such user facing behavior as generated merge commit messages. To get Git to treat `master` and `main` truly without special handling, set empty value to config option `merge.suppressDest` [1]:

    $ git config merge.suppressDest ""
`master` is also used as the default name for the default branch in newly created repositories. See option `--initial-branch` of `git init` and config variable `init.defaultBranch` [2] to override. Git for Windows, for example, allows setting the config option in its installer.

Source code:

For merge commit formatting: https://github.com/git/git/blob/2108fe4a1976f95821e13503fd33...

For default branch naming: https://github.com/git/git/blob/91e2ab1587d8ee18e3d2978f2b7b...

Git for Windows installer suggesting setting `init.defaultBranch`:

- https://github.com/git-for-windows/build-extra/blob/586c46ec...

- https://github.com/git-for-windows/build-extra/blob/586c46ec...

Footnotes:

[1] https://git-scm.com/docs/git-merge#Documentation/git-merge.t...

[2] https://git-scm.com/docs/git-init#Documentation/git-init.txt...

Re: Git Branches: Intuition and Reality

#59

I have found that git makes a lot more sense if you reverse the mental model of lineage. People think about a lineage going forward . But a more useful way to think is in terms of backward pointers. A commit points to it's parent(s). Since a branch is just a commit ID, you can follow the parent links backwards to find the whole history of that branch. So a "branch point" is just where two chains of parent links conve…

The issue is that if you consider a branch to be what is really the history of the branch tip, then a branch is not just the part starting from the last join with another branch. Instead it is some directed path through the commit DAG, a path that in general can’t be reconstructed from the information Git keeps.

If, for example, you have a structure like

        |
        o
       / \
      o   o
   A  |   |  B
      o   o
       \ /        
        o
       / \
      o   o
   C  |   |  D
      o   o
       \ /
        o
        |
then conceptually the path CA might be one branch and DB the other branch (or alternatively, CB and DA). But this is not something that is represented in Git’s model.

Re: Git Branches: Intuition and Reality

#60
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 checkout main
    git stash apply
By thinking about branches as pointers, the commit graph existing independently, and stashes just being temporary commits, I feel I'm working much more directly with the underlying abstraction. Yes, git has commands for specific combinations of actions, but for an occasional user it's harder to remember every such command and which arguments and flags to pass in which order. It's either "look through documentation until you find graph diagrams illustrating what will happen for this order of arguments and flags" or "use the primitives 'move branch pointer', 'commit to branch', 'hold these changes for a second' for obtaining the commit tree you actually want. Knowing that the reflog exists also makes this insane-sounding working mode pretty non-scary. And yes, some operations (e.g. cherry-pick) you just need to do the "real" way.

(My git stash obsession is most likely just damage from years of using Perforce, which doesn't have a modified/staged distinction. The only way to commit only part of a changed file is via the equivalent of stash -> [restore half the file] -> commit -> stash pop.)

Prepares to be crucified...

Post reply on HN