Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I dunno - for me git doesn't really have branches. What it calls "branches" could more accurately be called version/commit pointers.

A "branch" traditionally in SCM was a sequence of versions/commits. In those SCMs, the "main" (or master) branch was an identifiable sequence of versions/commits.

There isn't a "main" branch in git, there's a current "main" version/commit. If your history is fully linear, a pointer to the "main" commit allows you, via parent following, to discern a "main" branch. But if you've been using merge, for example, there isn't a unique path from the current main commit to the root.



Understanding refs is key to understanding Git. It’s not really difficult and a lot of mystifying cargo cult stuff suddenly becomes intuitive once you realizing you’re pretty much just manipulating pointers into a directed acyclic graph where each node has between 0 and 2 parents.


0 and >0 parents technically, but in practice usually between 0 and 2. Merge commits (commits with >1 parent) can have more than 2 parents.


A proper branch in Git is just a name for a commit with the rule that when you commit to a branch you move the name to point to the new commit, and similar rules on push.


This does sometimes cause problems when trying to understand history. Would love if git recorded the current branch name (if any) somewhere in the commit


I have a git commit hook that automatically adds a git trailer containing the jira ticket/issue number to the commit message.

It gets this from the branch name which by my convention always contains the ticket ref.

It would be trivial to modify this to use the full branch name instead.


And now I'm on my computer, here is the modified version. Put this in:

.git/hooks/prepare-commit-msg

    #!/usr/bin/env bash

    BRANCH=$(git symbolic-ref --short HEAD 2> /dev/null)

    # fail gracefully if we don't have a branch
    if [ -z "$BRANCH" ]; then
        exit 0
    fi

    # add a git trailer with the branch
    git interpret-trailers --in-place --where before --if-exists addIfDifferent --trailer Branch="$BRANCH" "$1"
And if you add the following alias to your ~/.gitconfig you'll be able to see the branch names in the logs by typing git lb (for log branch):

    [alias]
        lb = log --color --graph --pretty=format:'%Cred%h%Creset %C(bold)%(trailers:key=Branch,valueonly,separator=%x2c)%Creset - %s %Cgreen(%cr)%C(bold blue) <%an>%C(nobold yellow)%d%Creset' --abbrev-commit


I'm thinking of writing a commit hook to make sure that every commit has the following metadata in trailers:

  - original parent commit
    
    Between this and the synopsis it
    should then be possible to find all
    evolutions of this commit.
  
  - original upstream URI and branch (if
    known) or upstream URI and branch
    targeted for integration (prompting
    if necessary)
  
  - commit type and optional tracker ID
When a commit has these metadata already, leave it.


Jujutsu is a great interface to the git data structures, and it makes all of this obviously true.


Meh getting too reductionist doesn’t necessarily help anything. Branching is a concept, not an implementation detail. There is a “main” branch in git because that’s how people talk about it, and what they usually mean when they refer to “main” or another branch; people are not usually referring to a singular tip-of-the-branch commit, but the whole branch and it’s history. A branch is different from a tag or a commit because the branch is expected to move frequently (point to different commits) and tags/commits aren’t. The implementation isn’t different, but the usage and meaning is. Branches in other SCMs are really no different conceptually. Perforce doesn’t identify any unique path from the tip of the branch to the root it came from, you can merge both directions. Same is true in other SCMs.


It's more than an implementation detail.

Without changing the DAG, I can arbitrarily move "main" to point at any commit I like in git. This isn't some esoteric action - this is done constantly as part of normal git workflow.

While what people understand as a branch in traditional SCM discussions can only grow by the addition of new versions. Commits/versions belong to a specific and unique branch and once added to the version tree, cannot "move" branches.

While in git, a commit can belong to any number of (git) "branches" which makes presenting a true branch based history (if the DAG contains merges) impossible. Which is why we all end up using a workflow based on rebase instead of one based branch-and-merge.

I've observed how much confusion using the name "branch" for what is a version pointer in git causes to those starting with git. The easiest way I've found to help people is to tell them forget about the word "branch" and think in terms of the DAG and pointers to elements in the DAG.


