The '--dotfiles' option does not work as expected in conjunction with tree folding
Hello,
The behaviour of the '--dotfiles' option seems unexpected when stow folds subtrees containing 'dot-' prefixed files. It appears that tree folding takes precedence over '--dotfiles', which is unintuitive as we are explicitly requesting translation to a dotfile.
Steps to reproduce: $ tree -a ~ /home/user/ ... └── stow └── stow-dir └── pkg-dir └── dot-file
$ cd ~/stow && stow --dotfiles stow-dir
Expected behaviour: $ tree -a ~ /home/user/ ... ├── pkg-dir │ └── .file -> ../stow/stow-dir/pkg-dir/dot-file └── stow └── stow-dir └── pkg-dir └── dot-file
Actual behaviour: $ tree -a ~ /home/user/ ... ├── pkg-dir -> stow/stow-dir/pkg-dir └── stow └── stow-dir └── pkg-dir └── dot-file
Would it be possible to modify the implementation of '--dotfiles' or introduce an option to control this behaviour? While a workaround is to use '--no-folding', it is not ideal since you lose a lot of functionality. Another workaround is to manually create the 'pkg-dir' prior to stowing, which would then also result in the expected behaviour shown above.
Additionally, if this new behaviour is to be implemented, I'm assuming it would be impossible/out of scope to have 'pkg-dir' removed when unstowing with 'stow -D stow-dir'. This means that stow will leave empty directories after itself even when it "owns" everything, which I understand may not align with design guidelines. Should it be implemented however, a note in the '--dotfiles' documentation warning about this would likely also be needed.
Hi, thanks for the report. Please can you confirm which version you see this on?
Of course, I also forgot to mention that I'm using WSL2. I'm not sure if/how this affects stow, so if there's a user error on my part, feel free to let me know! I have tested on Debian and Ubuntu and the behavior is identical even though '--verbose' output is different between the stow versions.
On Debian in WSL2:
$ stow -V stow (GNU Stow) version 2.4.1
$ perl -v This is perl 5, version 36, subversion 0 (v5.36.0) built for x86_64-linux-gnu-thread-multi ...
$ hostnamectl ... Virtualization: wsl Operating System: Debian GNU/Linux 12 (bookworm) Kernel: Linux 5.15.167.4-microsoft-standard-WSL2 Architecture: x86-64
On Ubuntu in WSL2:
$ stow -V stow (GNU Stow) version 2.3.1
$ perl -v This is perl 5, version 38, subversion 2 (v5.38.2) built for x86_64-linux-gnu-thread-multi ...
$ hostnamectl ... Virtualization: wsl Operating System: Ubuntu 24.04.1 LTS Kernel: Linux 5.15.167.4-microsoft-standard-WSL2 Architecture: x86-64
Verbose outputs
--verbose=5 on Debian in WSL2:
$ stow --dotfiles --simulate --verbose=5 stow-dir stow dir is /home/user/stow stow dir path relative to target /home/user is stow Planning stow of: stow-dir ... cwd now /home/user | Joining: stow stow-dir | Final join: stow/stow-dir Planning stow of package stow-dir... | Joining: . .stow | Final join: .stow | Joining: . .nonstow | Final join: .nonstow . not protected; shouldn't skip Stowing contents of stow / stow-dir / . (cwd=/home/user) target subdir is . | Joining: stow stow-dir . | Final join: stow/stow-dir | Checking whether . is a current/planned node | link_task_action(.): no task | dir_task_action(.): no task | Joining: . | Final join: . | parent_link_scheduled_for_removal(.): prefix . | parent_link_scheduled_for_removal(.): returning false | is_a_node(.): really exists | Joining: . pkg-dir | Final join: pkg-dir | Joining: . pkg-dir | Final join: pkg-dir | Joining: stow stow-dir | Final join: stow/stow-dir | Joining: stow/stow-dir .stow-local-ignore | Final join: stow/stow-dir/.stow-local-ignore | Joining: /home/user .stow-global-ignore | Final join: /home/user/.stow-global-ignore stow/stow-dir/.stow-local-ignore didn't exist /home/user/.stow-global-ignore didn't exist Using built-in ignore list Ignore list regexp for paths: /(?^:(^|/)(^/.stow-local-ignore$|^/LICENSE.|^/COPYING|^/README.)(/|$))/ Ignore list regexp for segments: /(?^:^(CVS|.cvsignore|.svn|.+,v|.#.+|_darcs|#.*#|.gitmodules|.git|.+~|.gitignore|RCS|.hg)$)/ Not ignoring pkg-dir Stowing entry stow / stow-dir / pkg-dir | Joining: stow stow-dir pkg-dir | Final join: stow/stow-dir/pkg-dir level of pkg-dir is 0 | Joining: stow/stow-dir/pkg-dir | Final join: stow/stow-dir/pkg-dir link destination stow/stow-dir/pkg-dir is_a_link(pkg-dir) | link_task_action(pkg-dir): no task is_a_link(pkg-dir): returning 0 | Checking whether pkg-dir is a current/planned node | link_task_action(pkg-dir): no task | dir_task_action(pkg-dir): no task | Joining: pkg-dir | Final join: pkg-dir | parent_link_scheduled_for_removal(pkg-dir): prefix pkg-dir | parent_link_scheduled_for_removal(pkg-dir): returning false | is_a_node(pkg-dir): returning false LINK: pkg-dir => stow/stow-dir/pkg-dir Planning stow of package stow-dir... done cwd restored to /home/user/stow WARNING: in simulation mode so not modifying filesystem.
--verbose=5 on Ubuntu in WSL2:
$ stow --dotfiles --simulate --verbose=5 stow-dir stow dir is /home/user/stow stow dir path relative to target /home/user is stow cwd now /home/user cwd restored to /home/user/stow cwd now /home/user Planning stow of package stow-dir... . not protected Stowing contents of stow/stow-dir (cwd=/home/user) => stow/stow-dir is_a_node(.) link_task_action(.): no task dir_task_action(.): no task parent_link_scheduled_for_removal(.): prefix parent_link_scheduled_for_removal(.): returning false is_a_node(.): really exists stow/stow-dir/.stow-local-ignore didn't exist /home/user/.stow-global-ignore didn't exist Using built-in ignore list Ignore list regexp for paths: /(?^:(^|/)(^/COPYING|^/README.|^/.stow-local-ignore$|^/LICENSE.)(/|$))/ Ignore list regexp for segments: /(?^:^(.+,v|.hg|.gitignore|CVS|.#.+|.git|.cvsignore|RCS|_darcs|.+~|.svn|#.*#)$)/ Not ignoring pkg-dir Adjusting: pkg-dir => pkg-dir Stowing stow / stow-dir / pkg-dir => stow/stow-dir/pkg-dir is_a_link(pkg-dir) link_task_action(pkg-dir): no task is_a_link(pkg-dir): returning 0 is_a_node(pkg-dir) link_task_action(pkg-dir): no task dir_task_action(pkg-dir): no task parent_link_scheduled_for_removal(pkg-dir): prefix pkg-dir parent_link_scheduled_for_removal(pkg-dir): returning false is_a_node(pkg-dir): returning false LINK: pkg-dir => stow/stow-dir/pkg-dir Planning stow of package stow-dir... done cwd restored to /home/user/stow WARNING: in simulation mode so not modifying filesystem.
Much appreciated, especially with all the very helpful and well-structured detail. While anything prior to 2.4.0 can be flakey with respect to --dotfiles, it sounds like you have discovered a genuine bug here still present in 2.4.1. Hopefully it won't be too hard to figure out and fix given the work I did in 2.4.0 to clean up that code, but I won't know until I take a closer look.
Thank you for your quick response and for looking into it!