-
Notifications
You must be signed in to change notification settings - Fork 365
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
command
#4747
base: main
Are you sure you want to change the base?
Conversation
0753a24
to
46731eb
Compare
cli/src/commands/sign.rs
Outdated
key: Option<String>, | ||
/// What revision to sign | ||
#[arg(long, short, default_value = "@")] | ||
revision: RevisionArg, |
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.
Wouldn't it make sense to have this be revisions: Vec<RevisionArg>
?
This way you can do jj sign -r main..bookmark
to sign a whole branch.
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.
That can still be supported with a single argument. The whole main..bookmark
string gets passed as a single argument from your shell to the jj
binary. The binary then decides how to interpret it, and the jj sign
command will presumably want to accept multiple revisions, as most commands do. Commands that want a single revision still accept revsets; they just validate that the revset contains exactly one revision.
It may still be a good idea to allow multiple revision arguments here, so the user can do jj sign -r X -r Y -r Z
to sign those three revsets.
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.
Thanks for the clarification c: and yes jj sign -r X -r Y -r Z
sounds logical
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.
A reminder about an identical discussion - maybe the issue with rebasing transformations got better since then?
If implemented naively, signing main..bookmark
is quadratic - signing an ancestor rebases all of it's descendants, and then we sign the child of that ancesrot rebasing all of its descendants and so on
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.
maybe the issue with rebasing transformations got better since then?
Yes, we have .transform_descendants(roots, |rewriter| ..)
API now.
517e230
to
45be04d
Compare
415d744
to
9184331
Compare
Co-authored-by: julienvincent <[email protected]>
9184331
to
6b049ab
Compare
Cannot make it accept multiple revisions easily, that'd have to be done with a rebaser
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
if !args.drop && commit.is_signed() { | ||
return Err(user_error( | ||
"Commit is already signed, use --force to sign anyway", | ||
)); | ||
} | ||
if commit.author().email != command.settings().user_email() { | ||
return Err(user_error( | ||
"Commit is not authored by you, use --force to sign anyway", | ||
)); | ||
} |
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.
Now that this version can work with multiple commits, those checks should show the offending one.
Or ones, actually, so they should eagerly collect lists of bad commits and print them all at once.
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: Also if !args.force
can be moved outside of the loop
}; | ||
let mut signed_commits = vec![]; | ||
tx.repo_mut().transform_descendants( | ||
commits.iter().ids().cloned().collect_vec(), |
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 commit_ids
here?
Hmm, if check_rewritable
consumes the vector that sounds meh, it should accept &[CommitId]
.
If it really does need to consume the vector for some reason ignore this comment then 🤷
(Or maybe it accepts any iterable and you just need to borrow there, I haven't looked at the code in a year already)
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(..) = ..
?
@@ -0,0 +1,117 @@ | |||
// Copyright 2023 The Jujutsu Authors |
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: maybe 2024 or 2025? or maybe this work was started in 2023 so it should be 2023?
let mut workspace_command = command.workspace_helper(ui)?; | ||
|
||
let commits = workspace_command | ||
.resolve_some_revsets_default_single(ui, &args.revisions)? |
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 think we want parse_union_revsets()
here. I think it's pretty expected to want to sign multiple commits at once.
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.
if args.drop { | ||
write!(formatter, "Signature was dropped: ")?; | ||
} else { | ||
write!(formatter, "Commit was signed: ")?; | ||
} |
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 would expect these messages to depend on the commit's previous signature, especially "Signature was dropped" is confusing for commits that were not signed to start with.
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.
This made sense with a singular commit, since pre-check would short-circuit the situation where args.drop=true and the commit was not signed
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. |
/// 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: it might be better to make the default configurable. I think jj sign
will be used in the context where we do jj fix && jj sign .. && jj git push
.
(can be addressed separately)
/// Sign a commit that is not authored by you or was already signed. | ||
#[arg(long, short)] | ||
force: 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.
nit: --force
tends to mean arbitrary things. Maybe --allow-not-mine
or something is better?
/// Drop the signature, explicitly "un-signing" the commit. | ||
#[arg(long, short = 'D', conflicts_with = "force")] | ||
drop: 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.
nit: just dropping idea: jj unsign
or jj signature drop
.
(It's unlikely we'll add more sub commands, so namespaced command might be overkill.)
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 like jj unsign
.
Splitting off the drop
from jj sign
would make cmd_sign()
a little simpler, as well.
.write()?; | ||
signed_commits.push(new_commit); | ||
} | ||
Ok(()) |
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.
maybe you'll need to do rewriter.reparent()
. cmd_describe()
does rewriter.rebase()
, but I think .reparent()
works there, too.
long, short, | ||
default_value = "@", | ||
value_name = "REVSETS", | ||
add = ArgValueCandidates::new(complete::all_revisions), |
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: complete::mutable_revisions
?
Thanks @yuja! |
Closes #4712
Checklist
If applicable:
CHANGELOG.md