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
|
||||||
|
|
||||||
`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
|
## Key Features
|
||||||
|
|
||||||
- **Advanced Path Handling**: Robust support for dot syntax, home directory expansion, and both absolute and relative paths
|
- **Flexible Usage**: Works with any directories, not just config files
|
||||||
- **Flexible Usage**: Supports both dot syntax (.) and explicit source/target paths
|
- **Interactive Mode**: Option to interactively specify target directory
|
||||||
- **Symlink Management**: Creates and manages symlinks while maintaining correct directory structure
|
- **Recursive Processing**: Can process entire directory trees
|
||||||
- **Conflict Resolution**: Handles existing files and symlinks gracefully
|
- **Advanced Path Handling**: Supports home directory expansion and relative paths
|
||||||
- **Comprehensive Logging**: Detailed logging of all operations for tracking and debugging
|
- **Symlink Management**: Creates and manages symlinks while maintaining directory structure
|
||||||
|
- **Ignore Patterns**: Supports local and global ignore patterns
|
||||||
|
|
||||||
## Usage
|
## 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
|
```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
|
```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 .
|
guile -L . stash.scm .
|
||||||
```
|
```
|
||||||
|
|
||||||
When using the dot syntax (`.`), stash will:
|
## Common Use Cases
|
||||||
|
|
||||||
1. Use the current directory as the package directory
|
1. **Organizing Dotfiles**:
|
||||||
2. Use the parent directory as the stow directory
|
|
||||||
3. Create symlinks in the corresponding location under your home directory
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
1. Explicit paths:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Move ~/.config/rofi to ~/.dotfiles/.config and create symlink
|
# Move config files to dotfiles repo
|
||||||
guile -L . stash.scm --target=~/.dotfiles/.config --source=~/.config/rofi
|
guile -L . stash.scm --source ~/.config --target ~/.dotfiles/config --recursive
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Dot syntax:
|
2. **Backing Up Documents**:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Move to your package directory
|
# Move documents to external drive
|
||||||
cd ~/.dotfiles/.config/rofi
|
guile -L . stash.scm --source ~/Documents --target /media/backup/docs --recursive
|
||||||
# Create symlink at ~/.config/rofi
|
```
|
||||||
guile -L . stash.scm .
|
|
||||||
|
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
|
## Path Handling
|
||||||
|
|
||||||
Stash handles various path formats:
|
Stash handles various path formats:
|
||||||
|
|
||||||
- Absolute paths: `/home/user/.config`
|
- Absolute paths: `/home/user/documents`
|
||||||
- Relative paths: `../config`, `./config`
|
- Relative paths: `../documents`, `./notes`
|
||||||
- Home directory: `~/.config`
|
- Home directory: `~/documents`
|
||||||
- Dot notation: `.`, `..`
|
- Dot notation: `.`, `..`
|
||||||
|
|
||||||
## Ignore Patterns
|
## Ignore Patterns
|
||||||
|
|
||||||
Stash supports two types of ignore files:
|
Stash supports two types of ignore files:
|
||||||
|
|
||||||
- `.stash-local-ignore`: Package-specific ignore patterns
|
- `.stash-local-ignore`: Directory-specific ignore patterns
|
||||||
- `.stash-global-ignore`: Global ignore patterns for all packages
|
- `.stash-global-ignore`: Global ignore patterns
|
||||||
|
|
||||||
Default ignore patterns:
|
Default ignore patterns:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
#:use-module (stash log) ;; Import log-action, current-timestamp
|
#:use-module (stash log) ;; Import log-action, current-timestamp
|
||||||
#:use-module (stash paths) ;; Import expand-home, concat-path, ensure-config-path
|
#:use-module (stash paths) ;; Import expand-home, concat-path, ensure-config-path
|
||||||
#:use-module (stash conflict) ;; Import prompt-user-for-action, handle-conflict
|
#: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
|
;;; Helper function to quote shell arguments
|
||||||
(define (shell-quote-argument arg)
|
(define (shell-quote-argument arg)
|
||||||
|
|
@ -37,18 +37,66 @@
|
||||||
(handle-conflict target-source-dir source-dir delete-directory log-action) ;; Conflict handling
|
(handle-conflict target-source-dir source-dir delete-directory log-action) ;; Conflict handling
|
||||||
;; If the target directory doesn't exist, proceed with the move
|
;; If the target directory doesn't exist, proceed with the move
|
||||||
(begin
|
(begin
|
||||||
(rename-file source-dir target-source-dir)
|
;; Try rename-file first (fast but only works on same device)
|
||||||
(display (format #f "Moved ~a to ~a\n" source-dir target-source-dir))
|
(catch 'system-error
|
||||||
(log-action (format #f "Moved ~a to ~a" source-dir target-source-dir))))
|
(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
|
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
|
;;; Helper function to create a symlink
|
||||||
(define (create-symlink-in-parent source target)
|
(define (create-symlink source target)
|
||||||
"Create a symlink in the parent directory of the original source."
|
"Create a symlink from source to target."
|
||||||
(let ((parent-dir (dirname target)))
|
(when (file-exists? source)
|
||||||
;; Create parent directory if it doesn't exist
|
(delete-file source))
|
||||||
(if (not (file-exists? parent-dir))
|
(let ((source-dir (dirname source)))
|
||||||
(mkdir parent-dir #o755))
|
(when (not (file-exists? source-dir))
|
||||||
;; Create the symlink
|
(mkdir-p source-dir)))
|
||||||
(symlink source target)
|
(format #t "Creating symlink: ~a -> ~a~%" source target)
|
||||||
(log-action (format #f "Created symlink ~a -> ~a" target source))))
|
(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
|
;; stash-help.scm --- Help message module for Stash
|
||||||
|
|
||||||
(define-module (stash help)
|
(define-module (stash help)
|
||||||
#:export (display-help))
|
#:use-module (ice-9 format)
|
||||||
|
#:export (display-help
|
||||||
|
display-version))
|
||||||
|
|
||||||
;;; Function to display help message
|
;;; Function to display help message
|
||||||
(define (display-help)
|
(define (display-help)
|
||||||
"Display help message explaining how to use the program."
|
"Display help message explaining how to use the program."
|
||||||
(display "
|
(display "\
|
||||||
Usage: stash.scm [--source <source-dir> --target <target-dir>] [options]
|
Usage: stash.scm [OPTION...] [.]
|
||||||
stash.scm [-s <source-dir> -t <target-dir>] [options]
|
|
||||||
stash.scm .
|
|
||||||
|
|
||||||
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:
|
Options:
|
||||||
--source, -s Specify the source directory to be moved.
|
-s, --source=DIR Source directory to stash
|
||||||
--target, -t Specify the target directory where the symlink should be created.
|
-t, --target=DIR Target directory where files will be stashed
|
||||||
--version, -v Show the current version of the program.
|
-r, --recursive Recursively process directories under source
|
||||||
--help, -h Display this help message.
|
-i, --interactive Interactively prompt for target directory
|
||||||
--no-folding Disable directory tree folding.
|
-h, --help Display this help
|
||||||
--simulate Show what would happen without making changes.
|
-v, --version Display version information
|
||||||
--adopt Import existing files into the stow directory.
|
|
||||||
--restow Remove and recreate all symlinks.
|
|
||||||
--delete Remove all symlinks for a package.
|
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
1. Using explicit paths:
|
# Using dot syntax (after files are stashed):
|
||||||
guile -L . stash.scm --source ~/.config/test --target ~/.dotfiles/
|
cd ~/.dotfiles/config/nvim
|
||||||
guile -L . stash.scm -s ~/.config/test -t ~/.dotfiles/
|
stash.scm . # Creates symlink in ~/.config/nvim
|
||||||
|
|
||||||
2. Using dot syntax (like GNU Stow):
|
# Stash a single directory:
|
||||||
cd ~/.dotfiles/.config/test
|
stash.scm -s ~/Documents/notes -t ~/backup/notes # Move notes and create symlink
|
||||||
guile -L . stash.scm .
|
|
||||||
|
# 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
|
# Recursively stash an entire directory:
|
||||||
")
|
stash.scm -s ~/.config -t ~/.dotfiles/config -r # Stash all config files
|
||||||
(exit 0))
|
|
||||||
|
# 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)
|
(define (plan-operations tree package)
|
||||||
(let* ((source-base (package-path package))
|
(let* ((source-base (package-path package))
|
||||||
(target-base (package-target 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 "Source base: ~a~%" source-base)
|
||||||
(format #t "Target base: ~a~%" target-base)
|
(format #t "Target base: ~a~%" target-base)
|
||||||
(format #t "Source name: ~a~%" source-name)
|
(format #t "Source name: ~a~%" source-name)
|
||||||
|
(format #t "Target path: ~a~%" target-path)
|
||||||
|
|
||||||
;; Create symlink for the entire directory
|
;; Create parent directory if it doesn't exist
|
||||||
(let* ((target-path (string-append (getenv "HOME") "/.config/" source-name)))
|
(let ((target-dir (dirname target-path)))
|
||||||
(format #t "Target path: ~a~%" target-path)
|
(when (not (file-exists? target-dir))
|
||||||
|
(format #t "Creating parent directory: ~a~%" target-dir)
|
||||||
;; Create parent directory if it doesn't exist
|
(mkdir-p target-dir)))
|
||||||
(let ((target-dir (dirname target-path)))
|
|
||||||
(when (not (file-exists? target-dir))
|
;; Remove existing directory or file
|
||||||
(format #t "Creating parent directory: ~a~%" target-dir)
|
(when (file-exists? target-path)
|
||||||
(mkdir target-dir #o755)))
|
(format #t "Removing existing path: ~a~%" target-path)
|
||||||
|
(system (string-append "rm -rf " target-path)))
|
||||||
;; Remove existing directory or file
|
|
||||||
(when (file-exists? target-path)
|
;; Move source to target
|
||||||
(format #t "Removing existing path: ~a~%" target-path)
|
(format #t "Moving ~a to ~a~%" source-base target-path)
|
||||||
(system (string-append "rm -rf " target-path)))
|
(rename-file source-base target-path)
|
||||||
|
|
||||||
;; Create symlink
|
;; Create symlink
|
||||||
(let ((source-target (if (string-prefix? (getenv "HOME") source-base)
|
(format #t "Creating symlink: ~a -> ~a~%" source-base target-path)
|
||||||
(string-append target-base "/" source-name)
|
(symlink target-path source-base)
|
||||||
source-base)))
|
'()))
|
||||||
(format #t "Creating symlink: ~a -> ~a~%" target-path source-target)
|
|
||||||
(symlink source-target target-path)
|
|
||||||
'()))))
|
|
||||||
|
|
|
||||||
150
stash.scm
150
stash.scm
|
|
@ -35,6 +35,8 @@
|
||||||
|
|
||||||
(define-module (stash)
|
(define-module (stash)
|
||||||
#:use-module (ice-9 getopt-long)
|
#:use-module (ice-9 getopt-long)
|
||||||
|
#:use-module (ice-9 ftw)
|
||||||
|
#:use-module (ice-9 rdelim)
|
||||||
#:use-module (stash help)
|
#:use-module (stash help)
|
||||||
#:use-module (stash colors)
|
#:use-module (stash colors)
|
||||||
#:use-module (stash log)
|
#:use-module (stash log)
|
||||||
|
|
@ -46,28 +48,38 @@
|
||||||
#:use-module (srfi srfi-1)
|
#:use-module (srfi srfi-1)
|
||||||
#:use-module (srfi srfi-19))
|
#: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
|
;;; Command-line options
|
||||||
(define %options
|
(define %options
|
||||||
'((target (value #t) (single-char #\t))
|
'((target (value #t) (single-char #\t))
|
||||||
(source (value #t) (single-char #\s))
|
(source (value #t) (single-char #\s))
|
||||||
|
(recursive (value #f) (single-char #\r))
|
||||||
|
(interactive (value #f) (single-char #\i))
|
||||||
(help (value #f) (single-char #\h))
|
(help (value #f) (single-char #\h))
|
||||||
(version (value #f) (single-char #\v))
|
(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))))
|
|
||||||
|
|
||||||
;;; Function to handle dot directory stashing
|
;;; Function to handle dot directory stashing
|
||||||
(define (handle-dot-stash current-dir)
|
(define (handle-dot-stash)
|
||||||
"Handle stashing when using the '.' syntax. Uses parent as stow dir and $HOME as target."
|
"Handle stashing when using the '.' syntax. Uses parent directory as target."
|
||||||
(let* ((pkg-dir (canonicalize-path current-dir))
|
(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))
|
(stow-dir (dirname pkg-dir))
|
||||||
(home-dir (getenv "HOME")))
|
(target-dir (dirname stow-dir))
|
||||||
(format #t "pkg-dir: ~a~%" pkg-dir)
|
(ignore-patterns (read-ignore-patterns pkg-dir)))
|
||||||
(format #t "stow-dir: ~a~%" stow-dir)
|
(format #t "Package directory: ~a~%" pkg-dir)
|
||||||
(format #t "home-dir: ~a~%" home-dir)
|
(format #t "Stow directory: ~a~%" stow-dir)
|
||||||
(values pkg-dir stow-dir home-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
|
;;; Version function
|
||||||
(define (display-version)
|
(define (display-version)
|
||||||
|
|
@ -90,45 +102,95 @@
|
||||||
(no-folding? (assoc-ref options 'no-folding)))
|
(no-folding? (assoc-ref options 'no-folding)))
|
||||||
(plan-operations tree package)))
|
(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
|
;;; Main entry point
|
||||||
(define (main args)
|
(define (main args)
|
||||||
"Main function to parse arguments and execute the program."
|
"Main function to parse arguments and execute the program."
|
||||||
(setenv "GUILE_AUTO_COMPILE" "0")
|
(setenv "GUILE_AUTO_COMPILE" "0")
|
||||||
|
|
||||||
(let* ((options (getopt-long args %options))
|
(let* ((options (getopt-long args %options))
|
||||||
(source-dir (assoc-ref options 'source))
|
(help-wanted? (option-ref options 'help #f))
|
||||||
(target-dir (assoc-ref options 'target))
|
(version-wanted? (option-ref options 'version #f))
|
||||||
(non-option-args (option-ref options '() '())))
|
(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
|
(cond
|
||||||
((assoc-ref options 'help) (display-help))
|
(help-wanted? (display-help) (exit 0))
|
||||||
((assoc-ref options 'version) (display-version))
|
(version-wanted? (display-version) (exit 0))
|
||||||
;; Handle "stash ." syntax
|
|
||||||
((and (= (length non-option-args) 1)
|
;; Handle dot syntax
|
||||||
(string=? (car non-option-args) "."))
|
((and (= (length (option-ref options '() '())) 1)
|
||||||
(call-with-values
|
(string=? (car (option-ref options '() '())) "."))
|
||||||
(lambda () (handle-dot-stash (getcwd)))
|
(handle-dot-stash))
|
||||||
(lambda (pkg-dir stow-dir home-dir)
|
|
||||||
(let ((package (make-package
|
;; Handle interactive mode
|
||||||
(basename pkg-dir)
|
((and source interactive?)
|
||||||
pkg-dir
|
(let ((target-path (prompt-for-target (canonicalize-path source))))
|
||||||
home-dir
|
(handle-explicit-stash source target-path recursive?)))
|
||||||
(read-ignore-patterns pkg-dir))))
|
|
||||||
(handle-stow package options)))))
|
;; Handle explicit paths with optional recursion
|
||||||
;; Handle traditional --source --target syntax
|
((and source target)
|
||||||
((not (and target-dir source-dir))
|
(handle-explicit-stash source target recursive?))
|
||||||
(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))
|
|
||||||
(else
|
(else
|
||||||
(let* ((expanded-source (expand-home source-dir))
|
(display-help)
|
||||||
(expanded-target (expand-home target-dir))
|
(exit 1)))))
|
||||||
(package (make-package
|
|
||||||
(basename expanded-source)
|
(define (handle-explicit-stash source target recursive?)
|
||||||
expanded-source
|
"Handle stashing with explicit source and target paths."
|
||||||
expanded-target
|
(let* ((source-path (canonicalize-path source))
|
||||||
(read-ignore-patterns expanded-source))))
|
(target-path (canonicalize-path target))
|
||||||
(handle-stow package options))))))
|
(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
|
;; Entry point for stash
|
||||||
(main (command-line))
|
(main (command-line))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue