-
Notifications
You must be signed in to change notification settings - Fork 382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement jj sign
and jj unsign
commands
#4747
base: main
Are you sure you want to change the base?
Conversation
0753a24
to
46731eb
Compare
517e230
to
45be04d
Compare
6b049ab
to
2c49a16
Compare
This is still WIP, but I would love to get some feedback on the general approach and if this has any design flaws that I need to address before polishing. // edit Some things, which have been discussed in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually left a couple of comments.
Dropped signatures hint and builtin templates can be separate PRs, yes.
Will reiterate my idea for the templates here:
There should be a some_consistent_name_for_crypt_sig
template that's used in the default log template and by default it's empty.
Then there should be some_other_name
template that's unused, but is present by default, with my pretty checkmarks or whatever.
And then in FAQ or in docs, there should be a tip "you can run this jj config command to set that first template to equal that second one so that you'll get checkmarks in all the default templates".
"Or you can set that template to if(signature, "[•]", "")
to just differentiate signed commits from unsigned in the logs, which is very fast compared to actually verifying them"
Another thing that could be in some tips and tricks section, is an alias for jj log
that has --config= set that template thing
to show signatures temporarily.
Also for the builtin_log_detailed
there was a change somewhere that showed a Signature:
extra line - maybe do the same thing with empty default one + unused verifying one?. Or have the detailed log always verify. Maybe Yuja has an opinion there
lib/src/commit_builder.rs
Outdated
pub fn set_sign_key(&mut self, sign_key: Option<String>) -> &mut Self { | ||
self.sign_settings.key = sign_key; | ||
if sign_key.is_some() { | ||
self.sign_settings.key = sign_key; | ||
} | ||
self | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: looking at the overall method now (my previous nit was a mechanical refactor to avoid an unnecessary clone) - set_sign_key
does nothing if given option is None, which is a bit.. meh, idk - could change it to accept sign_key: String
directly and make the callers do the if let Some(..) = ..
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since sign_settings.key
is Option<String>
, wouldn't we eliminate the case, where the caller wants to explicitly set sign_settings.key = None
?
Or is this case nonsense and sign_settings.key
should never be set to None
after initialization (SignSettings::from_settings()
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@necauqua, I reverted the change as I honestly do not recall why I made it in the first place. 🤔
Let me know if you have any objections.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I never loaded the entire context of this specific PR into my head, so I don't have any objections whatsoever
If it works, it works, and if it also happens to make sense, the better)
cli/tests/test_sign_command.rs
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make most of these tests sign multiple commits to make sure that that works as expected
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
random suggestion, ignore if it doesn't make sense:
Snapshot the evolog to ensure no quadratic rebases that I feared?.
This is probably handled by tests of transform_descendants
already.
Wow, thanks a lot for your feedback, @necauqua and @martinvonz! Now I am all set to return to my dungeon and work on the how. // edit |
Very offtopic, sorry, but there's no chance in hell I'd have contributed to jj if is was cpp or something. |
Thanks @yuja! |
jj sign
commandjj sign
and jj unsign
commands
Things have been a bit hectic at work recently, but I expect to make some progress on this in the next couple of days. |
1866a0b
to
3edbfa3
Compare
b534a86
to
29a8933
Compare
docs/config.md
Outdated
### Manually signing commits | ||
|
||
You can use [`jj sign`](./cli-reference.md#jj-sign)/[`jj | ||
unsign`](./cli-reference.md#jj-unsign)to sign/unsign commits manually. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@yuja, we can extend this section with an example for the workflow you mentioned in #4747 (comment) when implementing #5428.
29a8933
to
57cf2eb
Compare
* The new `jj sign` command allows signing commits. | ||
|
||
* The new `jj unsign` command allows unsigning commits. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These do feel a bit scarce, but maybe that's ok? I am open to suggestions of things to add here.
57cf2eb
to
bb917db
Compare
/// What revision(s) to sign | ||
#[arg( | ||
long, short, | ||
default_value = "@", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: omit the default value for now? I don't think the current default is useful. (the same for unsign
)
add = ArgValueCandidates::new(complete::mutable_revisions), | ||
)] | ||
revisions: Vec<RevisionArg>, | ||
/// Sign a commit that is not authored by you. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: "Sign revisions that are .."?
let bad_commits = commits | ||
.into_iter() | ||
.filter(|commit| commit.author().email != user_email) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Can't we use SignSettings::should_sign()
?
BTW, I'm not sure if this should be an error. If a warning is good enough, warnings can be emitted within/after transform_descendants()
.
@necauqua any thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thoughts about jj philosophy are that you can always jj undo
any rewrite - at least if jj gabe you a warning/notice that something unusual has happened.
So, in that case, error => warning is good imo
//offtop
I guess the same applies to removing all: btw, even though I like it and didn't like the idea of getting rid of it - if jj brings your attention to the fact that rebase happened to affect multiple revs so that you can undo it if it was not what you expected - the all: stumble it not needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like the idea of embracing jj undo
. Warnings it is then!
} | ||
|
||
if commits.contains(rewriter.old_commit()) { | ||
let commit_builder = rewriter.reparent(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll probably need to do always .reparent()
. See cmd_describe()
for example.
.reparent()
can be skipped when !rewriter.parents_changed() && no_need_to_sign_the_current_commit
. cmd_describe()
excludes uninteresting commits prior to transform_descendants()
. I'm not sure which is better here, but prefiltering might simplify the condition to do .reparent()
.
cli/src/commands/sign.rs
Outdated
1 => { | ||
write!(formatter, "Signed commit ")?; | ||
tx.base_workspace_helper() | ||
.write_commit_summary(formatter.as_mut(), &signed_commits[0])?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use tx.write_commit_summary()
and tx.commit_summary_template()
because signed_commits[N]
is rewritten commits. (also applies to unsign)
@@ -1,6 +1,7 @@ | |||
--- | |||
source: cli/tests/test_generate_md_cli_help.rs | |||
description: "AUTO-GENERATED FILE, DO NOT EDIT. This cli reference is generated by a test as an `insta` snapshot. MkDocs includes this snapshot from docs/cli-reference.md." | |||
snapshot_kind: text |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: you'll need to upgrade cargo-insta
cli/src/commands/sign.rs
Outdated
match signed_commits.len() { | ||
0 => (), | ||
1 => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: you can also use slice pattern
match &*signed_commits {
[] => {}
[commit] => ...
commits => ...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, neat! Thanks.
// edit
I have to say, your suggestion blew my mind a little. That was definitely another moment of "Rust is awesome!".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did not apply that suggestion to this part, yet. I got some compiler errors around .into_iter().ids()
and since this part might change (#4747 (comment)), I decided to leave that until tomorrow when my mind is fresh.
/// Unsign a commit that is not authored by you. | ||
#[arg(long, short)] | ||
allow_not_mine: bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@necauqua Do we need --allow-not-mine
for unsign?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, it's the same thing with jj undo
- I now think that we don't need this flag at all in both subcommands.
If jj prints a warning that you messed with someone's signature, you can just undo that.
Or, if we keep it, it's better to have it in both ones for consistency
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, it's the same thing with jj undo - I now think that we don't need this flag at all in both subcommands.
If jj prints a warning that you messed with someone's signature, you can just undo that.
Couldn't agree more on removing the flag entirely. That also leaves us with a simpler interface.
bb917db
to
6842c60
Compare
Commits to be signed, which are not authored by the user, require `--allow-not-mine`. We do not resign commits, which are already signed. For people using hardware devices for signatures, resigning commits is cumbersome (e.g. having to touch your YubiKey for each signature). The output of `jj sign` is based on that of `jj abandon`. --- Co-authored-by: julienvincent <[email protected]> Co-authored-by: necauqua <[email protected]>
Commits to be unsigned, which are signed and not authored by the user, require `--allow-not-mine`. The output of `jj unsign` is based on that of `jj abandon`. --- Co-authored-by: julienvincent <[email protected]> Co-authored-by: necauqua <[email protected]>
6842c60
to
5ce99e0
Compare
Closes #4712
Checklist
If applicable:
CHANGELOG.md
I have updated the config schema (cli/src/config-schema.json)