stow icon indicating copy to clipboard operation
stow copied to clipboard

Forcing a directory to be split open

Open anntzer opened this issue 6 years ago • 11 comments

(Initially posted on the help-stow mailing list.)

Like many others, I use stow to manage my dotfiles across multiple machines. In particular, a small exerpt of my stow tree includes

~/dotfiles/vim/plugin ~/dotfiles/vim/syntax ...

so I can just cd ~/dotfiles && stow -S vim after git-cloning dotfiles to a new machine.

But that'll just do a symlink ~/.vim -> ~/dotfiles/.vim, which is not what I want: this will cause e.g. ~/.vim/undo to end up in ~/dotfiles as well. So I always want to split open ~/.vim. In this specific case I could alternatively get away with gitignoring all other directories in ~/.vim, but this is less optimal for dotfiles which reside e.g. in ~/.config, as I really don't want ~/.config to be a symlink but rather ~/.config/foo to be one. So I need to first create ~/.vim myself and manually add a directory into it (e.g. ~/.vim/undo) before stowing my dotfiles, to force splitting that directory. It would be nice it it was possible to create e.g. ~/dotfiles/vim/.stow-force-split (name is up to bikeshedding) to say, "you are not allowed to symlink this directory directly, but must split it" (and that file shouldn't get symlinked itself).

Looking forward to your thoughts.

anntzer avatar Oct 04 '18 13:10 anntzer

Sounds like you are looking for --no-folding.

You can add that to a .stowrc file in your repository: https://github.com/FichteFoll/dotfiles/blob/master/.stowrc

FichteFoll avatar Oct 07 '18 17:10 FichteFoll

But (I think?) this completely disables folding, whereas I only want to disable it for specific directories.

anntzer avatar Oct 07 '18 18:10 anntzer

Yes, it completely disables folding (for the package you want to stow). I don't think there's a better way currently than pre-creating a fake directory tree that will cause stow to not fold certain directories.

FichteFoll avatar Oct 07 '18 18:10 FichteFoll

That's unfortunately correct. I recently uploaded the repository I use for this, so you can use that as an example for the workaround, but I agree it would be much nicer if Stow could be configured to never fold certain directories. Sadly I don't have time to implement this right now but I'd gladly review pull requests if someone else does.

aspiers avatar Jan 20 '19 18:01 aspiers

I'll share my solution. Normally I don't want directories to be folded: I want them to be created as actual directories, but I want some of them to be folded, e.g. dotfiles/vim/pack/*. Note that I don't want to apply folding to an entire stowed directory, but to a subdirectory.

I wrote this wrapper script that by default disabled foldins, allowing it for directory trees which begin in a directory containing a file named fold-here.

#!/bin/sh

set -e

stow=stow

if ! "$stow" -V >/dev/null 2>/dev/null; then
	echo "Not found: '$stow'" >&2
	return 1
fi

if [ "$#" -ne 1 ]; then
	echo "Usage: $0 <directory>" >&2
	return 1
fi

dir=$1

if [ ! -d "$dir" ]; then
	echo "Not found: '$dir'." >&2
	return 1
fi

if [ -z "$(find "$dir" -name fold-here)" ]; then
	# No "fold-here" file found, assume no-folding.
	$stow --no-folding "$dir"
else
	# Create the parent folders of the "fold-here" files.
	# The deepest "fold-here" in a tree wins.
	cd "$dir"
	find . -mindepth 2 -name fold-here -exec dirname {} + | xargs -I{} mkdir -vp ../../{}
	cd ..

	$stow --ignore="/fold-here$" "$dir"
fi

I keep this script in my dotfiles directory.

paride avatar Mar 12 '19 10:03 paride

The script is buggy. I wrote an improved version of it, but I'm not yet sure it is a good approach.

paride avatar Mar 12 '19 20:03 paride

I ended up with a simper (and more flexible) solution. I define the list of directories which should not be folded in a file named <package>/stow-skel.nostow, one per line, and then I stow using this wrapper script:

#!/bin/sh

set -e

for arg; do
    if [ -f "$arg/stow-skel.nostow" ]; then
        xargs -a "$arg/stow-skel.nostow" -I{} mkdir -pv ../{}
    fi
done

stow --ignore="\.nostow" $@

It would be very nice to have this functionality handled directly by stow; hopefully the hooks mentioned in the TODO file will allow to do something similar.

paride avatar Mar 12 '19 21:03 paride

FWIW, I have a similar but inverted use-case where I want most of my dotfiles to be non-folded; with some exceptions such as ~/.emacs.d which require folding to circumvent upstream bugs that arise when files are copied (i.e. symlinks are not expected there).

To achieve this, I adopted a similar strategy as @paride and added a hidden file called .fold in each dotfile directory which needs to be folded. During (re)stowing, the directories listed in .fold are deleted (with an interactive prompt before) such that they can be effectively folded upon (re)stowing. An example of .fold is shown below:

$ tree -a -L 2

...
├── emacs
│   ├── .emacs.d
│   ├── .fold
│   └── .spacemacs.d
...

The .fold file specifies which directories should be deleted in the target directory to allow for refolding later on.

$ cat emacs/.fold

.emacs.d
.spacemacs.d

The logic of all this is encoded in fold_stow, which is ultimately managed by a Makefile. The relevant portion of fold_stow is shown below, in case it is of help to anyone:

fold_stow() {
  # define local variabless
  local command="$1"
  local source="${2//\~/$HOME}"
  local destination="${3//\~/$HOME}"
  local dotfiles="$4"
  local dotfiles_nofold=()
  local dotfiles_fold=()

  # loop over files and execute logic
  for dotfile in $dotfiles; do
    # check for the existence of .fold
    src_dotfile="$source/$dotfile"
    if [ -f "$src_dotfile/.fold" ]; then
      # loop through all foldable directories
      for fold in $(xargs -a "$src_dotfile/.fold"); do
        src_fold_directory="$src_dotfile/$fold"
        dest_fold_directory="$destination/$fold"
        # prompt to delete directories if they are not symlinks
        if [[ -d "$src_fold_directory" && -d "$dest_fold_directory" && \
                ! -L "$dest_fold_directory" ]]; then
          read -rp "Delete $dest_fold_directory for folding? (y/N): " ans
          # only delete if answer is y or Y
          if [[ "$ans" == [yY] ]]; then
            rm -rf "$dest_fold_directory"
          fi
        fi
      done
      dotfiles_fold+=("$dotfile")
    else
      dotfiles_nofold+=("$dotfile")
    fi
  done

  # execute stow for folding directories
  if ((${#dotfiles_fold[@]})); then
    eval "$command -d $source -t $destination --ignore='^\.fold$' ${dotfiles_fold[*]}"
  fi

  # execute stow for --no-folding directories
  if ((${#dotfiles_nofold[@]})); then
    eval "$command -d $source -t $destination --no-folding ${dotfiles_nofold[*]}"
  fi
}

atreyasha avatar Jul 19 '21 17:07 atreyasha

In case anyone missed my comment above, my solution is here: https://github.com/aspiers/ANTIFOLD.

aspiers avatar Jul 19 '21 18:07 aspiers

Upon further thought, maybe this is a candidate to be added to the (non-existent) FAQ.

aspiers avatar Jul 19 '21 18:07 aspiers

I have started doing this, which seems to work so far, so it seems a lot simpler to do?

# Make some files to prevent folding
[ ! -d ~/.config ] && mkdir ~/.config
touch ~/.config/.stow-no-folding
[ ! -d ~/.local/share ] && mkdir -p ~/.local/share
touch ~/.local/share/.stow-no-folding
[ ! -d ~/.ssh ] && mkdir ~/.ssh
touch ~/.ssh/.stow-no-folding

stow --adopt -Rv -d ~/.dotfiles -t ~ stowpackage

--adopt has never worked for me, whatever I try though.

eggbean avatar Oct 25 '22 18:10 eggbean