cli: implement jj gerrit send for sending changes to Gerrit#2845
cli: implement jj gerrit send for sending changes to Gerrit#2845thoughtpolice wants to merge 3 commits into
jj gerrit send for sending changes to Gerrit#2845Conversation
ae28e5f to
1fb8e16
Compare
jj gerrit mail for submitting changes to Gerritjj gerrit mail for sending changes to Gerrit
1fb8e16 to
0d6be43
Compare
0d6be43 to
65e7135
Compare
martinvonz
left a comment
There was a problem hiding this comment.
I still haven't reviewed the last commit, but it's getting late, so here's what I have for now.
| // to ensure we parse the footer in an unambiguous manner; this avoids cases | ||
| // where a colon in the body of the message is mistaken for a footer line | ||
| let mut footer = IndexMap::new(); | ||
| let lines: Vec<&str> = body.trim().lines().rev().collect(); |
There was a problem hiding this comment.
nit: the first line could be skipped by .next() and could do .rev().map().take_while() or something.
| /// the form `branch@remote`, where `branch` is the target branch name that | ||
| /// the change will be submitted to, and `remote` is the remote to push to. | ||
| /// This remote MUST be a Git push URL for your Gerrit instance. | ||
| #[arg(long = "for", short = 'f', default_value = "main@origin")] |
There was a problem hiding this comment.
might be better to use trunk() as the default. We can't determine the remote if the trunk revision had multiple remote branches, though.
There was a problem hiding this comment.
So while trunk() probably does make some sense since it tracks what the upstream would be implicitly (without hardcoding pre-existing branch/remote names), I think it's rather confusing because revsets are already used to specify the trees you want to push, and can be specified multiple times, and trunk() looks quite like one at a glance. For example,
jj gerrit mail -r 'open() ~ empty()' -f 'trunk()'
reads quite strangely to me, especially because trunk() would be rather special in this context where it actually specifies a pair of (branch, remote) to send the change to; it's not clear at a glance what that means. I think that the branch@remote syntax is more familiar in this case since it's what shows up in the default log output, it's used for all other @git refs, etc. At the same time, it's also reasonably easy to translate to Gerrit's git push origin HEAD:refs/for/main so it's not too unfamiliar. It's half-way between us and them.
Related, but what I would actually like to do is have this default be over-rideable in the repository configuration, so that people can also do things like set the URL to the Gerrit instance. e.g.
jj config set --repo gerrit.default-remote canon@upstream
or something of this manner to override main@origin as the default --for. We could also have a nested TOML list in the same manner so that remotes can have individually specified default branch targets, e.g. set gerrit.<remote>.default-for "master" where <remote> can be any Git remote.
There was a problem hiding this comment.
jj gerrit mail -r 'open() ~ empty()' -f 'trunk()'
Yeah, it's a bit odd. I don't think --for should support a revset in general, but I mean the default could be derived from the trunk(). If the user configured per-repository trunk(), it would be something like trunk() = "foo@origin", so the purpose is quite similar to gerrit.default-remote. Maybe we should have a parsable configuration for that, and both trunk() and the gerrit --for should default to it?
BTW, is this origin in --for foo@origin the gerrit endpoint? The project I worked before had a gerrit-specific SSH remote, and we usually don't pull from it. In that setup, the user-visible remote branch is something like master@origin, but the remote to push to is gerrit.
| // mark the old commit as rewritten to itself, but only because it | ||
| // was a --dry-run, so we can give better error messages later | ||
| old_to_new.insert(original_commit.clone(), (original_commit.clone(), true)); | ||
| continue; |
There was a problem hiding this comment.
Maybe we can just rewrite the commits, and discard the transaction? iirc, git push --dry-run -c does a similar thing.
| } else { | ||
| write!(ui.stderr(), "Skipped Change-Id (it already exists) for ")?; | ||
| } | ||
| tx.write_commit_summary(ui.stderr_formatter().as_mut(), new)?; |
There was a problem hiding this comment.
Perhaps, this loop can be moved to the previous "rewriting" loop, and we wouldn't have to maintain the mapping between old and new commits.
I think the commit summary can be printed by tx.base_workspace_helper() to suppress scary markers.
|
From an outside perspective of a non-gerrit user, the word 'mail' in something seemingly unrelated to email (it is, right?) feels.. really weird Just my input, I've no real arguments for or against |
e41fca1 to
4fb99d4
Compare
What do you think we should call it? @thoughtpolice's first suggestion was |
|
I think I also plan on adding some other commands to fetch changes from Gerrit too (though sending them is the most important since fetching them has some workarounds.) Not sure how that factors in to the vocabulary, but something to consider... |
|
I suggest
Oh, and Git has
and Darcs has
|
|
I too am very supportive of |
4fb99d4 to
462f966
Compare
jj gerrit mail for sending changes to Gerritjj gerrit send for sending changes to Gerrit
bcf8a9c to
699346f
Compare
8a40482 to
08e210b
Compare
e71a4ee to
11ed301
Compare
| /// | ||
| /// This command modifies each commit in the revset to include a `Change-Id` | ||
| /// footer in its commit message if one does not already exist. Note that | ||
| /// this ID is NOT compatible with jj IDs, and is Gerrit-specific. |
There was a problem hiding this comment.
Can't remember if this was discussed earlier, but have we considered making this a transient hash of the JJ change ID that isn't actually stored in the committee description?
I've implemented something roughly similar to what you've done, and one problem I had was that when I run JJ split you end up with, by default, two commits with the same gerrit change ID.
That being said, I consider this a very good improvement over the current behaviour, so I'd recommend we submit as is even if we were to change it to that in the future.
There was a problem hiding this comment.
I like using jj change hex as Change-Id (see #4477 (comment)) but I'm not sure if we can get away with not storing it explicitly -- splits could get very confusing.
There was a problem hiding this comment.
Even git interactions could get weird (git commit --amend --no-edit, for example).
There was a problem hiding this comment.
I think that a hybrid approach where we don't store it explicitly automatically, but if you have manually stored it explicitly then we use that Change-ID would work relatively well. It'd also work well with splits by providing reasonable defaults, but could be overridden if required.
Re splits, it should work just fine as long as the user knows exactly what's gonna happen with the split (specifically, jj split to associate the change-ID with the second one, and jj commit to associate the Change-ID with the first one). Maybe we need something like jj change-id swap foo bar or jj change-id set -r foo <change-id>.
This merges PR jj-vcs#3191 (OpenSSH) and PR jj-vcs#2845 (Gerrit) in order to build from the combination.
This merges PR jj-vcs#3191 (OpenSSH) and PR jj-vcs#2845 (Gerrit) in order to build from the combination.
Natural extension of the existing `[T]` instance. Signed-off-by: Austin Seipp <aseipp@pobox.com> Change-Id: Ib7f6fd829096b2cac8e3d7b9471a92ddb76a621b
To be used for parsing `Change-Id`s from commits, in service of Gerrit support. Signed-off-by: Austin Seipp <aseipp@pobox.com> Change-Id: I434d76b1229b36b815622ad7409ced3a405cbe22
This implements the most basic workflow for submitting changes to Gerrit,
through a verb called 'send'. This verb is intended to be distinct from the word
'submit', which for Gerrit means 'merge a change into the repository.'
Given a list of revsets (specified by multiple `-r` options), this will parse
the footers of every commit, collect them, insert a `Change-Id` (if one doesn't
already exist), and then push them into the given remote.
Because the argument is a revset, you may submit entire trees of changes at
once, including multiple trees of independent changes, e.g.
jj gerrit send -r foo:: -r baz::
There are many other improvements that can be applied on top of this, including
a ton of consistency and "does this make sense?" checks. However, it is flexible
and a good starting point, and you can in fact both submit and cycle reviews
with this interface.
Signed-off-by: Austin Seipp <aseipp@pobox.com>
11ed301 to
83b9b3a
Compare
|
I understand that this PR is working to send a special changeID for gerrit, I would assume that ideally the changeID for jj would similarly come from the gerrit changeID when commits come with a gerrit changeID, implementing at least for gerrit issue #4706 |
|
Oh man, with all of the recent smattering of new jj buzz, I decided to take the plunge. I am so happy to have found this PR, which is the last remaining need for me (I really don't want to go do the EDITOR Change-Id hack). What is left to get this merged? Sure seems like this branch is stable, well maintained, in use, etc. |
My attention has been split elsewhere — among other JJ stuff, and a new job that does not use Gerrit — so I haven't had the time to push this over the finish line. There are many people using it, so I agree it's not ideal. It mostly just needs some tests and I would consider it in a reviewable state (modulo typical review cycles), it's just low on my stack of TODO items. |
Would you like someone else to help finish it? I'm not volunteering, but it maybe @jtolio or someone else is interested. |
That would be nice, but it is kind of annoying for a number of reasons. A common one (expressed elsewhere) is that you might want to abandon a change on Gerrit, then try to re-push a new version, but it will reject that if the I don't think there's a clear "best way" to make it work when the Change-Id trailer is not part of the actual commit object. That's part of the reason I changed the logic from using the stable Honestly, I still think the best long-term strategy might be to just cooperate with the Git developers and for the two of us (Git and Jujutsu) to standardize on a |
Yes, I'd be happy to get some help; someone else could even re-file this PR (assuming they kept my name as a coauthor) and I'd be happy to review it and suggest some things I wanted. I think some people have even offered previously; but of course life finds a way to make us busy... |
|
In case it helps anyone, I set [template-aliases]
'gerrit_change_id(change_id)' = '"Id0000000" ++ change_id.normal_hex()'
[templates]
draft_commit_description = '''
separate("\n",
description.remove_suffix("\n"),
if(!description.contains(change_id.normal_hex()),
"\nChange-Id: " ++ gerrit_change_id(change_id)
),
"\n",
surround("JJ: Changes:\n", "", indent("JJ: \t", diff.summary()))
)
''' |
Gerrit send support is added in jj-vcs/jj#2845 We want it, so let's pull in the latest commit from that branch... it's not perfect but from PR comments it seems stable enough Change-Id: I9032158d455619374818989ffd37788d6f004684 Reviewed-on: https://git.clicks.codes/c/Chimera/NixFiles/+/778 Tested-by: Skyler Grey <minion@clicks.codes> Reviewed-by: Samuel Shuert <coded@clicks.codes>
|
Since I was just notified that this was closed, and I was watching this PR, perhaps someone else besides me will appreciate knowing that this was closed because support was merged in #7245 !! Docs here: https://github.com/jj-vcs/jj/pull/7396/files |
Warning
This PR is very rough, and needs significant polish to handle edge cases and other small nits, but functions for basic uses just fine, I think. Please only use it on copies of your existing repositories, or throwaway repositories that you can
jj undoon. This warning will be removed once I feel it's more stable.Please remember: Do not taunt Happy Fun Ball.
This implements the most basic workflow for submitting changes to Gerrit, through a verb called 'send'. Send was chosen to avoid conflicting terminology with terms like "submit" (which merges a change in Gerrit) or "review" which is ambiguous.
Given a list of revsets (specified by multiple
-roptions), this will parse the footers of every commit, collect them, insert aChange-Id(if one doesn't already exist), and then push them into the given remote.Because the argument is a revset, you may submit entire trees of changes at once, including multiple trees of independent changes, e.g.
The remote to push into is, by default,
main@origin, and will translate torefs/for/mainat that remote. I.e. it is expected that you are using Gerrit as the source of truth, of course. You can specify another remote using the argument--forhowever, e.g.--for main@gerritwhich will change the remote. This syntax is intended to resemble existing syntax for remote references injjfor the sake of UX consistency, rather than blindly exposing the Gitrefs/syntax to users, which isn't needed, practically speaking.There are many other improvements that can be applied on top of this, including a ton of consistency and "does this make sense?" checks. However, it is flexible and a good starting point, and you can in fact both submit and cycle reviews with this interface.