Skip to content
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

Replace --allow-large-revsets by all: prefix #1911

Merged
merged 4 commits into from
Jul 29, 2023
Merged

Conversation

martinvonz
Copy link
Member

Checklist

If applicable:

  • I have updated CHANGELOG.md
  • I have updated the documentation (README.md, docs/, demos/)
  • I have updated the config schema (src/config-schema.json)
  • I have added tests to cover my changes

@PhilipMetzger
Copy link
Contributor

PhilipMetzger commented Jul 25, 2023

This looks great, although I'm left wondering if we should do a soft deprecation for the --allow-large-revsets option and then remove it.

cc @ilyagr, for a final look.

@martinvonz
Copy link
Member Author

This looks great, although I'm left wondering if we should do a soft deprecation for the --allow-large-revsets option and then remove it.

Good idea. I kept the flag and made it print an error message. I'm not sure if that counts as soft deprecation, but I think it's good enough.

@ocharles
Copy link

Out of interest, what's the motivation? Fewer CLI flags?

@martinvonz
Copy link
Member Author

Out of interest, what's the motivation? Fewer CLI flags?

Sorry, I really should have said in the commit message. Will update it. But yes, it's so we have a generic way without needing a new flag for each revset you might want to allow duplicates for. For some commands, you might even need multiple such flags otherwise (--allow-large-revset-in-destination and --allow-large-revset-in-source or similar), which becomes very awkward.

@chooglen
Copy link
Contributor

In my mental model of revsets, multiple() feels a bit like an oddball because it doesn't affect which revisions the revset resolves to. I think of 'whether or not multiple revisions are allowed' as a concern for whatever consumes the revset. Does that match your mental model?

@necauqua
Copy link
Contributor

In my mental model it's just a metadata flag, which the consumer could or could not care about, similar to what -L was but embedded in the revset.

Generally you could just always allow those, but this flag is just 'yes-I-know-what-Im-doing' confirmation (and also like a divergent changeid or branch would be caught by this too).

Avoiding having -L-in-dest and -L-in-src is very nice.

@chooglen
Copy link
Contributor

In my mental model it's just a metadata flag

Yes, exactly! I would prefer for such flags to be expressed in its own syntax than to reuse the revset 'function' syntax.
In the meantime, I'm ok to document that this 'function' isn't really a 'function' and punt on what the metadata syntax should be (probably until we decide to add another one).

@PhilipMetzger
Copy link
Contributor

Good idea. I kept the flag and made it print an error message. I'm not sure if that counts as soft deprecation, but I think it's good enough.

That's definitely a soft deprecation in my book, as we discourage further use before removing it.

lib/src/revset.rs Outdated Show resolved Hide resolved
src/cli_util.rs Show resolved Hide resolved
@necauqua
Copy link
Contributor

expressed in its own syntax

Yes I was thinking about having some sigil for this or something, but just using the function syntax for everything is also not bad, idk - and way, way less hacky, even if still a bit hacky, than having those -L flags I always disliked

@martinvonz
Copy link
Member Author

expressed in its own syntax

Yes I was thinking about having some sigil for this or something, but just using the function syntax for everything is also not bad, idk - and way, way less hacky, even if still a bit hacky, than having those -L flags I always disliked

I think we talked about using % or maybe other symbols like *. I feel like jj rebase -d 'x|y%' or jj rebase -d '*x|y' looks too much like regular operators (and this sigil we're talking about would only be allowed at the top level), but maybe we can find a better syntax. We can't use many:x|y because : is already an operator. I suppose we could use it if we replaced the current : operator by :: to match hg :)

@necauqua
Copy link
Contributor

necauqua commented Jul 25, 2023

Taking inspiration from Rust itself - and the fact that you were discussing having other things like sized which would take extra arguments - we could have an alternative function application syntax, which is like multiple!(..) where the ! (or other symbol or other form of parens etc) denotes that this is not a revset function but rather is something different, just like Rust does with macros.

Just a random idea to consider :)

Well since those flags or whatever are toplevel-only, the syntax you were describing (something like prefix many:) is basically that, I guess

Never used hg in my life but having less syntax difference in things that are (it seems, idk 🙃) extremely similar is a good thing imo

@ilyagr
Copy link
Contributor

ilyagr commented Jul 26, 2023

It might take me a bit of time to think about it properly and/or try out jj with that patch. Feel free to just merge it without waiting for me.

I suppose we could use it if we replaced the current : operator by :: to match hg :)

something like prefix many:

I like these ideas since they are shorter to type and don't require quotes.

