Hacker News new | ask | show | jobs
by eru 1058 days ago
> I also wonder, how much better git could be if it was based on DAGs not trees [...]

Git generally supports DAGs.

> [...] corollarily I‘d like to rebase subtrees (sub-DAGs) instead of single branches.

Rebasing is something you do to the commit graph, which is a DAG. Branches only come in incidentally. Branches in git are really just mutable pointer to immutable commits.

What you are describing is probably some useful workflow, I guess?

2 comments

I seem to understand GP wants to move a whole DAG potentially having multiple leaves, not just a DAG ending at a single leaf.

IOW

   git rebase --onto shaX shaY shaZ
ends at shaZ, git walks backwards from it until the commit whose parent is shaY to produce the list of commits to cherry-pick onto shaX

So presumably this would be useful:

    git rebase --onto shaX shaY [shaZ1 shaZ2 shaZ3 ...]
with shaZn being optional and consisting of all leaves down from shaY

This is achievable with git but it's not just doing n rebases like so:

    git rebase --onto shaX shaY shaZ1
    git rebase --onto shaX shaY shaZ2
    git rebase --onto shaX shaY shaZ3
    ...
because each rebase would produce different commits for parts that are common to shaZ n1 and shaZn2 ancestry, so one would have to first find all the branching points and do partial rebases onto the rebased parent commits in order.

It definitely can be done (manually or automatically) but is not as trivial as one might think.

OK, that makes sense.

I think if you wanted to do this, it would probably be easiest to produce an artificial leaf that points to all the leaves you want to rebase.

Yes, and the original commenter's point is that git does not support this well, at the data model layer even. Because commits have exactly one parent commit, which is immutable, and because rebasing creates a new commit, rebasing an entire subtree with N nodes under it requires N operations, rather than just 1.

Personally I think it's a pretty small price to pay for the advantage of the single-immutable-parent model, but I do think it's surprising that there aren't better tools for this workflow. I do this all the time, but manually and painstakingly.

> Because commits have exactly one parent commit

This is not true, merge commits are commits with > 1 parents (octopus merges are merely commits with > 2 parents)

But indeed since the parents commits are part of the computation of a commit's sha then by design changing the parents means changing the commit's sha, which is a very nice property to have.

That said, the model has what we said as a design consequence, but it's perfectly workable to have tooling that walks history and finds branching points and whatnot. Git does it all the time with existing porcelain commands, and I've done it in different contexts (e.g to produce diffs between tip and branching point, analysing which files have changed, and take action pertaining only to these file changes and their dependents) but the gist of it is the same.

It's merely a matter of such porcelain commands not being implemented and not being part of upstream git, so everyone either does it manually or invents their own tooling when they're sick of the pain point.

I can't edit anymore, but hopefully people can read my comment with s/commits have exactly one parent/non-merge commits have exactly one parent/ :)

But this did make me realize that the problem with this workflow isn't the "one parent" part at all, it's really just the "immutable parent" part.

But to the main thrust of your comment: Yes, this "porcelain" is what I meant by my surprise that there aren't better tools for this. I think the reason there aren't is that it is really hard to do this well with porcelain, because of requiring a bundle of modifications to the commit tree, where there is no good way to make the whole bundle atomic. So it works fine (and I have a script for it) in the happy path, but it becomes a bit of a nightmare if something doesn't apply quite right.

And that's what I mean by this being unsupported at the data model level, that there is no way to do atomic operations on subtrees as a whole.

> [...] where there is no good way to make the whole bundle atomic.

You could just do all the commit manipulations you need to do, and only update the branches at the very end?

Updating the individual branch 'pointers' ain't atomic, but if there's nothing else going on in the repo at the time, it can't really fail; if you've already created the new commits.

You're right that the tooling doesn't support it well but commits have more than one parent all the time, they're just called merge commits.

They can actually have as many parent branches as you please but conflicts get harder to deal with the more parents you have.[1]

[1]: https://www.freblogg.com/git-octopus-merge

I realized the multiple parents thing was a total red herring. It really is just the immutability.

What I was thinking of was having multiple parents over time rather than at the same time like a merge commit. But that would just be one (not immutable) parent but with a history of what that parent was over time, not "multiple parents".

Not only it's useful, it's as easy to do in git as typing `git rebase -r`. Recently it even gained support for rewriting branch pointers in the process.
In general, it's almost always better to use long options. So that would be `git rebase --rebase-merges` in this case.

Almost always means: it's better eg when communicating with other humans, whether that's on a forum like HN or in code or scripts. Long options are easier for humans to understand and to 'google'. They also provide some redundancy against typos.

The sole exception, where short options can be useful, is when you are actually using a command line interactively. Use short options to your heart's content there.

Do you mean it can rewrite branch pointers that pointed to intermediate commits? How?
--update-refs