8 ms·
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 crea
by riperoni 3y ago
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 branch has any special meaning: It is a chain of related commits with a named entrypoint. Once you delete a branch (i.e. the named wandering pointer to a commit), you cannot identify a branch as such anymore. It is just a chain of related commits without a named label now. And nothing besides the name distinguished the branch from other commit chains before.
The master/dev/release branches are then a convention to keep an updated commit pointer on the chain of commits containing changes of interest.
- jansan 3y agoThis was the most useful piece of information that I have ever read about Git. But what happens if you merge branch A into beanch B? A and B will both contain the commits of A, but in B there may be commits of B between the commits that were merged. Do the same commits of A then have different parents depending on which branch they are on?
- paulddraper 3y agoMerging branch A into branch B does two things: 1. Create a new merge commit with two parents: the commit pointed to by A and the commit pointed to by B. 2. Set branch B to point at the new merge commit. This is a non-linear history; when comparing some commits there isn't a "before" or "after."
- hackernews1134 3y agoIf I checkout the merge commit and then do a ‘git log -n 5’, which parent pointer is followed to show the previous commit logs? A or B or Both / all if it is more than a 2-way merge?
- baronswindle 3y agoAs I recall, it shows commits from both parents in order of the committed date.
- paulddraper 3y agoBy default it's by commit date. Other ordering is possible, e.g. --topo-order
- emilecantin 3y agoTo complete the answer, the only difference if you merge branch B into branch A is that A is advanced to the new commit instead.
- seba_dos1 3y ago> Do the same commits of A then have different parents depending on which branch they are on? Absolutely not. Commits are immutable (representing whole repo state, not a diff), and branches are just (mutable) pointers to them. As the sibling already noted, a merge commit is just a regular commit. It simply points to multiple parents, "merging" them. Aside of the whole machinery to resolve conflicts etc. that's pretty much all there is to it. When your graph topology allows it, you can also merge branches without generating a new commit (so called "fast forward" merges) - such a merge does nothing but rewrites the branch pointer. You can also create merge commits that point to more parents than two ("octopus" merges). Reconciling the commits' content can get quite complicated in such cases, but from the repo graph perspective it's nothing special.
- xorcist 3y ago> Commits are immutable (representing whole repo state, not a diff) To make things more clear: Repo state here is the contents of all files, and some metadata including a pointer to the previous commit. So a commit hash uniquely identifies not only a set of files but the unique history leading up to it! That's why we some people like to call git the original block chain (there's no proof of work involved of course so it can never be used for payments or anything like that, but the merkle tree bit is similar enough).
- deleted 3y ago[deleted]
- tharkun__ 3y agoI keep repeating this every time someone talks about git and finds something weird or doesn't get branches, so I'm really glad your parent mentioned it as well and I know there's someone else out there that "gets" that: In git it's all just labels/pointers It's not useful at all to think about branches as the user sees them as "things" of their own. Branches don't "have" anything. Branches in that sense are just convenient labels. Of course actual "branches" in the commit tree exist whether you label them or not. Until `git` does a garbage collection and gets rid of anything that doesn't have a pointer ultimately leading to it - something that a human would understand aka branch/tag. And that's why we call these labels "branches" as well but it's actually one word for two things here. The actual tree branch and the label that's called branch. And a branch and a tag are basically the same exact thing underneath, just a file in the `.git` directory somewhere that contains a commit hash. All the meaning and differentiation of branch or tag is just in the human brain and how we and our tools treat them. Such as if you look at a particular commit in your tool of choice, it will tell you which branch it's part of. To create a branch you can literally just create a thousand randomly named files in the right part of the `.git` directory containing the same commit hash and suddenly this commit "is on all those branches". That's what git does and why creating a branch in git is so super fast.
- seba_dos1 3y agoTo make things more complicated, the word "tag" is also overloaded. It can either be just a reference (in git lingo, a "ref") to a commit - just like a branch, only differing from it in how the tools treat it; but they can also be "annotated tags" which are pointing to a special tag object which contains some metadata and only then points to a specific commit (or other kind of object...) :)
- evntdrvn 3y agoYou’ll also see the first type referred to as “lightweight tags”, if that helps anyone :)
- paulddraper 3y agoYeah they should have called them tags and annotations. Or labels and annotations. Or something else.
- loeg 3y agoIn short: merge commits have multiple parent commits. So your tree tracing logic bifurcates at that point. The commits in the merged history are not altered by the merge commit; they each have a single parent commit (unless they are also merge commits).
- skrebbel 3y agoFor 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 dislike the name any less but at least now I see where they were coming from.
- neuromanser 3y agoThat's (most probably) where the "head" terminology comes from, too.
- alfredpawney 3y agoYes you are correct. It traces back to Allen Newell
- mr_mitm 3y agoWhen 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.)
- cpeterso 3y agoLeaning into the tree metaphor (and following the precedent of other version control systems), git should have used the term trunk instead of master or main.
- hnarn 3y agoWhy? That would heavily imply that master/main is somehow technically different from all other branches (since a trunk is certainly not a branch), which to my knowledge is not true.
- loeg 3y agoI think this is covered adequately (if less completely) in the "technically correct" definition section.
- jillesvangurp 3y agoA key point with git is that every clone is effectively its own set of branches; even if they have the same name. The mechanisms you use for synchronizing your local branches with some remote branches are exactly the same as the mechanisms you use between to your local named branches. Git was actually designed initially for email based workflows where there was no central remote at all. Basically, that works by exporting patches and then applying them to your local branch. The branch name isn't even part of the patch. A git patch is just a textualized form of the list of commits you created locally. You can apply them to any branch you like. As long as you and whomever applies the patch has a common ancestor commit in common, the patch may merge cleanly. It's good hygiene to ensure it does by for example rebasing/merging/squashing before you email somebody your patches. If that somebody is called Linus Torvalds, he's going to be pretty strict about things like commit messages and things not being spaghetti ball of merges, reverts, forks, etc. Your mess, your problem. Linux development still works via mailing list. And forget about emailing him directly with a patch; you need to use the mailing lists like everybody else. And he works with a network of senior contributors that screen everything that comes in and that aggregate all the patches coming from upstream. So, he only gets involved at the end of the process. Of course the rest of us use network protocols to sync our repositories. But the important distinction here is that this is a two step process. First you fetch content from remote. This is simply ensuring you have all the commit objects you need in your local git database. Any branches you have are simply text files with the commit content hash they point to as the content in .git/refs/heads. Remote branches are the same but live in your local .git/refs/remotes/<remotename>. Those branches might be named something like origin/main to make it clear that that is a local branch from the origin remote. And then you rebase/merge between your local and "remote" (i.e. also local) branch as needed. Pull is just short hand for doing both steps in one go. All merges are local. Same with rebases. Most of the conventions people project on git are kind of cultural and vary between people and companies. It's helpful to read up on the git internals in the Git book. Github is sort of an opinionated take on this that back in the day made people coming from centralized version systems like subversion feel at home by providing a central repository and allowing them to push their changes there or "share" branches there. Not necessarily a great idea for bigger projects and limiting write access is common on Github.
- 3y ago
- cesarb 3y ago> 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). A good name for these "wandering tags" would be "heads", since it's what git calls them internally (for instance, when not packed, they're stored at the "refs/heads/" path in the repository). This also exposes a distinction between a "branch" and its "head", and that distinction can be useful.
- lisper 3y agoJust to make this explicit: a branch in the chain of commits. The start of the chain is pointed to by a head. When you create a new commit on a branch, the head (of that branch) changes to point to the new commit.
- lisper 3y agoArrggh. A branch IS a chain of commits.
- galaxyLogic 3y agoSo would it be correct to say that 'heads' are the LEAVES of the commits-tree? Or are there such leaves which are not considered 'heads'?
- Lornedon 3y agoNo. There are leaves that aren't heads (for example, after you delete a branch, the old commits just lie around until someone deliberately cleans them up), and there are heads that arent' leaves (for example if you branch a new feature branch from main. The feature branch brances off of main, so main is not a leaf anymore)
- galaxyLogic 3y ago> The feature branch brances off of main, so main is not a leaf anymore) But "main" remains a "head" even though the feature branch "continues the graph" from it? So there must be a difference between "Adding a new commit" and "Branching a new commit". I think I got it now. Thanks
- zie 3y agoaka Git is the simplest crappiest implementation that can work. Then they tacked a terrible UX onto it and shipped it. Chaos ensued and we as devs have spent the last almost 20 years fighting over trying to understand the chaos, we would have been way better off staying with SVN or Mercurial or Fossil(or pretty much any other VCS), but that ship has sailed and now we are stuck in the chaos. Now nobody understands their VCS and nobody will ever understand it, as the explanations are either too detailed and miss the forest for the trees or too high level and skip over all the trees that kill you when you accidentally run into them. I hope someone somewhere manages to convince us all to move to something sane.
- tharkun__ 3y agoWith all due respect, what a load of crap! SVN, branching that takes forever instead of a simple file with a commit hash in it? Are you serious? Mercurial, when I had to use it for almost 2 years? The single thing I missed the most is the fact that "everything is just a label" (or pointer if you will). Fossil I can't comment on with certainty, as I never really used it. This is probably gonna get voted into oblivion, but most devs just don't get VCS, period (yes I'm a dev, so this is an inside perspective). Git or not. They didn't get it in CVS days, they didn't get it in SVN days and they didn't get various commercial ones either. Somehow most devs just don't grok version control trees. But so far git is the single best VCS I have got to use. Everything that I actually need day to day follows from grokking the one simple rule: All those branches and tags are just labels/pointers/sticky notes and you can move them around at will and it's fast. I do get that there are tricky situations with octopus merges and all that jazz and the Linux kernel and a few other open source projects are probably some of the trickier use cases to understand. For your run of the mill corporate situation? Keep `master`/`main`/`whateveryoucallit` history straight with one commit per feature/change by doing rebases and squashing and you will never have a single minute of misunderstanding what is going on. IFF you grok that "everything is just a label" and you've actually "created a branch" by just creating a file in `.git/refs/heads/` yourself! And the second rule is: before you try anything, just: commit! And never close that terminal window. You might need that commit hash to reattach a label to it after you "destroyed" your branch (but git has not garbage collected yet and it's all actually still there). Or someone else still has your commits and you just get them from there and reattach a label. It's really so simple.