jj icon indicating copy to clipboard operation
jj copied to clipboard

Support pushing to a specific git refspec

Open benbrittain opened this issue 10 months ago • 14 comments

Primarily for basic gerrit interop

benbrittain avatar Aug 16 '23 23:08 benbrittain

I'm not sure how I feel about this. My main problem is how it would interact with the existing options we have for jj git push. For example, what does jj git push -c @- --refspec refs/for/master do? I think I know the answer; it just doesn't seem to make much sense.

When pushing multiple branches, I'd be surprised if anything other than Gerrit would accept multiple branches being pushed into a single other branch/ref.

I've previously proposed (on Discord only, I think) a separate command for pushing a single branch to another name on the remote. It might look something like jj git push-branch -r wty --to-branch main for pushing commit wty to branch main on the remote. That would avoid the problems with combining --to-branch with the other flags on jj git push (and, btw, the other flags play well with each other, I think). If we had such a command, it seems quite reasonable to add --to-ref to that in addition to --to-branch (but they're be mutually exclusive, of course). How does that sound?

martinvonz avatar Aug 17 '23 01:08 martinvonz

It might look something like jj git push-branch -r wty --to-branch main for pushing commit wty to branch main on the remote.

I'm a little confused about how this would work with respect to remote-tracking branches for main. I guess it could be OK to not touch it, but only as long as the user never wants to fetch main from that remote or, ideally, fetch anything from that remote. There might be a solution short of fully supporting upstream branches, but I'm not sure.

This is less problematic with respect to jj git push-branch --to-ref, since the matching jj git fetch-branch --from-ref could just create the commit, inform the user what it is, but not put any branch there.

ilyagr avatar Aug 17 '23 03:08 ilyagr

I'm a little confused about how this would work with respect to remote-tracking branches for main. I guess it could be OK to not touch it, but only as long as the user never wants to fetch main from that remote or, ideally, fetch anything from that remote. There might be a solution short of fully supporting upstream branches, but I'm not sure.

I was thinking that this wouldn't do anything with remote-tracking branches. Do you see any problems with that?

martinvonz avatar Aug 17 '23 04:08 martinvonz

I was thinking that this wouldn't do anything with remote-tracking branches. Do you see any problems with that?

The main think I'm worried about, I guess, is that if you jj git push-branch -r wty --to-branch main and then fetch from that remote, you'll have a "main" branch either created, or moved, or conflicted, at least unless some method like #2074 is used to prevent that (which might be doable). I'm not sure how bad this is, or if I'm looking at it wrong, but none of these options seem immediately great.

What should happen to remote-tracking branches depends on whether we want "moved" or "conflicted" in that situation, and on what we want to happen in other situations where the user does the pushing and/or fetching more than once.

On second thought, jj git fetch-ref is not so bad so long as it doesn't create any branches (even if the ref it fetches is a branch).

ilyagr avatar Aug 17 '23 04:08 ilyagr

The main think I'm worried about, I guess, is that if you jj git push-branch -r wty --to-branch main and then fetch from that remote, you'll have a "main" branch either created, or moved, or conflicted, at least unless some method like #2074 is used to prevent that (which might be doable). I'm not sure how bad this is, or if I'm looking at it wrong, but none of these options seem immediately great.

I think the main use case is for letting you create a few commits on top of main@origin without creating or moving any branches, and then jj git push-branch -r @- --to-branch main or something like that. If you then fetch from the remote, the main branch would typically simply have moved forward to the commit you pushed, which is what I would have wanted and expected. Oh, you're thinking that we should update our record of the remote when we push? Yeah, that seems better, both so the user sees an up to date main@origin and so we can deduce force-with-lease as we normally do.

martinvonz avatar Aug 17 '23 04:08 martinvonz

jj git push-branch does start to feel like an awkward name though since we're talking about jj git push-branch --to-ref working with this sub-command too.

benbrittain avatar Aug 17 '23 15:08 benbrittain

jj git push-branch does start to feel like an awkward name though since we're talking about jj git push-branch --to-ref working with this sub-command too.

True. jj git push-commit?

martinvonz avatar Aug 17 '23 15:08 martinvonz

I'll rewrite this patch to address what has been talked about here.

On second thought, jj git fetch-ref is not so bad so long as it doesn't create any branches (even if the ref it fetches is a branch).

Something like this would also be needed to get the basic gerrit support started

benbrittain avatar Aug 17 '23 15:08 benbrittain

On second thought, jj git fetch-ref is not so bad so long as it doesn't create any branches (even if the ref it fetches is a branch).

Something like this would also be needed to get the basic gerrit support started

