twpayne / chezmoi

Manage your dotfiles across multiple diverse machines, securely.
https://www.chezmoi.io/
MIT License
12.85k stars 477 forks source link

`chezmoi add` to allow trivial template updates via patching the diff #2601

Closed RuijieYu closed 1 year ago

RuijieYu commented 1 year ago

Is your feature request related to a problem? Please describe.

When editing the config file managed by a template file, usually the changes can be trivially applied to the source state. That is, when I make the following (trivial) changes to my config file .config/zathura/zathurarc, and then run chezmoi add, I am prompted that the source state is a templated file and chezmoi add would remove the template attribute. However, because the change is trivial, I would hope that this change can just be made on the source state as well.

diff --git a/.config/zathura/zathurarc b/.config/zathura/zathurarc
index d443a10fb0dcbdc2b836ed6d3e47030851a361b1..c266b81f7eb685736130f8689f16e6dfe6bc4342 100644
--- a/.config/zathura/zathurarc
+++ b/.config/zathura/zathurarc
@@ -4,6 +4,6 @@ set recolor-lightcolor "#000000"
 set recolor-keephue

 map <C-i> set recolor
-map <f8> set recolor
+map <F8> set recolor

 set first-page-column "1:1"

Describe the solution you'd like

Implement chezmoi add such that, or add a flag like --patch where, trivial changes to templated config files can be applied into the corresponding files in the source state.

If patching fails, I see two possible solutions: (1) ensure the source state is unchanged, and let the user determine how to update the source state; (2) if patch is used as mentioned below, keep the patched file and a reject file in the source state (or a temporary directory), and inform the user of these two files and allow the user to then update the source state manually.

Describe alternatives you've considered

On Unix contexts where patch is available, one could already do:

$ file=.config/zathura/zathurarc
$ chezmoi diff --reverse "$file" | patch "$(chezmoi source-path "$file")"

Additional context

None.

twpayne commented 1 year ago

chezmoi merge ?

twpayne commented 1 year ago

Implement chezmoi add such that, or add a flag like --patch where, trivial changes to templated config files can be applied into the corresponding files in the source state.

To add a bit more detail on this, I suspect that determining what is a "trivial change" and what is not is a very hard problem. For example given this source template:

one
{{ if .a -}}
two
{{ else if .b -}}
two
{{ end -}}
three

and this change to the file in your home directory:

 one
 two
+xyzzy
 three

where should the xyzzy line be added? Can you come up with an algorithm that does this reliably? For all templates?

This is why chezmoi add currently warns you instead of trying to preserve the template_ attribute, and why chezmoi re-add skips templates.

halostatue commented 1 year ago

I can think of an algorithm, but I wouldn’t want to implement it, TBH (it would be a lot of potentially fragile code), and it would only reliably work for templates that have a lot of common text in the beginning rather than templates that mix common text with template sections.

A given chunk would be auto-mergeable as long as no template tags have been seen prior to that chunk's range in the template file.

  1. A diff chunk parser would be required, or diff functionality would need to be implemented natively in chezmoi (no, thanks; I did that once in Ruby).
  2. It would abort at the first chunk that contains or comes after any template marker (complex because of template directives, which means that we would need to parse the whole thing to see if there are any template directives present).

Given your example, we could support

one
{{ if .a -}}
two
{{ else if .b -}}
two
{{ end -}}
three

with a change of

 one
+xyzzy
 two
 three

But nothing else. And, depending on how the diff actually shows up, we might not be able to support even that (it may only reliably work on larger blocks of static text where the chunk itself would be trivially parseable).

twpayne commented 1 year ago

Thanks for the discussion here. It is provably impossible to implement any such algorithm correctly.

For example, consider the diff

 alpha
+beta
 gamma

and the template

{{ if output "halts" "some-program" -}}
alpha
gamma
{{ else -}}
alpha
gamma
{{ end -}}

where the program halts is a program that returns a non-empty output if some-program always eventually terminates.

Adding beta to the correct place in the template requires halts to return the correct result for some-program, which it provably cannot.

Of course, some heuristics are possible, but these would likely take significantly more human time to implement than they would save