martinvonz / jj

A Git-compatible VCS that is both simple and powerful

Home Page:https://martinvonz.github.io/jj/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Splitting a parent of a merge commit results in weird changes to the commit graph

ilyagr opened this issue Β· comments

Steps to Reproduce the Problem

I'm very curious if others can reproduce this.

UPDATE: I might replace the jj-bug repo shortly; the bug turned out to be simply about splitting any parent of any merge commit anyway.

  1. jj git clone --colocate https://github.com/ilyagr/jj-bug (Get a perfectly organized and orderly repo 🀣 )
  2. cd jj-bug
  3. jj branch track 'glob:*@origin'

Then:

$ jj log -r ilya-
β—‹  xpzxqz ilyagr@ 50 minutes ago conflictsides f503642
β”‚  conflicts.rs: label conflict number and sides next to conflict markers
~

β—‹  txsstw ilyagr@ 1 hour ago diffedit3 be2133e
β”‚  diff editor: bundle a new `:builtin-web` GUI diff editor
~

β—‹  nutytz ilyagr@ 1 hour ago gvimdiff 4a41932
β”‚  merge_tools.toml: add gvimdiff config
~

β—‹  ypqnrx ilyagr@ 1 hour ago verdate 3aded1d
β”‚  cli version: Put `+` before build data instead of `-`
~
$ jj split -r conflictsides CHANGELOG.md  # Accept the descriptions as they are in the editor
Rebased 11 descendant commits
First part: xpzxqz 6cc476f conflicts.rs: label conflict number and sides next to conflict markers
Second part: tvootm 40d7ded conflictsides*| conflicts.rs: label conflict number and sides next to conflict markers
New conflicts appeared in these commits:
  trmunt 09a627e local-de3*| (conflict) Use local ../diffedit3-working
To resolve the conflicts, start by updating to it:
  jj new trmunttywktn
Then use `jj resolve`, or edit the conflict markers in the file directly.
Once the conflicts are resolved, you may want inspect the result with `jj diff`.
Then run `jj squash` to move the resolution into the conflicted commit.

$ jj log -r ilya-   # Changes weirdly
β—‹  tvootm ilyagr@ 2 minutes ago conflictsides* 40d7ded
β”‚  conflicts.rs: label conflict number and sides next to conflict markers
~

What happened to all the other parents of ilya?

Specifications

  • Platform: Mac OS
  • Version: a few days old, customized: jj 0.16.0+20240405-fd26902376c1dc96

cmd_split() rebases the children onto exactly one new parent. That's probably the problem. I'm not sure why it can't do rebase_descendants().

// Rebase descendants of the commit being split.
let new_parents = if args.siblings {
vec![first_commit.clone(), second_commit.clone()]
} else {
vec![second_commit.clone()]
};
let children: Vec<Commit> = RevsetExpression::commit(commit.id().clone())
.children()
.evaluate_programmatic(tx.base_repo().as_ref())?
.iter()
.commits(tx.base_repo().store())
.try_collect()?;
let num_rebased = rebase_descendants(
&mut tx,
command.settings(),
&new_parents,
&children,
Default::default(),
)?;

IIRC a direct call to MutableRepo::rebase_descendants doesn't work in the --siblings case since we need to add two new parents. The bug seems to be that we're dropping any other parents that child had previously. This is very similar to what we're doing in jj parallelize, except that the bug isn't present there because we include the uninvolved parents.

I can't really tell what's happening in Iyla's example, but it's clear that the uninvolved (i.e. not split) parents were dropped if you print a more complete graph before and after the split.

I'll upload a fix.

a direct call to MutableRepo::rebase_descendants doesn't work in the --siblings case since we need to add two new parents.

Perhaps, the commit could be recorded as rewritten to two new commits? If the original commit had a branch, it will diverge. It's not ideal, but I think is technically correct.

I'm pretty sure that I tried this when I first implemented --siblings and it caused the original commit to become divergent (not just the branches).

Since then set_rewritten_commit only now only accepts one argument after e55168f.

The docs for set_divergent_rewrite seem to suggest the rebasing won't happen the way we want it to.
https://github.com/martinvonz/jj/blame/a786802b4017f6e20cc3ecde054e249e4f8ec005/lib/src/repo.rs#L834-L841

I'll try and see if I can get this to work, but if you have a specific suggestion that you think will work please let me know.

I'm pretty sure that I tried this when I first implemented --siblings and it caused the original commit to become divergent (not just the branches).

Divergence just means that there is more than one visible commit with the same change id, so that shouldn't be the case.

The docs for set_divergent_rewrite seem to suggest the rebasing won't happen the way we want it to. https://github.com/martinvonz/jj/blame/a786802b4017f6e20cc3ecde054e249e4f8ec005/lib/src/repo.rs#L834-L841

Oh, good point. I really should have thought about that myself since I made the change.

I don't know if there's a better way of doing this then. I think I'm getting close to done with the rebase API refactoring. Hopefully this can be rewritten and simplified once that's done.

I busted out the old set_rewritten_commit function that takes a vec of commit IDs to see what happens. Looks like you're right that the commit doesn't become divergent, so I must have been thinking of the branches. Doing this does change where @ ends up after the split though when --siblings is used, and of course branches become divergent.

#3486

Maybe it's better to fix with the current PR and rewrite when the API changes are complete?

Thank you all very much!

This is far less subtle than I expected, it doesn't explain other issues I think I was seeing. I thought we'd have a test for this and, well, now we do :).

~~Actually, no, we don't have that test. I'll hopefully remember to add it ~~

Update: The test will be there after #3488.