mirror of https://codeberg.org/glenneth/stash.git
feat: add installation instructions and fix dot syntax stashing
- Add detailed installation instructions to README - Fix dot syntax stashing to use parent directory as target - Fix cross-filesystem stashing issues - Update test script to handle module paths correctly
This commit is contained in:
parent
269afd966d
commit
db11c09f3a
110
README.md
110
README.md
|
|
@ -1,71 +1,119 @@
|
|||
# Stash
|
||||
|
||||
`stash` is a command-line utility written in Guile Scheme that helps manage symbolic links (symlinks) for files and directories. This tool is inspired by GNU Stow but written in Guile Scheme. It provides advanced path handling and conflict resolution, allowing users to manage their dotfiles and configuration files effectively.
|
||||
`stash` is a command-line utility written in Guile Scheme that helps organize your files by moving them to a target location and creating symbolic links (symlinks) in their original location. While it's great for managing dotfiles, it works with any directories you want to organize.
|
||||
|
||||
## Installation
|
||||
|
||||
1. **Prerequisites**:
|
||||
- Guile Scheme 3.0.9 or later
|
||||
- A Unix-like environment (Linux/macOS)
|
||||
|
||||
2. **Installation Steps**:
|
||||
|
||||
```sh
|
||||
# Clone the repository
|
||||
git clone https://github.com/yourusername/stash.git
|
||||
cd stash
|
||||
|
||||
# Add to your ~/.guile load path
|
||||
mkdir -p ~/.guile.d/site/3.0
|
||||
ln -s $(pwd)/modules/stash ~/.guile.d/site/3.0/
|
||||
|
||||
# Optional: Add a convenient alias to your shell config (~/.bashrc or ~/.zshrc)
|
||||
echo 'alias stash="guile -L $(pwd) $(pwd)/stash.scm"' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
3. **Verify Installation**:
|
||||
|
||||
```sh
|
||||
# Test if stash works
|
||||
stash --help
|
||||
```
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Advanced Path Handling**: Robust support for dot syntax, home directory expansion, and both absolute and relative paths
|
||||
- **Flexible Usage**: Supports both dot syntax (.) and explicit source/target paths
|
||||
- **Symlink Management**: Creates and manages symlinks while maintaining correct directory structure
|
||||
- **Conflict Resolution**: Handles existing files and symlinks gracefully
|
||||
- **Comprehensive Logging**: Detailed logging of all operations for tracking and debugging
|
||||
- **Flexible Usage**: Works with any directories, not just config files
|
||||
- **Interactive Mode**: Option to interactively specify target directory
|
||||
- **Recursive Processing**: Can process entire directory trees
|
||||
- **Advanced Path Handling**: Supports home directory expansion and relative paths
|
||||
- **Symlink Management**: Creates and manages symlinks while maintaining directory structure
|
||||
- **Ignore Patterns**: Supports local and global ignore patterns
|
||||
|
||||
## Usage
|
||||
|
||||
There are two ways to use stash:
|
||||
Stash offers several ways to organize your files:
|
||||
|
||||
1. Using explicit source and target directories:
|
||||
1. **Interactive Mode** (easiest for beginners):
|
||||
|
||||
```sh
|
||||
guile -L . stash.scm --target=<target-dir> --source=<package-dir>
|
||||
# Move Pictures directory to a backup location
|
||||
guile -L . stash.scm --source ~/Pictures --interactive
|
||||
```
|
||||
|
||||
2. Using the dot syntax (similar to GNU Stow):
|
||||
2. **Explicit Paths**:
|
||||
|
||||
```sh
|
||||
cd ~/.dotfiles/.config/package
|
||||
# Move Documents to backup while keeping symlink
|
||||
guile -L . stash.scm --source ~/Documents/notes --target ~/backup/notes
|
||||
|
||||
# Move project to code archive
|
||||
guile -L . stash.scm --source ~/projects/webapp --target ~/code/archive/webapp
|
||||
```
|
||||
|
||||
3. **Recursive Mode** (for entire directory trees):
|
||||
|
||||
```sh
|
||||
# Archive entire projects directory
|
||||
guile -L . stash.scm --source ~/projects --target ~/archive/projects --recursive
|
||||
```
|
||||
|
||||
4. **Dot Syntax** (after files are stashed):
|
||||
|
||||
```sh
|
||||
# Recreate symlink for previously stashed directory
|
||||
cd ~/backup/notes
|
||||
guile -L . stash.scm .
|
||||
```
|
||||
|
||||
When using the dot syntax (`.`), stash will:
|
||||
## Common Use Cases
|
||||
|
||||
1. Use the current directory as the package directory
|
||||
2. Use the parent directory as the stow directory
|
||||
3. Create symlinks in the corresponding location under your home directory
|
||||
|
||||
### Examples
|
||||
|
||||
1. Explicit paths:
|
||||
1. **Organizing Dotfiles**:
|
||||
|
||||
```sh
|
||||
# Move ~/.config/rofi to ~/.dotfiles/.config and create symlink
|
||||
guile -L . stash.scm --target=~/.dotfiles/.config --source=~/.config/rofi
|
||||
# Move config files to dotfiles repo
|
||||
guile -L . stash.scm --source ~/.config --target ~/.dotfiles/config --recursive
|
||||
```
|
||||
|
||||
2. Dot syntax:
|
||||
2. **Backing Up Documents**:
|
||||
|
||||
```sh
|
||||
# Move to your package directory
|
||||
cd ~/.dotfiles/.config/rofi
|
||||
# Create symlink at ~/.config/rofi
|
||||
guile -L . stash.scm .
|
||||
# Move documents to external drive
|
||||
guile -L . stash.scm --source ~/Documents --target /media/backup/docs --recursive
|
||||
```
|
||||
|
||||
3. **Project Organization**:
|
||||
|
||||
```sh
|
||||
# Archive old project while keeping it accessible
|
||||
guile -L . stash.scm --source ~/projects/old-app --target ~/archive/projects/old-app
|
||||
```
|
||||
|
||||
## Path Handling
|
||||
|
||||
Stash handles various path formats:
|
||||
|
||||
- Absolute paths: `/home/user/.config`
|
||||
- Relative paths: `../config`, `./config`
|
||||
- Home directory: `~/.config`
|
||||
- Absolute paths: `/home/user/documents`
|
||||
- Relative paths: `../documents`, `./notes`
|
||||
- Home directory: `~/documents`
|
||||
- Dot notation: `.`, `..`
|
||||
|
||||
## Ignore Patterns
|
||||
|
||||
Stash supports two types of ignore files:
|
||||
|
||||
- `.stash-local-ignore`: Package-specific ignore patterns
|
||||
- `.stash-global-ignore`: Global ignore patterns for all packages
|
||||
- `.stash-local-ignore`: Directory-specific ignore patterns
|
||||
- `.stash-global-ignore`: Global ignore patterns
|
||||
|
||||
Default ignore patterns:
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#:use-module (stash log) ;; Import log-action, current-timestamp
|
||||
#:use-module (stash paths) ;; Import expand-home, concat-path, ensure-config-path
|
||||
#:use-module (stash conflict) ;; Import prompt-user-for-action, handle-conflict
|
||||
#:export (move-source-to-target create-symlink-in-parent delete-directory))
|
||||
#:export (move-source-to-target create-symlink delete-directory mkdir-p execute-operations file-is-symlink?))
|
||||
|
||||
;;; Helper function to quote shell arguments
|
||||
(define (shell-quote-argument arg)
|
||||
|
|
@ -37,18 +37,66 @@
|
|||
(handle-conflict target-source-dir source-dir delete-directory log-action) ;; Conflict handling
|
||||
;; If the target directory doesn't exist, proceed with the move
|
||||
(begin
|
||||
(rename-file source-dir target-source-dir)
|
||||
(display (format #f "Moved ~a to ~a\n" source-dir target-source-dir))
|
||||
(log-action (format #f "Moved ~a to ~a" source-dir target-source-dir))))
|
||||
;; Try rename-file first (fast but only works on same device)
|
||||
(catch 'system-error
|
||||
(lambda ()
|
||||
(rename-file source-dir target-source-dir)
|
||||
(display (format #f "Moved ~a to ~a\n" source-dir target-source-dir))
|
||||
(log-action (format #f "Moved ~a to ~a" source-dir target-source-dir)))
|
||||
(lambda args
|
||||
;; If rename-file fails, fall back to cp -R and rm -rf
|
||||
(system (string-append "cp -R " (shell-quote-argument source-dir) " " (shell-quote-argument target-source-dir)))
|
||||
(system (string-append "rm -rf " (shell-quote-argument source-dir)))
|
||||
(display (format #f "Moved (via copy) ~a to ~a\n" source-dir target-source-dir))
|
||||
(log-action (format #f "Moved (via copy) ~a to ~a" source-dir target-source-dir))))))
|
||||
target-source-dir)) ;; Return the path of the moved source directory
|
||||
|
||||
;;; Helper function to create a symlink in the parent directory of the original source
|
||||
(define (create-symlink-in-parent source target)
|
||||
"Create a symlink in the parent directory of the original source."
|
||||
(let ((parent-dir (dirname target)))
|
||||
;; Create parent directory if it doesn't exist
|
||||
(if (not (file-exists? parent-dir))
|
||||
(mkdir parent-dir #o755))
|
||||
;; Create the symlink
|
||||
(symlink source target)
|
||||
(log-action (format #f "Created symlink ~a -> ~a" target source))))
|
||||
;;; Helper function to create a symlink
|
||||
(define (create-symlink source target)
|
||||
"Create a symlink from source to target."
|
||||
(when (file-exists? source)
|
||||
(delete-file source))
|
||||
(let ((source-dir (dirname source)))
|
||||
(when (not (file-exists? source-dir))
|
||||
(mkdir-p source-dir)))
|
||||
(format #t "Creating symlink: ~a -> ~a~%" source target)
|
||||
(symlink target source)
|
||||
(log-action (format #f "Created symlink ~a -> ~a" source target)))
|
||||
|
||||
;;; Helper function to check if a path is a symlink
|
||||
(define (file-is-symlink? path)
|
||||
"Check if a path is a symbolic link."
|
||||
(catch 'system-error
|
||||
(lambda ()
|
||||
(eq? 'symlink (stat:type (lstat path))))
|
||||
(lambda args #f)))
|
||||
|
||||
;;; Helper function to create parent directories recursively
|
||||
(define (mkdir-p path)
|
||||
"Create directory and parent directories if they don't exist."
|
||||
(display (format #f "Creating parent directory: ~a\n" path))
|
||||
(let ((parent (dirname path)))
|
||||
(when (not (file-exists? parent))
|
||||
(mkdir-p parent))
|
||||
(when (not (file-exists? path))
|
||||
(mkdir path #o755))))
|
||||
|
||||
;;; Helper function to execute operations
|
||||
(define (execute-operations operations)
|
||||
"Execute a list of operations."
|
||||
(for-each
|
||||
(lambda (op)
|
||||
(case (car op)
|
||||
((mkdir) (mkdir-p (cadr op)))
|
||||
((symlink) (create-symlink (cadr op) (caddr op)))
|
||||
((move) (move-source-to-target (cadr op) (caddr op)))
|
||||
((delete) (delete-directory (cadr op)))
|
||||
(else (display (format #f "Unknown operation: ~a\n" op)))))
|
||||
operations))
|
||||
|
||||
;;; Export list
|
||||
(export mkdir-p
|
||||
execute-operations
|
||||
move-source-to-target
|
||||
create-symlink
|
||||
file-is-symlink?)
|
||||
|
|
|
|||
|
|
@ -1,38 +1,50 @@
|
|||
;; stash-help.scm --- Help message module for Stash
|
||||
|
||||
(define-module (stash help)
|
||||
#:export (display-help))
|
||||
#:use-module (ice-9 format)
|
||||
#:export (display-help
|
||||
display-version))
|
||||
|
||||
;;; Function to display help message
|
||||
(define (display-help)
|
||||
"Display help message explaining how to use the program."
|
||||
(display "
|
||||
Usage: stash.scm [--source <source-dir> --target <target-dir>] [options]
|
||||
stash.scm [-s <source-dir> -t <target-dir>] [options]
|
||||
stash.scm .
|
||||
(display "\
|
||||
Usage: stash.scm [OPTION...] [.]
|
||||
|
||||
Stash is a Guile Scheme utility for managing symlinks with conflict resolution.
|
||||
Stash is a symlink management utility that helps organize your files by moving them
|
||||
to a target location and creating symbolic links in their original location.
|
||||
|
||||
Options:
|
||||
--source, -s Specify the source directory to be moved.
|
||||
--target, -t Specify the target directory where the symlink should be created.
|
||||
--version, -v Show the current version of the program.
|
||||
--help, -h Display this help message.
|
||||
--no-folding Disable directory tree folding.
|
||||
--simulate Show what would happen without making changes.
|
||||
--adopt Import existing files into the stow directory.
|
||||
--restow Remove and recreate all symlinks.
|
||||
--delete Remove all symlinks for a package.
|
||||
-s, --source=DIR Source directory to stash
|
||||
-t, --target=DIR Target directory where files will be stashed
|
||||
-r, --recursive Recursively process directories under source
|
||||
-i, --interactive Interactively prompt for target directory
|
||||
-h, --help Display this help
|
||||
-v, --version Display version information
|
||||
|
||||
Examples:
|
||||
1. Using explicit paths:
|
||||
guile -L . stash.scm --source ~/.config/test --target ~/.dotfiles/
|
||||
guile -L . stash.scm -s ~/.config/test -t ~/.dotfiles/
|
||||
# Using dot syntax (after files are stashed):
|
||||
cd ~/.dotfiles/config/nvim
|
||||
stash.scm . # Creates symlink in ~/.config/nvim
|
||||
|
||||
2. Using dot syntax (like GNU Stow):
|
||||
cd ~/.dotfiles/.config/test
|
||||
guile -L . stash.scm .
|
||||
# Stash a single directory:
|
||||
stash.scm -s ~/Documents/notes -t ~/backup/notes # Move notes and create symlink
|
||||
|
||||
# Stash with interactive target selection:
|
||||
stash.scm -s ~/Pictures -i # Will prompt for target directory
|
||||
|
||||
This will create symlinks in ~/.config/test pointing to ~/.dotfiles/.config/test
|
||||
")
|
||||
(exit 0))
|
||||
# Recursively stash an entire directory:
|
||||
stash.scm -s ~/.config -t ~/.dotfiles/config -r # Stash all config files
|
||||
|
||||
# Stash any directory to any location:
|
||||
stash.scm -s ~/projects/web -t ~/backup/code/web # Not limited to dotfiles
|
||||
|
||||
Note: Stash works with any directories, not just config files or dotfiles.
|
||||
You can use it to organize any files by moving them to a backup/storage
|
||||
location while maintaining easy access through symlinks.
|
||||
|
||||
For more information, visit: https://codeberg.org/glenneth/stash
|
||||
"))
|
||||
|
||||
(define (display-version)
|
||||
(display "stash version 0.1.0-alpha.1\n"))
|
||||
|
|
|
|||
|
|
@ -62,30 +62,29 @@
|
|||
(define (plan-operations tree package)
|
||||
(let* ((source-base (package-path package))
|
||||
(target-base (package-target package))
|
||||
(source-name (basename source-base)))
|
||||
(source-name (package-name package))
|
||||
(target-path (string-append target-base "/" source-name)))
|
||||
(format #t "Source base: ~a~%" source-base)
|
||||
(format #t "Target base: ~a~%" target-base)
|
||||
(format #t "Source name: ~a~%" source-name)
|
||||
(format #t "Target path: ~a~%" target-path)
|
||||
|
||||
;; Create symlink for the entire directory
|
||||
(let* ((target-path (string-append (getenv "HOME") "/.config/" source-name)))
|
||||
(format #t "Target path: ~a~%" target-path)
|
||||
|
||||
;; Create parent directory if it doesn't exist
|
||||
(let ((target-dir (dirname target-path)))
|
||||
(when (not (file-exists? target-dir))
|
||||
(format #t "Creating parent directory: ~a~%" target-dir)
|
||||
(mkdir target-dir #o755)))
|
||||
|
||||
;; Remove existing directory or file
|
||||
(when (file-exists? target-path)
|
||||
(format #t "Removing existing path: ~a~%" target-path)
|
||||
(system (string-append "rm -rf " target-path)))
|
||||
|
||||
;; Create symlink
|
||||
(let ((source-target (if (string-prefix? (getenv "HOME") source-base)
|
||||
(string-append target-base "/" source-name)
|
||||
source-base)))
|
||||
(format #t "Creating symlink: ~a -> ~a~%" target-path source-target)
|
||||
(symlink source-target target-path)
|
||||
'()))))
|
||||
;; Create parent directory if it doesn't exist
|
||||
(let ((target-dir (dirname target-path)))
|
||||
(when (not (file-exists? target-dir))
|
||||
(format #t "Creating parent directory: ~a~%" target-dir)
|
||||
(mkdir-p target-dir)))
|
||||
|
||||
;; Remove existing directory or file
|
||||
(when (file-exists? target-path)
|
||||
(format #t "Removing existing path: ~a~%" target-path)
|
||||
(system (string-append "rm -rf " target-path)))
|
||||
|
||||
;; Move source to target
|
||||
(format #t "Moving ~a to ~a~%" source-base target-path)
|
||||
(rename-file source-base target-path)
|
||||
|
||||
;; Create symlink
|
||||
(format #t "Creating symlink: ~a -> ~a~%" source-base target-path)
|
||||
(symlink target-path source-base)
|
||||
'()))
|
||||
|
|
|
|||
150
stash.scm
150
stash.scm
|
|
@ -35,6 +35,8 @@
|
|||
|
||||
(define-module (stash)
|
||||
#:use-module (ice-9 getopt-long)
|
||||
#:use-module (ice-9 ftw)
|
||||
#:use-module (ice-9 rdelim)
|
||||
#:use-module (stash help)
|
||||
#:use-module (stash colors)
|
||||
#:use-module (stash log)
|
||||
|
|
@ -46,28 +48,38 @@
|
|||
#:use-module (srfi srfi-1)
|
||||
#:use-module (srfi srfi-19))
|
||||
|
||||
;;; Color constants
|
||||
(define blue-text "\x1b[0;34m")
|
||||
(define yellow-text "\x1b[0;33m")
|
||||
(define red-text "\x1b[0;31m")
|
||||
|
||||
;;; Command-line options
|
||||
(define %options
|
||||
'((target (value #t) (single-char #\t))
|
||||
(source (value #t) (single-char #\s))
|
||||
(recursive (value #f) (single-char #\r))
|
||||
(interactive (value #f) (single-char #\i))
|
||||
(help (value #f) (single-char #\h))
|
||||
(version (value #f) (single-char #\v))
|
||||
(no-folding (value #f) (single-char #\n))
|
||||
(simulate (value #f) (single-char #\S))
|
||||
(adopt (value #f) (single-char #\a))
|
||||
(restow (value #f) (single-char #\R))
|
||||
(delete (value #f) (single-char #\D))))
|
||||
(version (value #f) (single-char #\v))))
|
||||
|
||||
;;; Function to handle dot directory stashing
|
||||
(define (handle-dot-stash current-dir)
|
||||
"Handle stashing when using the '.' syntax. Uses parent as stow dir and $HOME as target."
|
||||
(let* ((pkg-dir (canonicalize-path current-dir))
|
||||
(define (handle-dot-stash)
|
||||
"Handle stashing when using the '.' syntax. Uses parent directory as target."
|
||||
(let* ((current-dir (canonicalize-path (getcwd)))
|
||||
(pkg-dir (if (file-is-symlink? current-dir)
|
||||
(canonicalize-path (readlink current-dir))
|
||||
current-dir))
|
||||
(pkg-name (basename pkg-dir))
|
||||
(stow-dir (dirname pkg-dir))
|
||||
(home-dir (getenv "HOME")))
|
||||
(format #t "pkg-dir: ~a~%" pkg-dir)
|
||||
(format #t "stow-dir: ~a~%" stow-dir)
|
||||
(format #t "home-dir: ~a~%" home-dir)
|
||||
(values pkg-dir stow-dir home-dir)))
|
||||
(target-dir (dirname stow-dir))
|
||||
(ignore-patterns (read-ignore-patterns pkg-dir)))
|
||||
(format #t "Package directory: ~a~%" pkg-dir)
|
||||
(format #t "Stow directory: ~a~%" stow-dir)
|
||||
(format #t "Target directory: ~a~%" target-dir)
|
||||
(if (file-is-symlink? current-dir)
|
||||
(format #t "Directory is already stashed at: ~a~%" pkg-dir)
|
||||
(let ((package (make-package pkg-name pkg-dir target-dir ignore-patterns)))
|
||||
(process-package package)))))
|
||||
|
||||
;;; Version function
|
||||
(define (display-version)
|
||||
|
|
@ -90,45 +102,95 @@
|
|||
(no-folding? (assoc-ref options 'no-folding)))
|
||||
(plan-operations tree package)))
|
||||
|
||||
;;; Prompt user for target directory path
|
||||
(define (prompt-for-target source-path)
|
||||
"Prompt user for target directory path."
|
||||
(display (color-message (string-append "\nSource directory: " source-path "\n") blue-text))
|
||||
(display (color-message "Enter target directory path (where files will be stashed): " yellow-text))
|
||||
(let ((input (read-line)))
|
||||
(if (string-null? input)
|
||||
(begin
|
||||
(display (color-message "Target directory cannot be empty. Please try again.\n" red-text))
|
||||
(prompt-for-target source-path))
|
||||
(canonicalize-path (expand-home input)))))
|
||||
|
||||
;;; Main entry point
|
||||
(define (main args)
|
||||
"Main function to parse arguments and execute the program."
|
||||
(setenv "GUILE_AUTO_COMPILE" "0")
|
||||
|
||||
(let* ((options (getopt-long args %options))
|
||||
(source-dir (assoc-ref options 'source))
|
||||
(target-dir (assoc-ref options 'target))
|
||||
(non-option-args (option-ref options '() '())))
|
||||
(help-wanted? (option-ref options 'help #f))
|
||||
(version-wanted? (option-ref options 'version #f))
|
||||
(recursive? (option-ref options 'recursive #f))
|
||||
(interactive? (option-ref options 'interactive #f))
|
||||
(source (option-ref options 'source #f))
|
||||
(target (option-ref options 'target #f)))
|
||||
|
||||
(cond
|
||||
((assoc-ref options 'help) (display-help))
|
||||
((assoc-ref options 'version) (display-version))
|
||||
;; Handle "stash ." syntax
|
||||
((and (= (length non-option-args) 1)
|
||||
(string=? (car non-option-args) "."))
|
||||
(call-with-values
|
||||
(lambda () (handle-dot-stash (getcwd)))
|
||||
(lambda (pkg-dir stow-dir home-dir)
|
||||
(let ((package (make-package
|
||||
(basename pkg-dir)
|
||||
pkg-dir
|
||||
home-dir
|
||||
(read-ignore-patterns pkg-dir))))
|
||||
(handle-stow package options)))))
|
||||
;; Handle traditional --source --target syntax
|
||||
((not (and target-dir source-dir))
|
||||
(display (color-message "Error: Either use '.' in a package directory or provide both --source and --target arguments.\n" red-text))
|
||||
(display (color-message "Usage: stash.scm [--target <target-dir> --source <source-dir>] | [.]\n" yellow-text))
|
||||
(exit 1))
|
||||
(help-wanted? (display-help) (exit 0))
|
||||
(version-wanted? (display-version) (exit 0))
|
||||
|
||||
;; Handle dot syntax
|
||||
((and (= (length (option-ref options '() '())) 1)
|
||||
(string=? (car (option-ref options '() '())) "."))
|
||||
(handle-dot-stash))
|
||||
|
||||
;; Handle interactive mode
|
||||
((and source interactive?)
|
||||
(let ((target-path (prompt-for-target (canonicalize-path source))))
|
||||
(handle-explicit-stash source target-path recursive?)))
|
||||
|
||||
;; Handle explicit paths with optional recursion
|
||||
((and source target)
|
||||
(handle-explicit-stash source target recursive?))
|
||||
|
||||
(else
|
||||
(let* ((expanded-source (expand-home source-dir))
|
||||
(expanded-target (expand-home target-dir))
|
||||
(package (make-package
|
||||
(basename expanded-source)
|
||||
expanded-source
|
||||
expanded-target
|
||||
(read-ignore-patterns expanded-source))))
|
||||
(handle-stow package options))))))
|
||||
(display-help)
|
||||
(exit 1)))))
|
||||
|
||||
(define (handle-explicit-stash source target recursive?)
|
||||
"Handle stashing with explicit source and target paths."
|
||||
(let* ((source-path (canonicalize-path source))
|
||||
(target-path (canonicalize-path target))
|
||||
(package-name (basename source-path))
|
||||
(ignore-patterns (read-ignore-patterns source-path)))
|
||||
(if recursive?
|
||||
(handle-recursive-stash source-path target-path)
|
||||
(let ((package (make-package package-name source-path target-path ignore-patterns)))
|
||||
(process-package package)))))
|
||||
|
||||
(define (handle-recursive-stash source target)
|
||||
"Recursively process directories under source."
|
||||
(let* ((source-path (canonicalize-path source))
|
||||
(target-path (canonicalize-path target))
|
||||
(source-name (basename source-path))
|
||||
(target-config-path (string-append target-path "/" source-name))
|
||||
(entries (if (file-is-directory? source-path)
|
||||
(scandir source-path)
|
||||
(list (basename source-path))))
|
||||
(valid-entries (filter (lambda (entry)
|
||||
(and (not (member entry '("." "..")))
|
||||
(file-is-directory? (string-append source-path "/" entry))))
|
||||
entries)))
|
||||
;; First ensure the config directory exists in target
|
||||
(if (not (file-exists? target-config-path))
|
||||
(mkdir-p target-config-path))
|
||||
;; Then process each subdirectory
|
||||
(for-each
|
||||
(lambda (entry)
|
||||
(let* ((source-dir (string-append source-path "/" entry))
|
||||
(package-name entry)
|
||||
(ignore-patterns (read-ignore-patterns source-dir)))
|
||||
(let ((package (make-package package-name source-dir target-config-path ignore-patterns)))
|
||||
(process-package package))))
|
||||
valid-entries)))
|
||||
|
||||
(define (process-package package)
|
||||
"Process a single package for stashing."
|
||||
(let* ((tree (analyze-tree package))
|
||||
(operations (plan-operations tree package)))
|
||||
(execute-operations operations)))
|
||||
|
||||
;; Entry point for stash
|
||||
(main (command-line))
|
||||
|
|
|
|||
Loading…
Reference in New Issue