2

GNU Stow does not run properly if the target files already exist. So, i have written a script which will first delete all files and links in the target then run Stow.

#!/bin/bash

fd --hidden --ignore-file .gitignore --base-directory="$HOME/.dotfiles/.common-dotfiles" --type l --type f | sd ^. $HOME | xargs -I{} rm {}
stow --no-folding --target="$HOME" --dir="$HOME/.dotfiles" --restow .common-dotfiles

But my gut is telling me that this is not a good idea. What am I missing here? Is there any risk running this script? How can I improve this script?

Update 1:

GNU Stow create an exact copy of the source directory structure in the target. So, I first take the directory structure of the source. Change the path to match the target. Then run xargs -I{} rm {} on it.

I have broken the output of the pipe bellow.

First I am searching all the files in the $HOME/.dotfiles/.common-dotfiles directory.

% fd --hidden --ignore-file .gitignore --base-directory="$HOME/.dotfiles/.common-dotfiles" --type l --type f
.config/VSCodium/User/globalStorage/alefragnani.project-manager/projects.json
.config/VSCodium/User/globalStorage/state.vscdb
.config/VSCodium/User/keybindings.json
.config/VSCodium/User/settings.json
.config/VSCodium/User/snippets/c.json
.config/VSCodium/User/snippets/custom-snippets.code-snippets
.config/VSCodium/product.json

Now I am modifying the path using piping the output of previous command to sd ^. $HOME to match the target directory.

% fd --hidden --ignore-file .gitignore --base-directory="$HOME/.dotfiles/.common-dotfiles" --type l --type f | sd ^. $HOME
/home/ismail/.config/VSCodium/User/globalStorage/alefragnani.project-manager/projects.json
/home/ismail/.config/VSCodium/User/globalStorage/state.vscdb
/home/ismail/.config/VSCodium/User/keybindings.json
/home/ismail/.config/VSCodium/User/settings.json
/home/ismail/.config/VSCodium/User/snippets/c.json
/home/ismail/.config/VSCodium/User/snippets/custom-snippets.code-snippets
/home/ismail/.config/VSCodium/product.json

So, now I am no longer deleting files in $HOME/.dotfiles/.common-dotfiles but in $HOME.

Ahmad Ismail
  • 2,478
  • 1
  • 22
  • 47
  • 2
    In by experience stow creates and removes links. I never had a problem. – ctrl-alt-delor Jun 08 '22 at 12:35
  • This will break if any of your file names contain newlines. Probably not an issue for dotfiles, but might be worth thinking about. – terdon Jun 08 '22 at 12:39
  • @terdon how can I fix the new line issue? – Ahmad Ismail Jun 08 '22 at 12:50
  • Probably by using `find -type f -o type l -delete` instead of `fd`, but please [edit] your question and explain exactly what files should be removed and what files kept. I _think_ you want to delete every file or symlink under `$HOME/.dotfiles/.common-dotfiles` except `$HOME/.dotfiles/.common-dotfiles/.gitignore`, is that correct? – terdon Jun 08 '22 at 13:07
  • @terdon I have updated the question to explain what I am doing. – Ahmad Ismail Jun 08 '22 at 13:26
  • 1
    I don't want to post an answer because I don't know the first thing about `stow`, but this `find` command should remove the files you want to remove: `find . -maxdepth 1 \( -type f -o -type l \) -print0 | sed -z "s|^.|$HOME|" | xargs -0 -I{} rm {}`. ***test it before using!*** You can replace `rm` with `echo` to get the list of files that will be removed. Oh, and this assumes GNU tools pretty much in all steps. – terdon Jun 08 '22 at 15:27
  • Your question is vague. I've used `Stow` recently for the first time, and In *my case* it worked well: get the latest version of `mtr` on my system. It never occurred to me to try to run my `Stow` *installation* alongside the older ver of `mtr` until I read your question. I used `apt purge mtr` before I started with `Stow`. If you don't do this, I don't know if it will work, but then I don't know exactly what you mean by `target`. FWIW - I posted my [`Stow` recipe on GitHub](https://github.com/seamusdemora/PiFormulae/blob/master/InstallSoftwareWithStow.md). Thanks! – Seamus Jun 09 '22 at 01:36
  • I use Ansible and shell scripting to check for conflicting target files in my $HOME folder and delete them before running GNU Stow. [I explain my approach here.](https://unix.stackexchange.com/a/746076/572504) – NebulaSurfer May 17 '23 at 14:51

1 Answers1

1

I have done some research and found the answer in the documentation.

‘--adopt’

Warning! This behaviour is specifically intended to alter the contents of your stow directory. If you do not want that, this option is not for you.

When stowing, if a target is encountered which already exists but is a plain file (and hence not owned by any existing stow package), then normally Stow will register this as a conflict and refuse to proceed. This option changes that behaviour so that the file is moved to the same relative place within the package’s installation image within the stow directory, and then stowing proceeds as before. So effectively, the file becomes adopted by the stow package, without its contents changing.

This is particularly useful when the stow package is under the control of a version control system, because it allows files in the target tree, with potentially different contents to the equivalent versions in the stow package’s installation image, to be adopted into the package, then compared by running something like ‘git diff ...’ inside the stow package, and finally either kept (e.g. via ‘git commit ...’) or discarded (‘git checkout HEAD ...’).

So, what I should do is:

git checkout HEAD
stow --adopt --stow .common-dotfiles
git checkout HEAD
stow --restow .common-dotfiles

If we need to keep some changes, then we can run git diff, and resolve the merge conflicts after stow --adopt command.

In the above commands i use restore . instead of checkout HEAD.

Ahmad Ismail
  • 2,478
  • 1
  • 22
  • 47