jj icon indicating copy to clipboard operation
jj copied to clipboard

[git] Feature request: track commit rewriting from Git

Open chooglen opened this issue 3 years ago • 4 comments

jj doesn't know about commits rewritten on the Git side, e.g. if I have this history:

C
|
B
|
A

and I swap B and C using git rebase -i, I'd want jj to tell me that my repo looks like:

B'
|
C'
|
A

Instead, jj tells me that my repo looks like:

B'  C
|   |
C'  B
|  /
A

One way to do this would be to add a Git post-rewrite hook (which IIRC is what git-branchless does).

chooglen avatar Aug 25 '22 03:08 chooglen

I think it works in simple cases. I just tried it in a tiny repo with just three linear commits like in your example. Could it be that you have other branches still pointing to C or one of its descendants?

martinvonz avatar Aug 25 '22 03:08 martinvonz

Ah, yes I had a branch pointing to one of those commits.

Maybe that wasn't the best example. Here's another one:

o E (branch-foo)
| 
o D
| 
o C
| 
| B @ (working copy) 
|/  
o A (HEAD@git)

after rewording with git commit --amend --only (no files):

@ F (working copy)
| 
o A'  HEAD@git
| 
| o E
| | 
| o D 
| | 
| o C
| | 
| o B
|/  
o A

chooglen avatar Aug 25 '22 16:08 chooglen

In another instance, jj thought that I had rewritten some commits in Git, but I actually didn't. I don't have a good handle of the reproduction recipe or what the relevant data points are, but the scenario looked something like:

@ 
| 
o 
| o D
| | 
| o 
| | 
| o 
| | 
| | o 
| | | 
| | o 
| | | 
| | o 
| |/  
| o C
| | 
| o 
| | 
| o 
| | 
| o 
| | 
o |   
:\ \  
: ~/
: o A
:/  
o-.   B
:\ \  

After importing refs, jj rebased C and its descendants onto B, losing A:C. Curiously, C has a branch pointing to it and jj moves the branch to B. This branch wasn't touched in Git at all.

Adding a Git branch to D prevents this bad rebase from happening. Adding a Git branch to A causes C and its descendants to be rebased onto A instead of B.

chooglen avatar Oct 10 '22 20:10 chooglen

I think what happened is that the Git branch pointing to C was removed (or moved), so the commits A:C were effectively removed from the Git repo. When that happens, the next ref-import into jj will notice that you apparently wanted to remove A:C (since you - or maybe git fetch - decided that in the Git repo), so it rebases the descendants C.. off of them. Could that be what happened?

martinvonz avatar Oct 10 '22 22:10 martinvonz

the Git branch pointing to C was removed (or moved)

IIRC, that's the exact opposite of what happened. I created a jj branch at C, and (since my jj branch exports aren't working well) I also created an identical branch in Git, but somehow jj thought that the branch should be at B instead. It was extra weird because B was a commit from the remote which was not rewritten.

Perhaps fiddling with the branch in both Git and jj was the source of the problem? In the colocated repo case, I wonder if it makes sense to treat the Git refs like remotes and let the user manually resolve conflicts, e.g. we could have a conflicted foo-branch where Git's foo-branch@git-repo is different from jj's local foo-branch.

chooglen avatar Oct 11 '22 16:10 chooglen