Sure git is a bit different than P4 or Svn or whatever; they all have unique implementation details. I don’t disagree that git confusion sometimes exists nor that you’ve seen it, but it might make your point clearer to give a specific example, one that knowing a branch is a pointer fixes. I’m not sure if it’s useful to think of commits as immovable; they can be rebased and cherry picked, the “version” (SHA) is another implementation detail. Moving the content of commits around is common standard practice with git. That’s a different type of move than a branch move, not what I was referring to above, but I guess I’m arguing that branching is more about workflows, conventions, and conceptual understanding than how it works technically. You’re right that it’s sometimes useful to know that a git branch is a pointer and super lightweight, but that doesn’t help you differentiate branches and tags, for example. Git has a bunch of features that are ‘just’ a pointer. Knowing that is good for advanced workflows, but not really necessary for basic version control.


For a specific example of where choosing a name ("branch") which obfuscates the fact that you are actually manipulating a pointer, maybe consider what it means to delete a branch? Or create one for that matter.

Same in general programming, being clear on the difference between a reference and referee is vital to proper conceptualize what the operations actually do.

But I'm not sure even that's convincing as after using git for a while it becomes completely instinctual which is why it may not seem important to clearly identify a pointer to a commit and an actual sequence of commits related by the parent/child relationship.


I’m not following. Creating and deleting branches is easy in git without knowing it’s a pointer. What exactly is confusing about it, and how does knowing it’s a pointer help? Creating and deleting branches in Perforce is also easy, and they’re not pointers.


I never got deep enough into the legacy VCSs like Subversion to do things like branching. Or even the peer Mercurial. So I have no other VCS concept of so-called branching.

I don’t get what is the problem with Git’s concept of branching. It is mutable, yes, and commits aren’t somehow marked with the information about what branch it was made on. I can understand that some might expect it to. But why is Git’s “branch” so strange that it doesn’t deserve to call itself that? All technical names are in the end synthetic.

> Without changing the DAG, I can arbitrarily move "main" to point at any commit I like in git. This isn't some esoteric action - this is done constantly as part of normal git workflow.

You can reset to whatever in general. But this is typically not done for the main branch. People who pull it will get an error if you rewrite it completely. Only fast-forward updates are the normal ones (when you go from an ancestor commit to a descendant).

> While in git, a commit can belong to any number of (git) "branches" which makes presenting a true branch based history (if the DAG contains merges) impossible.

A true branch history? When does this matter? You can see that main has commits and things are merged into it. That’s typical. Of course people can make a mess of that (too easy really). But usually you have a few immortal histories (we can call them histories if you want) and things eventually end up in them.

> Which is why we all end up using a workflow based on rebase instead of one based branch-and-merge.

Plenty of people use only merges. Some hate rebase. Even though they shouldn’t.

> I've observed how much confusion using the name "branch" for what is a version pointer in git causes to those starting with git. The easiest way I've found to help people is to tell them forget about the word "branch" and think in terms of the DAG and pointers to elements in the DAG.

In other words you explain to them concretely what it is. Yes? “Branch” is just a name.

Is a “bookmark” in Mercurial any more obvious?


> I don’t get what is the problem with Git’s concept of branching. It is mutable, yes,

Exactly like a branch on a tree: It's a living thing, that keeps on growing. The word "branch" refers to the whole thing, regardless of where the tip of it has grown to. Utterly intuitive.

And just like branches on trees grow at their tips, the "branch" pointer in git, where you add stuff, is the latest commit. Of course; where else should it grow?

> and commits aren’t somehow marked with the information about what branch it was made on.

Again, exactly like a branch on a tree: Pluck a leaf from it, and there is no sign on it to say which branch it came from. But you can look at any twig or leaf and see what it's attached to, all the way to the trunk, and where it forks off from the trunk is where the branch begins.

(Somewhere along the length of it, you could score the branch name into the bark of the tree... Nah, analogy getting a bit too literal there. But still, totally intuitive.)


Opinionated branching is super obnoxious. It's much better to let you see the truth with a thin traditional branching veneer, and you can choose to enforce "fast-forwards"ness or not.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: