Comment by nasretdinov

12 days ago

Nice work! On a complete tangent, Git is the only SCM known to me that supports recursive merge strategy [1] (instead of the regular 3-way merge), which essentially always remembers resolved conflicts without you needing to do anything. This is a very underrated feature of Git and somehow people still manage to choose rebase over it. If you ever get to implementing merges, please make sure you have a mechanism for remembering the conflict resolution history :).

[1] https://stackoverflow.com/questions/55998614/merge-made-by-r...

I remember in a previous job having to enable git rerere, otherwise it wouldn't remember previously resolved conflicts.

https://git-scm.com/book/en/v2/Git-Tools-Rerere

  • I believe rerere is a local cache, so you'd still have to resolve the conflicts again on another machine. The recursive merge doesn't have this issue — the conflict resolution inside the merge commits is effectively remembered (although due to how Git operates it actually never even considers it a conflict to be remembered — just a snapshot of the closest state to the merged branches)

    • Are people repeatedly handling merge conflicts on multiple machines?

      If there was a better way to handle "I needed to merge in the middle of my PR work" without introducing reverse merged permanently in the history I wouldn't mind merge commits.

      But tools will sometimes skip over others work if you `git pull` a change into your local repo due to getting confused which leg of the merge to follow.

      2 replies →

  • The recursive merge is about merging branches that already have merges in them, while rerere is about repeating the same merge several times.

  • Rerere is dangerous and counterproductive - it tries to give rebase the same functionality that merge has, but since rebase is fundamentally wrong it only stacks the wrongness.

On recursive merging, by the author of mercurial

https://www.mercurial-scm.org/pipermail/mercurial/2012-Janua...

  • Yeah, the point about high complexity of the recursive merge is valid, and that's what I would expect from the Mercurial devs too. I personally find it a bit unfortunate that Git ended up winning tbh, but since it did, I think it makes sense to at least cherish what it has out of the box :)

    • In some ways, the legacy of mercurial lives through jujutsu/jj and offers some sanity and familiarity on top of git's UI. But with that said, mercurial is far from dead, major "under-the-hood" works are going strong (including a rewrite in rust), the hosting situation is getting good with heptapod (a branch of gitlab with native mercurial support).

      I really don't see any downside to recommending mercurial in 2026. Git isn't just inferior as a VCS in the subjective sense of "oh… I don't like this or that inconsistent aspect of its UI", but in very practical and meaningful ways (on technical merit) that are increasingly forgotten about the more it solidifies as a monopoly:

      - still no support for branches (in the traditional sense, as a commit-level marker, to delineate series of related commits) means that a branchy-DAG is border-line useless, and tools like bisect can't use the info to take you at the series boundaries

      - still no support for phasing (to mark which commits have been exchanged or are local-only and safe to edit)

      - still no support for evolve (to record history rewrites in a side-storage, making concurrent/distributed history rewrites safe and mostly automatic)

Much more principled (and hence less of a foot-gun) way of handling conflicts is making them first class objects in the repository, like https://pijul.org does.

  • Jujutsu too[0]:

    > Jujutsu keeps track of conflicts as first-class objects in its model; they are first-class in the same way commits are, while alternatives like Git simply think of conflicts as textual diffs. While not as rigorous as systems like Darcs (which is based on a formalized theory of patches, as opposed to snapshots), the effect is that many forms of conflict resolution can be performed and propagated automatically.

    [0] https://github.com/jj-vcs/jj

  • I feel like people making new VCSes should just re-use GIT storage/network layer and innovate on top of that. Git storage is flexible enough for that, and that way you can just.... use it on existing repos with very easy migration path for both workflows (CI/CD never need to care about what frontend you use) and users

    • Git storage is just a merkle tree. It's a technology that's been around forever and was simultaneously chosen by more than one vcs technology around the same time. It's incredibly effective so it makes sense that it would get used.

    • The bottleneck with git is actually the on-the-fly packfile generation. The server has to burn CPU calculating deltas for every clone. For a distributed system it seems much better to use a simple content-addressable store where you just serve static blobs.

    • It is my understanding that under the hood, the repository has quite a bit of state that can get mangled. That is why naively syncing a git repo with say Dropbox is not a surefire operation.

  • It's very cool though I imagine it's doa due to lack of git compatibility...

    • Lack of current-SCM incumbent compatibility can be an advantage. Like Linus decided to explicitly do the reverse of every SVN decision when designing git. He even reversed CLI usability!

      2 replies →

New to me was discovering within the last month that git-merge doesn't have a merge strategy of "null": don't try to resolve any merge conflicts, because I've already taken care of them; just know that this is a merge between the current branch and the one specified on the command-line, so be a dutiful little tool and just add it to your records. Don't try to "help". Don't fuck with the index or the worktree. Just record that this is happening. That's it. Nothing else.

  • Doesn't `git merge -s ours` do this?

        This resolves any number of heads, but the resulting tree of the merge is always
        that of the current branch head, effectively ignoring all changes from all other
        branches. It is meant to be used to supersede old development history of side
        branches. Note that this is different from the -Xours option to the ort merge strategy.

  • What does that even mean? There already is reset hard.

    • The name "null" is confusing; you have to pick something. However, I think what is desired here is the "theirs" strategy, i.e. to replace the current branch's tree entirely with the incoming branch's tree. The end result would be similar to a hard reset onto the incoming branch, except that it would also create a merge commit. Unfortunately, the "theirs" strategy does not exist, even though the "ours" strategy does exist, apparently to avoid confusion with the "theirs" option [1], but it is possible to emulate it with a sequence of commands [2].

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

      [2]: https://stackoverflow.com/a/4969679/814422

    • What do you mean, "What does it mean?" It means what I wrote.

      > There already is reset hard.

      That's not... remotely relevant? What does that have to do with merging? We're talking about merging.

      11 replies →

I hate git squash, it only goes one direction and personally I dont give a crap if it took you 100 commits to do one thing, at least now we can see what you may have tried so we dont repeat your mistakes. With git squash it all turns into, this is what they last did that mattered, and btw we cant merge it backwards without it being weird, you have to check out an entirely new branch. I like to continue adding changes to branches I have already merged. Not every PR is the full solution, but a piece of the puzzle. No one can tell me that they only need 1 PR per task because they never have a bug, ever.

Give me normal boring git merges over git squash merges.

That's something new to me (using git for 10 years, always rebased)

  • I'm even more lazy. I almost always clone from scratch after merging or after not touching the project for some time. So easy and silly :)

    I always forget all the flags and I work with literally just: clone, branch, checkout, push.

    (Each feature is a fresh branch tho)

as far as I understand the problem (sorry, the SO isn't the clearest around), Fossil should support this operation. It does one better, since it even tracks exactly where merges come from. In Git, you have a merge commit that shows up with more than one parent, but Fossil will show you where it branched off too.

Take out the last "/timeline" component of the URL to clone via Fossil: https://chiselapp.com/user/chungy/repository/test/timeline

See also, the upstream documentation on branches and merging: https://fossil-scm.org/home/doc/trunk/www/branching.wiki