-
Notifications
You must be signed in to change notification settings - Fork 324
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
FR: Enhance aliases to run multiple commands with conditional logic #3673
Comments
This FR is probably a subset of #3262, as it is along the static/dynamic configuration discussion, which has been brought up many times in Discord. |
I actually just ran across this myself. I would very much like an alias that lets me do |
This feels somewhat related to #3575 and #3577. I think we could do it with minimal changes to the templating language. We'd just have to swap out the builtins for a few different ones, and require the user to create a template returning a [template-aliases]
'stack(x)' = 'surround("mutable() && ::(", ")", x)'
[aliases]
upload = {
args = {
"fix": {
"short": "-f",
"long": "--fix",
"type": "bool"
},
"lint": {
"short": "-l",
"long": "--lint",
"type": "bool"
},
"revision": {
"short": "-r",
"long": "--revision",
"type": "revset"
"default": "@"
}
},
"positional": True,
commands = '
[
if(args.fix, ["fix", "-r", stack("opts.revision)]),
if(args.lint, ["lint", "-r", stack(opts.revision)])
["git", "push", "-r", opts.revision] ++ positional
]
'
} What do people think of this? I personally have a lot of use-cases that I'd be able to solve with something like this. |
My gut feeling is that this is too much as a command alias system (and I would probably want to use general-purpose language than template DSL at this complexity.) That said, I think it's not uncommon to chain commands conditionally (like |
Yeah, I do agree that it does seem to be pushing the boundaries on what I'd feel comfortable using it for. I think that extensions could definitely solve this use case pretty well, but they have the opposite problem. A general-purpose language can call the jj api, but it's really overkill for what's essentially a simple string transformation. My personal opinion is that:
How about this? This seems far more simple, inspired by #3873 [aliases]
'upload()' = ["upload", "@"]
'upload(r, *args)' = [
["fix", "-r", "$r"],
# What does "-r" do?
["lint", "-r", "$r"],
# I know the *args isn't valid syntax, but I don't know what the right syntax would be
["git", "push", "-r", "$r", "$args"],
] Alternatively, we could try something a little more python-like [aliases]
'upload(revision="@", *args)' = [
["fix", "-r", "$revision"],
["lint", "-r", "$revision"],
["git", "push", "-r", "$revision", "$args"]
] I'm personally thinking that |
Totally agree.
It looks much manageable (lack of --flag parsing will make things simpler), and I like it. BTW, the current command aliases are expanded in a loop until converge. I think it's similar to C preprocessor. I'm not sure if the proposed aliases syntax can be compatible with the current substitution logic, and if we make it be evaluated like stacked function call, some user aliases might have to be adjusted.
I think we'll at least need to support |
I think it should be compatible. If you don't use parentheses in the alias name, it would expand to Then for the substitutions, instead of
Yes, that was my assumption on how they would be executed. Also sorry, I accidentally clicked "edit" on your comment instead of "quote reply". It should be restored now. |
Yeah, I just thought there might be a subtle behavior difference, but I don't have any particular example in mind. With these aliases: [aliases]
foo = ["--color", "always", "baz"]
baz = [] in the current cpp-like substitution logic,
If it's stacked like normal function calls:
(for this example, the result is the same) |
I've started working on a prototype, I'll try and send it out this week. It doesn't look too difficult. |
Ok, it turned out to be more effort than I thought, but I learnt a lot, and had some ideas to improve it. I was thinking of using the templating language to solve this. For example: 'upload(r, *args)' = """
# We need support for lists as a first-class citizen of the templating language. At the moment, you can use lists, but you can't define them.
command(["fix", "-r", r])
.then(|r| ["lint", "--revision=" ++ r.commit_id])
# We'd have to allow ++ to work on lists, or add some way to join two lists together
.then(|| ["git", "push", "-r", r.commit_id] ++ args])
""" This would allow us to solve two things: Firstly, we could chain 'x()' = """
command(["foo"]).then(|| ["bar"]).or(["baz"])
""" Secondly, we can pipe the output of previous commands onto the current command. For example, duplicate would "return" the new commit: 'duplicate_onto_head(r)' = """
command(["duplicate", "-r", r])
.then(|duplicate| ["rebase", "-s", duplicate.commit_id, "-d", "@"])
""" What do people think of this? It seems to add some complexity, but it feels to me like it strikes the right balance between complexity and power. I also think that although it adds some complexity, using explicit functions instead of just lists of lists makes it more obvious. |
I like your earlier proposal better because it was relatively simple. I'm a bit worried that if we try to add something like your most recent proposal, it's going to be a slippery slope to writing our own shell script language. |
That seems pretty fair to me (it also simplifies implementation a lot). The question I have left is how we want to do variable formatting. My current thoughts are:
|
If we don't need the former, I'll just special case
BTW, I prefer |
I also dislike the new proposal and also want to have a slimmed down syntax. But your new proposal probably belongs to the conversation in #3262. |
For martinvonz#3673, we will have aliases such as: ```toml 'upload(revision)' = [ ["fix", "-r", "$revision"], ["lint", "-r", "$revision"], ["git", "push", "-r", "$revision"], ] ``` Which will require aliases to map to `Vec<Vec<String>>`
For martinvonz#3673, we will have aliases such as: ```toml 'upload(revision)' = [ ["fix", "-r", "$revision"], ["lint", "-r", "$revision"], ["git", "push", "-r", "$revision"], ] ``` Which will require aliases to map to `Vec<Vec<String>>`
For martinvonz#3673, we will have aliases such as: ```toml 'upload(revision)' = [ ["fix", "-r", "$revision"], ["lint", "-r", "$revision"], ["git", "push", "-r", "$revision"], ] ``` Which will require aliases to map to `Vec<Vec<String>>`
For martinvonz#3673, we will have aliases such as: ```toml 'upload(revision)' = [ ["fix", "-r", "$revision"], ["lint", "-r", "$revision"], ["git", "push", "-r", "$revision"], ] ``` Template aliases: 1) Start as Config::Value 2) Are converted to String 3) Are placed in the alias map 4) Expand to a TemplateExpression type via expand_defn. However, command aliases: 1) Start as Config::Value 2) Are converted to Vec<Vec<String>> 3) Are placed in an alias map 4) Do not expand Thus, AliasesMap will need to support non-string values.
For martinvonz#3673, we will have aliases such as: ```toml 'upload(revision)' = [ ["fix", "-r", "$revision"], ["lint", "-r", "$revision"], ["git", "push", "-r", "$revision"], ] ``` Template aliases: 1) Start as Config::Value 2) Are converted to String 3) Are placed in the alias map 4) Expand to a TemplateExpression type via expand_defn. However, command aliases: 1) Start as Config::Value 2) Are converted to Vec<Vec<String>> 3) Are placed in an alias map 4) Do not expand Thus, AliasesMap will need to support non-string values.
For martinvonz#3673, we will have aliases such as: ```toml 'upload(revision)' = [ ["fix", "-r", "$revision"], ["lint", "-r", "$revision"], ["git", "push", "-r", "$revision"], ] ``` Template aliases: 1) Start as Config::Value 2) Are converted to String 3) Are placed in the alias map 4) Expand to a TemplateExpression type via expand_defn. However, command aliases: 1) Start as Config::Value 2) Are converted to Vec<Vec<String>> 3) Are placed in an alias map 4) Do not expand Thus, AliasesMap will need to support non-string values.
For martinvonz#3673, we will have aliases such as: ```toml 'upload(revision)' = [ ["fix", "-r", "$revision"], ["lint", "-r", "$revision"], ["git", "push", "-r", "$revision"], ] ``` Template aliases: 1) Start as Config::Value 2) Are converted to String 3) Are placed in the alias map 4) Expand to a TemplateExpression type via expand_defn. However, command aliases: 1) Start as Config::Value 2) Are converted to Vec<Vec<String>> 3) Are placed in an alias map 4) Do not expand Thus, AliasesMap will need to support non-string values.
For #3673, we will have aliases such as: ```toml 'upload(revision)' = [ ["fix", "-r", "$revision"], ["lint", "-r", "$revision"], ["git", "push", "-r", "$revision"], ] ``` Template aliases: 1) Start as Config::Value 2) Are converted to String 3) Are placed in the alias map 4) Expand to a TemplateExpression type via expand_defn. However, command aliases: 1) Start as Config::Value 2) Are converted to Vec<Vec<String>> 3) Are placed in an alias map 4) Do not expand Thus, AliasesMap will need to support non-string values.
What is the benefit of this approach over the much simpler idea of
I'm tried to check the discussion if my question was already discussed, this is the closest I found:
I don't quite follow this. People who are using |
If aliases would be able to run multiple commands with conditional logic, many workflow specific commands (porcelain in git jargon) could be implemented by the user or his/her company.
Many current commands expect some idiomatic workflow which could be implemented as a default aliases if aliases allow for conditional logic and new command eg.
is-same-revision <revision> <revision>
This change would externalize such workflow specific commands (and discussions) to user configurable defaults and be potentionaly replaced by user/company preferred workflow...
The text was updated successfully, but these errors were encountered: