twpayne / chezmoi

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

A subcommand about move or rename a managed file #2850

Closed sunn4room closed 1 year ago

sunn4room commented 1 year ago

When I want to rename a managed file, I always do this:

chezmoi forget xxx
mv xxx new-xxx
chezmoi add new-xxx

It's a little fussy, and I cannot find a appropriate subcommand with chezmoi help, and there is no useful message in github repo issues. Any advice?

twpayne commented 1 year ago

A rename subcommand is very difficult to implement, which is why chezmoi doesn't yet have one. Some explanation:

When I want to rename a managed file, I always do this:

chezmoi forget xxx
mv xxx new-xxx
chezmoi add new-xxx

This isn't quite correct as it only works on the current machine. If you have another machine with xxx already on it, then after you run chezmoi apply you will end up with both xxx and new-xxx. chezmoi will ignore the existing xxx (because you ran chezmoi forget xxx, so chezmoi won't touch it), but will add new-xxx (because you ran chezmoi add new-xxx).

To robustly rename xxx to new-xxx, you need to chezmoi add new-xxx add also ensure that xxx is removed. You can ensure that xxx is removed by either adding it to .chezmoiremove or creating an empty xxx file in your source state without the empty_ prefix (because chezmoi will remove empty files unless they have the empty_ prefix).

To implement a rename robustly, you not only have to cope with local renames, but also with the .chezmoiremove and .chezmoiignore files, both of which are templates and also support pattern matching.

So renaming xxx to new-xxx is not simple to implement, which is why you still have to do things manually.

Happy to hear ideas on how to implement a chezmoi mv/chezmoi rename command.

sunn4room commented 1 year ago

I find a file named '.chezmoistat.boltdb' in xdg config directory. If I'm right, this file record the status of applied files in a specific machine. When renamed a file in another machine, chezmoi can scan the applied files, and find the outdated files which is already not exists in source directory, then delete it at a right time. Is this idear useful?

Some dotfiles manager use symlink to sync dotfiles, but chezmoi use copy. It's a great idea. and it's meaningful to implement some useful features such as template and encrypt. But I think that .chezmoiremove is a little fussy. Chezmoi use apply to update specific dotfiles content to destination of local machine. It means that chezmoi has the right to manage the content of destination dotfiles. When the content changed, chezmoi should remind user to re-add or apply. But I cannot find a appropriate subcommand to release the right. Temporarily named release, when run chezmoi release xxx, chezmoi should delete the state in .chezmoistate.boltdb ( there is a subcommand chezmoi state ); When run chezmoi release -f xxx, chezmoi should also delete the destination file.

After implement release subcommand, it's natural to implement rename subcommand. After renamed a file in another machine, chezmoi can release the nonexisted files automatically.

halostatue commented 1 year ago

One would need to have a record of things that were in state but are no longer in state, that weren’t simply forgotten (e.g., chezmoi forget). With the sort of thing that you’re talking about, you could have a data loss situation:

  1. You chezmoi apply on machine1 and machine2.
  2. On machine1 you chezmoi forget something. This removes it from the chezmoi state and repo. You commit and push up the change.
  3. On machine2 you forget to chezmoi forget something, but instead do chezmoi apply. With what you’ve identified, this looks like a remove, so chezmoi removes something from machine2.

I’m talking about deletion, but just like git, chezmoi does not have a concept of a rename, only delete and add. (Renames are heuristically determined.)

Terraform has a similar issue, in that you can rename something in your definitions that requires you to manually record the rename or you will end up destroying and creating your resources. There are two ways to do this: terraform state mv OLDNAME NEWNAME or a moved block:

moved { 
  from = OLDNAME
  to = NEWNAME
}

With moved blocks, you can keep them around forever:

moved {
  from = OLDNAME
  to = NEWNAME
}

moved {
  from = NEWNAME
  to = NEWERNAME
}

moved {
  from = NEWERNAME
  to = NEWESTNAMEYET
}

chezmoi would need to implement such rename rules, but heuristic determination won’t be the answer. (If you do a git mv OLDNAME NEWNAME and then edit the file so that it is very different, instead of a rename in git status, you will see deleted OLDNAME and added NEWNAME.)

All this to say that (1) this isn’t easy, especially with multiple machines involved, (2) it requires explicit support to make possible, and (3) I don’t think that the effort expended on this would be worth the ~effort involved~ benefit received.