Why is that needed? I'm not saying it's not, I probably just don't know enough about Gerrit.

martinvonz avatar Aug 17 '23 16:08 martinvonz

The way someone interacts with gerrit changes is through a ref:

git fetch ssh://HOST/REPO refs/changes/UniqueRefIdentifier && git checkout/cherry-pick/ FETCH_HEAD

So, being able to grab a ref and interact with it is important

benbrittain avatar Aug 17 '23 17:08 benbrittain

Also github has those readonly PR refs I mentioned too

necauqua avatar Aug 17 '23 17:08 necauqua

The main think I'm worried about, I guess, is that if you jj git push-branch -r wty --to-branch main and then fetch from that remote, you'll have a "main" branch either created, or moved, or conflicted, at least unless some method like #2074 is used to prevent that (which might be doable). I'm not sure how bad this is, or if I'm looking at it wrong, but none of these options seem immediately great.

I think the main use case is for letting you create a few commits on top of main@origin without creating or moving any branches, and then jj git push-branch -r @- --to-branch main or something like that. If you then fetch from the remote, the main branch would typically simply have moved forward to the commit you pushed, which is what I would have wanted and expected. Oh, you're thinking that we should update our record of the remote when we push? Yeah, that seems better, both so the user sees an up to date main@origin and so we can deduce force-with-lease as we normally do.

I was actually getting things backwards before. There's a mixup keeps happening when I think about fetching & remote-tracking branches.

On third thought, I now think that the only reasonable thing to do is to have jj git push-branch (or whatever it's called) not touch remote-tracking branches in any way, just as you suggested. I no longer think that this is likely to cause much trouble.

Why I think push-branch must not touch the remote-tracking branches.

Let's say jj git push-branch -r wty --to-branch main moved the remote-tracking branch for main. Then, a subsequent jj git fetch would see that the actual position of the branch on the remote is the same as the position of the remote-tracking branch. In this case, jj git fetch doesn't fetch anything, as it assumes the local repo is up to date. This is clearly bad. Also, jj git push would replace the branch on the remote for the same reason, which is also bad.

OTOH, if the remote-tracking branch was left where it was, jj git fetch would fetch the new commits properly and either fast-forward the branch or create a conflict (as appropriate). jj git push would do nothing since it will notice that the branch was updated on the remote (the remote-tracking branch does not match the actual position of the branch on the remote).

Why I was worried

This feature makes it more likely that people use remotes with branch names that don't correspond to the local branches (e.g. if the remote's main branch corresponds to, say, the local prod branch as opposed to the local main branch).

jj doesn't have great support for such remotes at the moment, but I now think that a push-branch operation won't create any extra bugs (on top of any bugs jj may already have when dealing with such remotes). If you are fetching from a weird remote with jj git fetch, the same thing would happen regardless of whether the reason for its weirdness is that you used jj push-branch to move a branch in it or whether the remote was that way when you added it.

Fetching like this is probably not a great idea (unless you're doing something tricky), but it should merely result in local branches becoming conflicted. Hopefully, these conflicts will be easy to understand and easy to resolve (using the remote-tracking branches, for example).

ilyagr avatar Aug 18 '23 03:08 ilyagr

Let's say jj git push-branch -r wty --to-branch main moved the remote-tracking branch for main. Then, a subsequent jj git fetch would see that the actual position of the branch on the remote is the same as the position of the remote-tracking branch.

In that scenario, new position of the remote-tracking main can be observed by cmd_git_push*() and local main will move without issuing jj git fetch. It's not probably what we want, but that's how we handle remote-tracking branches right now. If we didn't move the tracking branch (which I don't know how to achieve with libgit2), jj git push-commit -r x --to-branch main; ...; jj git push-commit -r y --to-branch main might fail because the tracking branch is out of sync?

For Gerrit use case, I think this kind of stuff wouldn't matter since refs/for isn't tracked.

yuja avatar Aug 18 '23 06:08 yuja

The motivation for this change was Gerrit, but that will ideally be subsumbed by #2845, avoiding most of the nits and UX problems we've talked about here — though we could address them another time, I firmly believe this way forward is much better for Gerrit support.

thoughtpolice avatar Jan 18 '24 20:01 thoughtpolice

I have recently been playing around with Radicle, which is a decentralized git source forge. It takes advantage of pushing to specific refspecs for features like creating patches. I haven't used Gerrit, from what I gather it probably does something similar.

The Radicle documentation for this can be found here: https://radicle.xyz/guides/user#working-with-patches

Zambito1 avatar Apr 03 '24 01:04 Zambito1