This also suggests a possible solution to another minor issue I have. I often intentionally omit --allow-large-revsets to preview what the command is going to do before doing it. (Then, I just add -L to the command). One more flexible way to solve this usecase would be to have another operator show:. Using show:revset should act like many:revset but also print out the revisions that revset expanded to. I can then use jj undo if I the list of revisions is not what I expected.

@ilyagr
Copy link
Contributor

ilyagr commented Jul 26, 2023

To be clear, what I said isn't meant to be blocking.

@martinvonz
Copy link
Member Author

I feel strongly that we should replace the --allow-large-revsets by some syntax in each revset (or possibly outside the revset, depending on how you define the scope of the revset syntax), but I don't feel strongly about that syntax.

I like many:x|y (or all:x|y, perhaps) because it can be reused for file pattern syntax later if we decide to copy hg's way of doing it (things like glob:**.c). The main drawback is that we'd need to replace the current : operator by ::, which is a somewhat large breaking change. Still, if we think that syntax is the best solution, then I think it's better to change it now than to be stuck with something we don't like as much - I think we still have few enough users that we can do that.

We can have foo:bar continue to work and just print a warning. The only case we'd need to break right away is if exactly many:foo, but it's unlikely to affect many users because I don't think a branch named many is very common.

I'm also fine with going with multiple() as implemented (or renaming it to e.g. unbounded()).

@yuja
Copy link
Contributor

yuja commented Jul 27, 2023

I like many:x|y (or all:x|y, perhaps) because it can be reused for file pattern syntax later if we decide to copy hg's way of doing it (things like glob:**.c).

+1

The main drawback is that we'd need to replace the current : operator by ::, which is a somewhat large breaking change.

This might be actually better for newcomers since : has different use case in both hg and git. We can allow foo:bar range as you said, but I prefer to remove ambiguous syntax at some point.

@ilyagr
Copy link
Contributor

ilyagr commented Jul 27, 2023

If we're OK with many:, perhaps we should rename multiple to many, just because it's shorter to type?

I wrote out some thoughts below, and my conclusion from them is that I'd rename multiple to many for now, submit this, and see how we like it. I like the many: idea quite a bit, and I wouldn't mind switching to it right now too much, but it seems a little underbaked.

Various thoughts I had.

  • I like many: as well, mainly because I'm still excited about show:.

    It's not entirely clear how many: is distinguished form a function call. In my mind, we could consider it different because it affects the interpretation of the entire revset. I'm not sure if glob: would make sense in this interpretation.

    Alternatively, we could consider it a function and funtion:argument could be an alternative function call syntax with the explicit purpose of being easier to type in the shell and not requiring the user to track so many pairs of parentheses. It would have low precedence, similar to Haskell's $.

  • Other symbols that come to mind are many~revset (would require to change negation to ~~ or something) and many%revset (seems like it would just work). I think this looks a bit uglier, but I'm guessing I'd get used to it quickly.

  • I'm OK with switching to many: and :: immediately.

  • I'm OK with trying out many() first and then revisiting this a bit later. I'd probably implement show() at some point, since I think I'll miss that part of --allow-large-revsets functionality. I think undo is now reliable enough to rely on it and give the user ways to be informed about potential errors instead of trying to prevent all user errors.

  • I'm OK with having both many and many%revset for a while to see if we like %.

@ilyagr
Copy link
Contributor

ilyagr commented Jul 27, 2023

Replying to myself, after I saw Yuya's message.

It's not entirely clear how many: is distinguished form a function call. In my mind, we could consider it different because it affects the interpretation of the entire revset. I'm not sure if glob: would make sense in this interpretation.

It's encouraging to me that @yuja doesn't seem concerned about this. Do you have a mental model of what should be a function call and what should be a something: operator (or something%)? What are other examples of something: operators we'd want to have?

This is not blocking for this PR (in any of its potential forms).

@martinvonz
Copy link
Member Author

It's encouraging to me that @yuja doesn't seem concerned about this. Do you have a mental model of what should be a function call and what should be a something: operator (or something%)? What are other examples of something: operators we'd want to have?

IMO, what justifies a different syntax for this is that it's about changing the behavior of the revset as whole - jj rebase -d 'multiple(x)|y' would be syntactically valid but have no effect (at least the way I've implemented it in this PR. I haven't thought about what other prefixes we might have. On the other hand, if we add a sized() revset function, then that would make sense when applied to a sub-expression. We could treat a top-level sized() specially, but many: seems clearer.

(I think I actually like all: better than many:, btw.)

@yuja
Copy link
Contributor

