FR: `jj config unset [OPTIONS] <--user|--repo> <NAME>`
Is your feature request related to a problem? Please describe. Removing a configuration currently requires editing the file manually, having this command makes the removal of configs more convenient.
Describe the solution you'd like
Have a jj config subcommand that removes any set config by the given name.
Describe alternatives you've considered None
Additional context
This would give parity with git's config command, which likewise offers an unset subcommand
I have been lurking for a while now and this seems like a great opportunity to get involved.
Disclaimer: as a full-time working father of two my progress usually is slow but steady. In case that becomes a problem, let me know.
Do we want to follow git's behavior for unset; removing empty tables, which are the result of an unset?
Example
Before
[table]
foo = true
[another-table]
bar = true
Unset another-table.bar, which results in another-table being empty:
$ jj config unset another-table.bar
After
[table]
foo = true
..and should such removals recursively bubble up to the config root?
Before
[table]
foo = true
[another-table]
inline-table = { foo = false }
Unset another-table.inline-table.foo, which results in another-table.inline-table being empty, which in turn results in another-table being empty:
$ jj unset another-table.inline-table.foo
After
[table]
foo = true
To me this is the most intuitive, but I would like to confirm before spending time on implementing it.
There may be cases where an empty table is semantically meaningful. In particular, an inline empty table might indicate "enabled with default or no options". I've seen this pattern used productively in many domains.
Does Git have any cases where empty tables are semantically-meaningful? Actually – I guess in Git it's probably not possible to address a table by itself, but only its entries by pattern-matching on the key. It's more like they don't have tables in the actual data model, just keys with dots, and the table-like syntax is just shorthand, rather than representing a real nested object. That seems like a critical difference.
Likewise, Git represents lists with multivars, while TOML has real list objects. There's no empty list value in Git, while we can represent an empty list in jj. And it would probably be weird to automatically delete empty lists as the analogue to tables.
I don't know if jj has any cases of semantically-meaningful empty tables at present, but I could easily see it happening. I would advise to skip implementing the table deletion behavior for now and revisit later.
There may be cases where an empty table is semantically meaningful. In particular, an inline empty table might indicate "enabled with default or no options". I've seen this pattern used productively in many domains.
Thanks a lot for your quick feedback! I was not aware of that.
Does Git have any cases where empty tables are semantically-meaningful? Actually – I guess in Git it's probably not possible to address a table by itself, but only its entries by pattern-matching on the key. It's more like they don't have tables in the actual data model, just keys with dots, and the table-like syntax is just shorthand, rather than representing a real nested object. That seems like a critical difference.
I was just performing a manual test with a simple example by adding the following to ~/.gitconfig:
[table]
foo = true
..and running:
$ git config --global --unset table.foo
..which resulted in table being deleted in .gitconfig.
I don't know if jj has any cases of semantically-meaningful empty tables at present, but I could easily see it happening. I would advise to skip implementing the table deletion behavior for now and revisit later.
I am more than happy to skip the table deletion, as I was already starting to bang my head against the wall a little while spiking the implementation.
I was just performing a manual test
Not sure if I clearly expressed this: I think this is only an option for Git because the "tables" are not semantically meaningful as standalone entities, so Git can go ahead and delete the empty ones for convenience without issue. It makes sense for Git to do it for that reason [and then I explained why I believe that it doesn't make sense for TOML/us].
EDIT: I guess I didn't understand if you're providing evidence that Git does or doesn't have semantic tables when you wrote that, or if you were just providing more context/an example for the table deletion behavior in Git.
EDIT: I guess I didn't understand if you're providing evidence that Git does or doesn't have semantic tables when you wrote that, or if you were just providing more context/an example for the table deletion behavior in Git.
The latter.
I was just trying to describe how I came to the idea of removing empty tables (poorly, as it seems to me 😅).
I had a hunch, though, that I can't just deduct expected behavior for jj based on "this is how Git does it for that very simple case", hoping for someone (like you for instance) to come along to provide a more informed view.
So.. thanks again!
Naming question: do we prefer set/unset over set/delete?
One problem of jj config delete is that it could be confused with a command to delete config file.
I'm for set / unset, as you said delete can be confusing (alternatively, remove might work as a choice as well?)
I think set / unset is best.
Edit: because conceptually, it's not deleted since there are default values when config options are not in the config file.
@joyously raised an interesting question (https://github.com/martinvonz/jj/pull/4547#discussion_r1816930070): what about unsetting the same key twice in a row?
The current implementation returns a "key not found" error, because the first jj unset removes the key from the config. But what about unsetting a key twice, for which jj provides a default?
Let's say the user sets a value for git.push-bookmark-prefix in the user config.
Should running jj config --user unset git.push-bookmark-prefix twice really return an error?
I would expect it to silently fallback to jj's default value instead.
What do you think?
// edit
Maybe returning an error is actually ok. Since one must use either --user or --repo with jj unset, a "key not found" error would be semantically correct.