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.
> a path that in general can’t be reconstructed from the information Git keeps.
Uh... yes it can. Commits have a list of 0 or more parents. That creates a DAG. There are literal hordes of tools out there that reliably interpret this, from visualizer tools to practical mutators like git bisect.
Maybe you're trying to say that no single commit order exists that traverses the whole tree. That's true, because branches can merge together. But it remains a completely interpretable graph nonetheless.
But that's not related to the DAG at all. The branch can be changed at any moment for any reason to point to any commit with any content.
But it's true that conventionally, a new branch tip should always have the previous branch tip as an ancestor. But not always as a direct parent, and even if so it might be a merge commit that joins two different branches. There is indeed no single spanning path through a DAG.
But trying to explain it as "git doesn't store enough information" to construct that spanning path seems confused to me. It's not about what git stores, it's just math: there is no such path in the general case, period.
The fact that the branch tip can be moved to unrelated commits is another issue with Git’s model, and a mismatch to the intuitive “a named lineage in the DAG” conception of branches. In other VCSs, that would be a new/different branch, and you could still rename branches so that the same name will later refer to a different branch, but the branch history as such (including renames) would be preserved.
> mismatch to the intuitive “a named lineage in the DAG” conception of branches
Once more, that conception may be intuitive but it is wrong. A branch is emphatically NOT a line through the DAG, it's the whole DAG. There simply is no single list of patches to apply to get from one commit to another, even if both were at some point heads of the same branch, and even if one is an ancestor of the other.
And the reason it's wrong is that branches can merge together. You can have commit A descended from both the "main" branch and the "topic_a" branch, despite the fact that those two had diverged. This isn't a bug, it's a feature. You don't have to use it if you don't want to (lots of projects require linear commit histories in their main branch), but it's part of the tool nonetheless because some projects (Linux especially) use it heavily and to great effect.
I don’t see what is wrong. Branches in that conception are paths through the DAG. One would like to annotate such paths with names, and have those names automatically apply by default when adding a commit to the end of such a path.
This has nothing to do with lists of patches. Nevertheless, for any given path, it is always possible to compute a list of patches that would match that path. Just compute the diffs between all adjacent pairs of commits on that path. What you maybe mean is that you can’t replay just a single path and have it result in the same commit hash. That, of course, is correct, you need to replay the complete prefix DAG. However, I don’t see why you think that causes issues for the branches-as-paths conception.
Yes, branches can merge together. That just means that different paths can share nodes and edges. Just like two hiking trails can partially overlap. Again, I see no problem here.
No! No, they are not. This is a mistake you are making, and I'm trying (vainly, maybe) to correct it.
If you have a branched structure like that, and dump each commit as a patch, and try to trace a "path through the DAG" by applying those patches, you will find that they don't apply after the first merge commit. There is no single list of patches, because that's not the structure of the history. The merge commit "patch" must be a different delta depending on where you came from.
The problem is that the thing that Git calls a branch does not identify a specific path. Look at OP's example again: if you conceptualize branches as paths, then CA/DB and CB/DA are distinct ways to divide that graph into branches. But in Git there is no way to represent that distinction (at least, not as branches).
You can reconstruct it manually with a combination of the parent commit order and the automatic merge commit message, if you didn't change the commit message. But yeah, that second part isn't recorded in the structure itself.
Just to go off on a tangent - that's a pretty neat diagram for a throw away comment. was that just careful spacing in the HN textbox or did you use a tool - which one ? :-)
Define "this". If you git-log from the commit on the top of that ASCII graph, you get all the drawn commits listed (unless adjusted with arguments such as `--no-merges` or `--first-parent`).
If, for example, you have a structure like
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.