yuja commented Jul 27, 2023

Do you have a mental model of what should be a function call and what should be a something: operator (or something%)? What are other examples of something: operators we'd want to have?

I think <kind>:<expr> changes the interpretation of <expr>. I don't think we'll have many <kind>s for revset, but if we had a Git-compatible revset parser, it might be like git:HEAD^. More practical example is string pattern like branches(glob:"foo/*").

@ilyagr
Copy link
Contributor

ilyagr commented Jul 27, 2023

There another usecases in my mind for this kind of syntax with operators like glob% or regex%, both outside revsets and inside revset functions like branches(). This would not be global. I suppose such operators could be functions taking strings, but that's a bit long to type.

@ilyagr
Copy link
Contributor

ilyagr commented Jul 27, 2023

Re %: that symbol might require quoting on Windows. I'm not sure what the rule is if it's in the middle of a word, so there's a chance it's OK.

@martinvonz
Copy link
Member Author

Re %: that symbol might require quoting on Windows. I'm not sure what the rule is if it's in the middle of a word, so there's a chance it's OK.

I would go with : anyway. It seems like a more obvious choice, and it's what hg chose for file patterns.

I wrote out some thoughts below, and my conclusion from them is that I'd rename multiple to many for now

My current thinking is that we should do (and I think you said you're okay with this too):

  1. Allow :: as an alternative to :
  2. Print a warning when : is used
  3. Replace --allow-large-revsets by all:, still allowing : everywhere (only exactly the all: prefix will be stripped)
  4. Drop support for :

I don't know how much time I think we need between each step. I think we can probably do the first 3 in quick succession.

@martinvonz martinvonz marked this pull request as draft July 28, 2023 00:01
martinvonz added a commit that referenced this pull request Jul 28, 2023
The `--allow-large-revsets` flag we have on `jj rebase` and `jj new`
allows the user to do e.g. `jj rebase --allow-large-revsets -b
main.. -d main` to rebase all commits that are not in main onto
main. The reason we don't allow these revsets to resolve to multiple
commits by default is that we think users might specify multiple
commits by mistake. That's probably not much of a problem with `jj
rebase -b` (maybe we should always allow that to resolve to multiple
commits), but the user might want to know if `jj rebase -d @-`
resolves to multiple commits.

One problem with having a flag to allow multiple commits is that it
needs to be added to every command where we want to allow multiple
commits but default to one. Also, it should probably apply to each
revset argument those commands take. For example, even if the user
meant `-b main..` to resolve to multiple commits, they might not have
meant `-d main` to resolve to multiple commits (which it will in case
of a conflicted branch), so we might want separate
`--allow-large-revsets-in-destination` and
`--allow-large-revsets-in-source`, which gets quite cumbersome. It
seems better to have some syntax in the individual revsets for saying
that multiple commits are allowed.

One proposal I had was to use a `multiple()` revset function which
would have no effect in general but would be used as a marker if used
at the top level (e.g. `jj rebase -d 'multiple(@-)'`). After some
discussion on the PR adding that function (#1911), it seems that the
consensus is to instead use a prefix like `many:` or `all:`. That
avoids the problem with having a function that has no effect unless
it's used at the top level (`jj rebase -d 'multiple(x)|y'` would have
no effect).

Since we already have the `:` operator for DAG ranges, we need to
change it to make room for `many:`/`all:` syntax. This commit starts
that by allowing both `:` and `::`.

I have tried to update the documentation in this commit to either
mention both forms, or just the new and preferred `::` form. However,
it's useless to search for `:` in Rust code, so I'm sure I've missed
many instances. We'll have to address those as we notice them. I'll
let most tests use `:` until we deprecate it or delete it.
@martinvonz martinvonz changed the title Replace --allow-large-revsets by multiple() revset function Replace --allow-large-revsets by all: prefix Jul 28, 2023
@martinvonz martinvonz marked this pull request as ready for review July 28, 2023 18:12
@martinvonz
Copy link
Member Author

This PR now instead replaces : by :: and replaces --allow-large-revsets by a all: prefix

Copy link
Contributor

@PhilipMetzger PhilipMetzger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but wait on yuya or ilya before merging

src/cli_util.rs Show resolved Hide resolved
martinvonz added a commit that referenced this pull request Jul 28, 2023
The `--allow-large-revsets` flag we have on `jj rebase` and `jj new`
allows the user to do e.g. `jj rebase --allow-large-revsets -b
main.. -d main` to rebase all commits that are not in main onto
main. The reason we don't allow these revsets to resolve to multiple
commits by default is that we think users might specify multiple
commits by mistake. That's probably not much of a problem with `jj
rebase -b` (maybe we should always allow that to resolve to multiple
commits), but the user might want to know if `jj rebase -d @-`
resolves to multiple commits.

One problem with having a flag to allow multiple commits is that it
needs to be added to every command where we want to allow multiple
commits but default to one. Also, it should probably apply to each
revset argument those commands take. For example, even if the user
meant `-b main..` to resolve to multiple commits, they might not have
meant `-d main` to resolve to multiple commits (which it will in case
of a conflicted branch), so we might want separate
`--allow-large-revsets-in-destination` and
`--allow-large-revsets-in-source`, which gets quite cumbersome. It
seems better to have some syntax in the individual revsets for saying
that multiple commits are allowed.

One proposal I had was to use a `multiple()` revset function which
would have no effect in general but would be used as a marker if used
at the top level (e.g. `jj rebase -d 'multiple(@-)'`). After some
discussion on the PR adding that function (#1911), it seems that the
consensus is to instead use a prefix like `many:` or `all:`. That
avoids the problem with having a function that has no effect unless
it's used at the top level (`jj rebase -d 'multiple(x)|y'` would have
no effect).

Since we already have the `:` operator for DAG ranges, we need to
change it to make room for `many:`/`all:` syntax. This commit starts
that by allowing both `:` and `::`.

I have tried to update the documentation in this commit to either
mention both forms, or just the new and preferred `::` form. However,
it's useless to search for `:` in Rust code, so I'm sure I've missed
many instances. We'll have to address those as we notice them. I'll
let most tests use `:` until we deprecate it or delete it.
src/cli_util.rs Show resolved Hide resolved
src/cli_util.rs Show resolved Hide resolved
Copy link
Contributor

@ilyagr ilyagr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great overall, I added some optional comments. I'll probably finish reviewing later (though I also think there's no need to block this on my review). Either way, it seems like you'll rework this a bit based on Yuya's comments.

docs/revsets.md Outdated Show resolved Hide resolved
docs/github.md Outdated Show resolved Hide resolved
lib/src/revset.rs Show resolved Hide resolved
CHANGELOG.md Show resolved Hide resolved
martinvonz added a commit that referenced this pull request Jul 28, 2023
The `--allow-large-revsets` flag we have on `jj rebase` and `jj new`
allows the user to do e.g. `jj rebase --allow-large-revsets -b
main.. -d main` to rebase all commits that are not in main onto
main. The reason we don't allow these revsets to resolve to multiple
commits by default is that we think users might specify multiple
commits by mistake. That's probably not much of a problem with `jj
rebase -b` (maybe we should always allow that to resolve to multiple
commits), but the user might want to know if `jj rebase -d @-`
resolves to multiple commits.

One problem with having a flag to allow multiple commits is that it
needs to be added to every command where we want to allow multiple
commits but default to one. Also, it should probably apply to each
revset argument those commands take. For example, even if the user
meant `-b main..` to resolve to multiple commits, they might not have
meant `-d main` to resolve to multiple commits (which it will in case
of a conflicted branch), so we might want separate
`--allow-large-revsets-in-destination` and
`--allow-large-revsets-in-source`, which gets quite cumbersome. It
seems better to have some syntax in the individual revsets for saying
that multiple commits are allowed.

One proposal I had was to use a `multiple()` revset function which
would have no effect in general but would be used as a marker if used
at the top level (e.g. `jj rebase -d 'multiple(@-)'`). After some
discussion on the PR adding that function (#1911), it seems that the
consensus is to instead use a prefix like `many:` or `all:`. That
avoids the problem with having a function that has no effect unless
it's used at the top level (`jj rebase -d 'multiple(x)|y'` would have
no effect).

Since we already have the `:` operator for DAG ranges, we need to
change it to make room for `many:`/`all:` syntax. This commit starts
that by allowing both `:` and `::`.

I have tried to update the documentation in this commit to either
mention both forms, or just the new and preferred `::` form. However,
it's useless to search for `:` in Rust code, so I'm sure I've missed
many instances. We'll have to address those as we notice them. I'll
let most tests use `:` until we deprecate it or delete it.
Copy link
Contributor

@ilyagr ilyagr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

I consider the "make --allow-large-revset no longer also allow duplicates" an experiment -- I think it might be better to always allow those, but merging it and testing it out might be easier than thinking it through.

The `--allow-large-revsets` flag we have on `jj rebase` and `jj new`
allows the user to do e.g. `jj rebase --allow-large-revsets -b
main.. -d main` to rebase all commits that are not in main onto
main. The reason we don't allow these revsets to resolve to multiple
commits by default is that we think users might specify multiple
commits by mistake. That's probably not much of a problem with `jj
rebase -b` (maybe we should always allow that to resolve to multiple
commits), but the user might want to know if `jj rebase -d @-`
resolves to multiple commits.

One problem with having a flag to allow multiple commits is that it
needs to be added to every command where we want to allow multiple
commits but default to one. Also, it should probably apply to each
revset argument those commands take. For example, even if the user
meant `-b main..` to resolve to multiple commits, they might not have
meant `-d main` to resolve to multiple commits (which it will in case
of a conflicted branch), so we might want separate
`--allow-large-revsets-in-destination` and
`--allow-large-revsets-in-source`, which gets quite cumbersome. It
seems better to have some syntax in the individual revsets for saying
that multiple commits are allowed.

One proposal I had was to use a `multiple()` revset function which
would have no effect in general but would be used as a marker if used
at the top level (e.g. `jj rebase -d 'multiple(@-)'`). After some
discussion on the PR adding that function (#1911), it seems that the
consensus is to instead use a prefix like `many:` or `all:`. That
avoids the problem with having a function that has no effect unless
it's used at the top level (`jj rebase -d 'multiple(x)|y'` would have
no effect).

Since we already have the `:` operator for DAG ranges, we need to
change it to make room for `many:`/`all:` syntax. This commit starts
that by allowing both `:` and `::`.

I have tried to update the documentation in this commit to either
mention both forms, or just the new and preferred `::` form. However,
it's useless to search for `:` in Rust code, so I'm sure I've missed
many instances. We'll have to address those as we notice them. I'll
let most tests use `:` until we deprecate it or delete it.
The `--allow-large-revset` option for `jj rebase` and `jj new` is used
for allowing a single revset to resolve to more than one destination
commit. It also means that duplicate commits between individual
revsets are allowed (e.g. `jj rebase -d x -d 'x|y'`). I'm about to
replace the first meaning of the flag by a revset function. I don't
think it's worth keeping the flag only for the second meaning, so I'm
just removing the feature instead. We can add it back under a
different name (`--allow-duplicate-destinations`?) if people care
about it.
See the earlier commit introducing the `::` operator for reasoning.
@martinvonz martinvonz enabled auto-merge (rebase) July 29, 2023 05:22
@martinvonz martinvonz merged commit 9c8d8b7 into main Jul 29, 2023
@martinvonz martinvonz deleted the push-rmonyruvtvuq branch July 29, 2023 05:30
martinvonz added a commit that referenced this pull request Jul 29, 2023
The `--allow-large-revsets` flag we have on `jj rebase` and `jj new`
allows the user to do e.g. `jj rebase --allow-large-revsets -b
main.. -d main` to rebase all commits that are not in main onto
main. The reason we don't allow these revsets to resolve to multiple
commits by default is that we think users might specify multiple
commits by mistake. That's probably not much of a problem with `jj
rebase -b` (maybe we should always allow that to resolve to multiple
commits), but the user might want to know if `jj rebase -d @-`
resolves to multiple commits.

One problem with having a flag to allow multiple commits is that it
needs to be added to every command where we want to allow multiple
commits but default to one. Also, it should probably apply to each
revset argument those commands take. For example, even if the user
meant `-b main..` to resolve to multiple commits, they might not have
meant `-d main` to resolve to multiple commits (which it will in case
of a conflicted branch), so we might want separate
`--allow-large-revsets-in-destination` and
`--allow-large-revsets-in-source`, which gets quite cumbersome. It
seems better to have some syntax in the individual revsets for saying
that multiple commits are allowed.

One proposal I had was to use a `multiple()` revset function which
would have no effect in general but would be used as a marker if used
at the top level (e.g. `jj rebase -d 'multiple(@-)'`). After some
discussion on the PR adding that function (#1911), it seems that the
consensus is to instead use a prefix like `many:` or `all:`. That
avoids the problem with having a function that has no effect unless
it's used at the top level (`jj rebase -d 'multiple(x)|y'` would have
no effect).

Since we already have the `:` operator for DAG ranges, we need to
change it to make room for `many:`/`all:` syntax. This commit starts
that by allowing both `:` and `::`.

I have tried to update the documentation in this commit to either
mention both forms, or just the new and preferred `::` form. However,
it's useless to search for `:` in Rust code, so I'm sure I've missed
many instances. We'll have to address those as we notice them. I'll
let most tests use `:` until we deprecate it or delete it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants