mirror of
https://github.com/yarlson/lnk.git
synced 2025-08-29 17:49:47 +02:00
feat: simplify README and improve init command
This commit is contained in:
406
README.md
406
README.md
@@ -1,387 +1,165 @@
|
|||||||
# Lnk
|
# Lnk
|
||||||
|
|
||||||
**The missing middle: Safer than simple, simpler than complex.**
|
**Git-native dotfiles management that doesn't suck.**
|
||||||
|
|
||||||
Git-native dotfiles management that won't break your setup. Zero config, zero bloat, zero surprises.
|
Move your dotfiles to `~/.config/lnk`, symlink them back, and use Git like normal. That's it.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# The power of Git, the safety of proper engineering
|
lnk init
|
||||||
lnk init && lnk add ~/.vimrc && lnk push
|
lnk add ~/.vimrc ~/.bashrc
|
||||||
|
lnk push "setup"
|
||||||
```
|
```
|
||||||
|
|
||||||
[](./test) [](https://golang.org) [](LICENSE)
|
## Install
|
||||||
|
|
||||||
## Why Lnk?
|
|
||||||
|
|
||||||
**The dotfiles manager that fills the missing gap.**
|
|
||||||
|
|
||||||
While chezmoi offers 100+ features and Home Manager requires learning Nix, **Lnk focuses on doing the essentials perfectly**:
|
|
||||||
|
|
||||||
- 🎯 **Safe simplicity**: More robust than Dotbot, simpler than chezmoi
|
|
||||||
- 🛡️ **Bulletproof operations**: Comprehensive edge case handling (unlike minimal tools)
|
|
||||||
- ⚡ **Zero friction**: No YAML configs, no templates, no learning curve
|
|
||||||
- 🔧 **Git-native**: Clean commits, standard workflow, no abstractions
|
|
||||||
- 📦 **Zero dependencies**: Single binary vs Python/Node/Ruby runtimes
|
|
||||||
- 🚀 **Production ready**: 20 integration tests, proper error handling
|
|
||||||
- 🔄 **Smart sync**: Built-in status tracking and seamless multi-machine workflow
|
|
||||||
- 📁 **Directory support**: Manage entire config directories or individual files
|
|
||||||
|
|
||||||
**The market gap**: Tools are either too simple (and unsafe) or too complex (and overwhelming). Lnk is the **Goldilocks solution** – just right for developers who want reliability without complexity.
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install (30 seconds)
|
# Quick
|
||||||
curl -sSL https://github.com/yarlson/lnk/releases/latest/download/lnk-linux-amd64 -o lnk
|
|
||||||
chmod +x lnk && sudo mv lnk /usr/local/bin/
|
|
||||||
|
|
||||||
# Use (60 seconds)
|
|
||||||
lnk init -r git@github.com:you/dotfiles.git
|
|
||||||
lnk add ~/.bashrc ~/.vimrc ~/.gitconfig
|
|
||||||
lnk push "Initial dotfiles setup"
|
|
||||||
```
|
|
||||||
|
|
||||||
**That's it.** Your dotfiles are now version-controlled and synced.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
### Quick Install (Recommended)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Linux/macOS
|
|
||||||
curl -sSL https://raw.githubusercontent.com/yarlson/lnk/main/install.sh | bash
|
curl -sSL https://raw.githubusercontent.com/yarlson/lnk/main/install.sh | bash
|
||||||
|
|
||||||
# Or manually download from releases
|
# Manual
|
||||||
wget https://github.com/yarlson/lnk/releases/latest/download/lnk-$(uname -s | tr '[:upper:]' '[:lower:]')-amd64
|
wget https://github.com/yarlson/lnk/releases/latest/download/lnk-$(uname -s | tr '[:upper:]' '[:lower:]')-amd64
|
||||||
|
chmod +x lnk-* && sudo mv lnk-* /usr/local/bin/lnk
|
||||||
|
|
||||||
|
# From source
|
||||||
|
git clone https://github.com/yarlson/lnk.git && cd lnk && go build . && sudo mv lnk /usr/local/bin/
|
||||||
```
|
```
|
||||||
|
|
||||||
### From Source
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/yarlson/lnk.git && cd lnk
|
|
||||||
go build -ldflags="-s -w" -o lnk .
|
|
||||||
sudo mv lnk /usr/local/bin/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Package Managers
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Homebrew (macOS/Linux)
|
|
||||||
brew install yarlson/tap/lnk
|
|
||||||
|
|
||||||
# Arch Linux
|
|
||||||
yay -S lnk-git
|
|
||||||
```
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
**The mental model is simple**: Lnk moves your dotfiles to `~/.config/lnk/` and replaces them with symlinks.
|
|
||||||
|
|
||||||
```
|
|
||||||
Before: ~/.vimrc (actual file)
|
|
||||||
After: ~/.vimrc -> ~/.config/lnk/.vimrc (symlink)
|
|
||||||
```
|
|
||||||
|
|
||||||
Every change gets a Git commit with descriptive messages like `lnk: added .vimrc`.
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Initialize Once
|
### Setup
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
lnk init # Local repository
|
# Fresh start
|
||||||
lnk init -r git@github.com:username/dotfiles.git # With remote
|
lnk init
|
||||||
|
|
||||||
|
# With existing repo
|
||||||
|
lnk init -r git@github.com:user/dotfiles.git
|
||||||
```
|
```
|
||||||
|
|
||||||
**Safety features** (because your dotfiles matter):
|
### Daily workflow
|
||||||
|
|
||||||
- ✅ Idempotent - run multiple times safely
|
|
||||||
- ✅ Protects existing repositories from overwrite
|
|
||||||
- ✅ Validates remote conflicts before changes
|
|
||||||
|
|
||||||
### Manage Files & Directories
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
lnk add ~/.bashrc ~/.vimrc ~/.tmux.conf # Add multiple files
|
# Add files/directories
|
||||||
lnk add ~/.config/nvim ~/.ssh # Add entire directories
|
lnk add ~/.vimrc ~/.config/nvim ~/.gitconfig
|
||||||
lnk rm ~/.bashrc # Remove from management
|
|
||||||
|
# Check status
|
||||||
|
lnk status
|
||||||
|
|
||||||
|
# Sync changes
|
||||||
|
lnk push "updated vim config"
|
||||||
|
lnk pull
|
||||||
```
|
```
|
||||||
|
|
||||||
### Sync Commands
|
## How it works
|
||||||
|
|
||||||
```bash
|
```
|
||||||
lnk status # Check sync status with remote
|
Before: ~/.vimrc (file)
|
||||||
lnk push "Update vim configuration" # Stage, commit, and push changes
|
After: ~/.vimrc -> ~/.config/lnk/.vimrc (symlink)
|
||||||
lnk pull # Pull changes and restore symlinks
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Smart sync features**:
|
Your files live in `~/.config/lnk` (a Git repo). Lnk creates symlinks back to original locations. Edit files normally, use Git normally.
|
||||||
|
|
||||||
- ✅ Only commits when there are actual changes
|
## Why not just Git?
|
||||||
- ✅ Automatic symlink restoration after pull
|
|
||||||
- ✅ Clear status reporting (commits ahead/behind)
|
|
||||||
- ✅ Graceful error handling for missing remotes
|
|
||||||
|
|
||||||
### Real-World Workflow
|
You could `git init ~/.config/lnk` and manually symlink everything. Lnk just automates the tedious parts:
|
||||||
|
|
||||||
```bash
|
- Moving files safely
|
||||||
# Set up on new machine
|
- Creating relative symlinks
|
||||||
lnk init -r git@github.com:you/dotfiles.git
|
- Handling conflicts
|
||||||
lnk pull # Get your existing dotfiles with automatic symlink restoration
|
- Tracking what's managed
|
||||||
|
|
||||||
# Or clone existing manually for complex setups
|
|
||||||
git clone git@github.com:you/dotfiles.git ~/.config/lnk
|
|
||||||
cd ~/.config/lnk && find . -name ".*" -exec ln -sf ~/.config/lnk/{} ~/{} \;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
<details>
|
### First time setup
|
||||||
<summary><strong>📁 Common Development Setup</strong></summary>
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Initialize with remote (recommended)
|
|
||||||
lnk init -r git@github.com:you/dotfiles.git
|
lnk init -r git@github.com:you/dotfiles.git
|
||||||
|
lnk add ~/.bashrc ~/.vimrc ~/.gitconfig
|
||||||
# Shell & terminal
|
lnk push "initial setup"
|
||||||
lnk add ~/.bashrc ~/.zshrc ~/.tmux.conf
|
|
||||||
|
|
||||||
# Development tools
|
|
||||||
lnk add ~/.vimrc ~/.gitconfig ~/.ssh/config
|
|
||||||
|
|
||||||
# Language-specific
|
|
||||||
lnk add ~/.npmrc ~/.cargo/config.toml ~/.pylintrc
|
|
||||||
|
|
||||||
# Push to remote with sync command
|
|
||||||
lnk push "Initial dotfiles setup"
|
|
||||||
|
|
||||||
# Check what's managed and sync status
|
|
||||||
lnk status
|
|
||||||
cd ~/.config/lnk && git log --oneline
|
|
||||||
# 7f3a12c lnk: Initial dotfiles setup
|
|
||||||
# 4e8b33d lnk: added .cargo/config.toml
|
|
||||||
# 2a9c45e lnk: added .npmrc
|
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
### On a new machine
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><strong>🔄 Multi-Machine Sync</strong></summary>
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Machine 1: Initial setup
|
|
||||||
lnk init -r git@github.com:you/dotfiles.git
|
lnk init -r git@github.com:you/dotfiles.git
|
||||||
lnk add ~/.vimrc ~/.bashrc
|
lnk pull # auto-creates symlinks
|
||||||
lnk push "Setup from machine 1"
|
|
||||||
|
|
||||||
# Machine 2: Clone existing
|
|
||||||
lnk init -r git@github.com:you/dotfiles.git
|
|
||||||
lnk pull # Automatically restores symlinks
|
|
||||||
|
|
||||||
# Daily workflow: Keep machines in sync
|
|
||||||
lnk status # Check if changes need syncing
|
|
||||||
lnk push "Updated vim configuration" # Share your changes
|
|
||||||
lnk pull # Get changes from other machines
|
|
||||||
|
|
||||||
# Example sync session
|
|
||||||
lnk status
|
|
||||||
# Your branch is ahead of 'origin/main' by 2 commit(s)
|
|
||||||
|
|
||||||
lnk push "Added new aliases and vim plugins"
|
|
||||||
# Successfully pushed changes to remote
|
|
||||||
|
|
||||||
lnk pull # On other machine
|
|
||||||
# Successfully pulled changes and restored 0 symlink(s)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
### Daily edits
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><strong>🔄 Smart Sync Workflow</strong></summary>
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check current status
|
vim ~/.vimrc # edit normally
|
||||||
lnk status
|
lnk status # check what changed
|
||||||
# Repository is up to date with remote
|
lnk push "new plugins" # commit & push
|
||||||
|
|
||||||
# Make changes to your dotfiles
|
|
||||||
vim ~/.vimrc # Edit managed file
|
|
||||||
|
|
||||||
# Check what needs syncing
|
|
||||||
lnk status
|
|
||||||
# Your branch is ahead of 'origin/main' by 1 commit(s)
|
|
||||||
|
|
||||||
# Sync changes with descriptive message
|
|
||||||
lnk push "Added syntax highlighting and line numbers"
|
|
||||||
# Successfully pushed changes to remote
|
|
||||||
|
|
||||||
# On another machine
|
|
||||||
lnk pull
|
|
||||||
# Successfully pulled changes and restored 1 symlink(s):
|
|
||||||
# - .vimrc
|
|
||||||
|
|
||||||
# Verify sync status
|
|
||||||
lnk status
|
|
||||||
# Repository is up to date with remote
|
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
## Commands
|
||||||
|
|
||||||
## Technical Details
|
- `lnk init [-r remote]` - Create repo
|
||||||
|
- `lnk add <files>` - Move files to repo, create symlinks
|
||||||
|
- `lnk rm <files>` - Move files back, remove symlinks
|
||||||
|
- `lnk status` - Git status + sync info
|
||||||
|
- `lnk push [msg]` - Stage all, commit, push
|
||||||
|
- `lnk pull` - Pull + restore missing symlinks
|
||||||
|
|
||||||
### Architecture
|
## Technical bits
|
||||||
|
|
||||||
```
|
- **Single binary** (~8MB, no deps)
|
||||||
cmd/ # CLI layer (Cobra)
|
- **Atomic operations** (rollback on failure)
|
||||||
├── init.go # Repository initialization
|
- **Relative symlinks** (portable)
|
||||||
├── add.go # File adoption & symlinking
|
- **XDG compliant** (`~/.config/lnk`)
|
||||||
├── rm.go # File restoration
|
- **20 integration tests**
|
||||||
├── status.go # Sync status reporting
|
|
||||||
├── push.go # Smart commit and push
|
|
||||||
└── pull.go # Pull with symlink restoration
|
|
||||||
|
|
||||||
internal/
|
## Alternatives
|
||||||
├── core/ # Business logic
|
|
||||||
├── fs/ # File system operations
|
|
||||||
└── git/ # Git automation & sync
|
|
||||||
```
|
|
||||||
|
|
||||||
### What Makes It Robust
|
| Tool | Complexity | Why choose it |
|
||||||
|
| ------- | ---------- | ------------------------------------- |
|
||||||
- **20 integration tests** covering edge cases and error conditions
|
| **lnk** | Minimal | Just works, no config, Git-native |
|
||||||
- **Zero external dependencies** at runtime
|
| chezmoi | High | Templates, encryption, cross-platform |
|
||||||
- **Atomic operations** with automatic rollback on failure
|
| yadm | Medium | Git power user, encryption |
|
||||||
- **Relative symlinks** for cross-platform compatibility
|
| dotbot | Low | YAML config, basic features |
|
||||||
- **XDG compliance** with fallback to `~/.config`
|
| stow | Low | Perl, symlink only |
|
||||||
|
|
||||||
### Feature Positioning
|
|
||||||
|
|
||||||
| Feature | Lnk | Dotbot | yadm | chezmoi | Home Manager |
|
|
||||||
| ----------------------- | ------- | ------- | ----- | -------- | ------------ |
|
|
||||||
| **Simplicity** | ✅ | ✅ | ❌ | ❌ | ❌ |
|
|
||||||
| **Safety/Edge Cases** | ✅ | ❌ | ⚠️ | ✅ | ✅ |
|
|
||||||
| **Git Integration** | ✅ | ❌ | ✅ | ⚠️ | ❌ |
|
|
||||||
| **Zero Dependencies** | ✅ | ❌ | ❌ | ✅ | ❌ |
|
|
||||||
| **Cross-Platform** | ✅ | ✅ | ⚠️ | ✅ | ⚠️ |
|
|
||||||
| **Learning Curve** | Minutes | Minutes | Hours | Days | Weeks |
|
|
||||||
| **File Templating** | ❌ | ❌ | Basic | Advanced | Advanced |
|
|
||||||
| **Built-in Encryption** | ❌ | ❌ | ✅ | ✅ | Plugin |
|
|
||||||
| **Package Management** | ❌ | ❌ | ❌ | ❌ | ✅ |
|
|
||||||
|
|
||||||
**Lnk's niche**: Maximum safety and Git integration with minimum complexity.
|
|
||||||
|
|
||||||
### Performance
|
|
||||||
|
|
||||||
- **Single binary**: ~8MB, starts in <10ms
|
|
||||||
- **Minimal I/O**: Only touches files being managed
|
|
||||||
- **Git efficiency**: Uses native Git commands, not libraries
|
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
<details>
|
**Q: What if I already have dotfiles in Git?**
|
||||||
<summary><strong>How is this different from other dotfiles managers?</strong></summary>
|
A: `git clone your-repo ~/.config/lnk && lnk add ~/.vimrc` (adopts existing files)
|
||||||
|
|
||||||
| Tool | Stars | Approach | Complexity | Learning Curve | Git Integration | Cross-Platform | Key Strength |
|
**Q: How do I handle machine-specific configs?**
|
||||||
| ------------ | ----- | ------------------------ | ------------- | -------------- | --------------- | -------------- | ---------------------- |
|
A: Git branches, or just don't manage machine-specific files with lnk
|
||||||
| **Lnk** | - | Simple symlinks + safety | **Minimal** | **Minutes** | **Native** | ✅ | **Safe simplicity** |
|
|
||||||
| chezmoi | 15k | Templates + encryption | High | Hours/Days | Abstracted | ✅ | Feature completeness |
|
|
||||||
| Mackup | 14.9k | App config sync | Medium | Hours | Manual | macOS/Linux | GUI app settings |
|
|
||||||
| Home Manager | 8.1k | Declarative Nix | **Very High** | **Weeks** | Manual | Linux/macOS | Package + config unity |
|
|
||||||
| Dotbot | 7.4k | YAML symlinks | Low | Minutes | Manual | ✅ | Pure simplicity |
|
|
||||||
| yadm | 5.7k | Git wrapper | Medium | Hours | **Native** | Unix-like | Git-centric power |
|
|
||||||
|
|
||||||
**Lnk fills the "safe simplicity" gap** – easier than chezmoi/yadm, safer than Dotbot, more capable than plain Git.
|
**Q: Windows support?**
|
||||||
|
A: Symlinks work on Windows 10+, but untested
|
||||||
|
|
||||||
</details>
|
**Q: Production ready?**
|
||||||
|
A: I use it daily. It won't break your files. API might change (pre-1.0).
|
||||||
|
|
||||||
<details>
|
## Contributing
|
||||||
<summary><strong>Why choose Lnk over the alternatives?</strong></summary>
|
|
||||||
|
|
||||||
**Choose Lnk if you want:**
|
|
||||||
|
|
||||||
- ✅ **Safety first**: Bulletproof edge case handling, won't break existing setups
|
|
||||||
- ✅ **Git-native workflow**: No abstractions, just clean commits with clear messages
|
|
||||||
- ✅ **Zero learning curve**: 3 commands, works like Git, no configuration files
|
|
||||||
- ✅ **Zero dependencies**: Single binary, no Python/Node/Ruby runtime requirements
|
|
||||||
- ✅ **Production ready**: Comprehensive test suite, proper error handling
|
|
||||||
|
|
||||||
**Choose others if you need:**
|
|
||||||
|
|
||||||
- **chezmoi**: Heavy templating, password manager integration, Windows-first
|
|
||||||
- **Mackup**: GUI app settings sync via Dropbox/iCloud (macOS focus)
|
|
||||||
- **Home Manager**: Nix ecosystem, package management, declarative everything
|
|
||||||
- **Dotbot**: Ultra-minimal YAML configuration (no safety features)
|
|
||||||
- **yadm**: Git power user features, encryption, bare repo workflow
|
|
||||||
|
|
||||||
**The sweet spot**: Lnk is for developers who want dotfiles management **without the ceremony** – all the safety and Git integration you need, none of the complexity you don't.
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><strong>When NOT to use Lnk?</strong></summary>
|
|
||||||
|
|
||||||
**Lnk might not be for you if you need:**
|
|
||||||
|
|
||||||
❌ **File templating**: Different configs per machine → use **chezmoi**
|
|
||||||
❌ **Built-in encryption**: Secrets in dotfiles → use **chezmoi** or **yadm**
|
|
||||||
❌ **GUI app settings**: Mac app preferences → use **Mackup**
|
|
||||||
❌ **Package management**: Installing software → use **Home Manager** (Nix)
|
|
||||||
❌ **Complex workflows**: Multi-step bootstrapping → use **chezmoi** or custom scripts
|
|
||||||
❌ **Windows-first**: Native Windows support → use **chezmoi**
|
|
||||||
|
|
||||||
**Lnk's philosophy**: Do one thing (symlink management) extremely well, let other tools handle their specialties. You can always combine Lnk with other tools as needed.
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary><strong>What if I already have a dotfiles repo?</strong></summary>
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone your existing repo to the lnk location
|
git clone https://github.com/yarlson/lnk.git
|
||||||
git clone your-repo ~/.config/lnk
|
cd lnk
|
||||||
|
make deps # Install golangci-lint
|
||||||
# Lnk works with any Git repo structure
|
make check # Runs fmt, vet, lint, test
|
||||||
lnk add ~/.vimrc # Adopts existing files safely
|
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
**What we use:**
|
||||||
|
|
||||||
<details>
|
- **Runtime deps**: Only `cobra` (CLI framework)
|
||||||
<summary><strong>Is this production ready?</strong></summary>
|
- **Test deps**: `testify` for assertions
|
||||||
|
- **Build pipeline**: Standard Makefile with quality checks
|
||||||
|
|
||||||
**Yes, with caveats.** Lnk is thoroughly tested and handles edge cases well, but it's actively developed.
|
**Before submitting:**
|
||||||
|
|
||||||
✅ **Safe to use**: Won't corrupt your files
|
|
||||||
✅ **Well tested**: Comprehensive integration test suite
|
|
||||||
⚠️ **API stability**: Commands may evolve (following semver)
|
|
||||||
|
|
||||||
**Recommendation**: Try it on non-critical dotfiles first.
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
### Quick Dev Setup
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/yarlson/lnk.git && cd lnk
|
make check # Runs all quality checks + tests
|
||||||
make test # Run integration tests
|
|
||||||
make build # Build binary
|
|
||||||
make dev # Watch & rebuild
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Contributing
|
**Adding features:**
|
||||||
|
|
||||||
We follow standard Go practices:
|
- Put integration tests in `test/integration_test.go`
|
||||||
|
- Use conventional commits: `feat:`, `fix:`, `docs:`
|
||||||
- **Tests first**: All features need integration tests
|
|
||||||
- **Conventional commits**: `feat:`, `fix:`, `docs:`, etc.
|
|
||||||
- **No dependencies**: Keep the runtime dependency-free
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT License - see [LICENSE](LICENSE) file for details.
|
[MIT](LICENSE)
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Made by developers, for developers.** Star ⭐ if this saves you time.
|
|
||||||
|
@@ -15,15 +15,12 @@ var initCmd = &cobra.Command{
|
|||||||
remote, _ := cmd.Flags().GetString("remote")
|
remote, _ := cmd.Flags().GetString("remote")
|
||||||
|
|
||||||
lnk := core.NewLnk()
|
lnk := core.NewLnk()
|
||||||
if err := lnk.Init(); err != nil {
|
if err := lnk.InitWithRemote(remote); err != nil {
|
||||||
return fmt.Errorf("failed to initialize lnk: %w", err)
|
return fmt.Errorf("failed to initialize lnk: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if remote != "" {
|
if remote != "" {
|
||||||
if err := lnk.AddRemote("origin", remote); err != nil {
|
fmt.Printf("Initialized lnk repository by cloning: %s\n", remote)
|
||||||
return fmt.Errorf("failed to add remote: %w", err)
|
|
||||||
}
|
|
||||||
fmt.Printf("Initialized lnk repository with remote: %s\n", remote)
|
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Initialized lnk repository")
|
fmt.Println("Initialized lnk repository")
|
||||||
}
|
}
|
||||||
@@ -33,6 +30,6 @@ var initCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
initCmd.Flags().StringP("remote", "r", "", "Add origin remote URL to the repository")
|
initCmd.Flags().StringP("remote", "r", "", "Clone from remote URL instead of creating empty repository")
|
||||||
rootCmd.AddCommand(initCmd)
|
rootCmd.AddCommand(initCmd)
|
||||||
}
|
}
|
||||||
|
@@ -45,6 +45,16 @@ func getRepoPath() string {
|
|||||||
|
|
||||||
// Init initializes the lnk repository
|
// Init initializes the lnk repository
|
||||||
func (l *Lnk) Init() error {
|
func (l *Lnk) Init() error {
|
||||||
|
return l.InitWithRemote("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitWithRemote initializes the lnk repository, optionally cloning from a remote
|
||||||
|
func (l *Lnk) InitWithRemote(remoteURL string) error {
|
||||||
|
if remoteURL != "" {
|
||||||
|
// Clone from remote
|
||||||
|
return l.Clone(remoteURL)
|
||||||
|
}
|
||||||
|
|
||||||
// Create the repository directory
|
// Create the repository directory
|
||||||
if err := os.MkdirAll(l.repoPath, 0755); err != nil {
|
if err := os.MkdirAll(l.repoPath, 0755); err != nil {
|
||||||
return fmt.Errorf("failed to create lnk directory: %w", err)
|
return fmt.Errorf("failed to create lnk directory: %w", err)
|
||||||
@@ -70,6 +80,14 @@ func (l *Lnk) Init() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clone clones a repository from the given URL
|
||||||
|
func (l *Lnk) Clone(url string) error {
|
||||||
|
if err := l.git.Clone(url); err != nil {
|
||||||
|
return fmt.Errorf("failed to clone repository: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// AddRemote adds a remote to the repository
|
// AddRemote adds a remote to the repository
|
||||||
func (l *Lnk) AddRemote(name, url string) error {
|
func (l *Lnk) AddRemote(name, url string) error {
|
||||||
if err := l.git.AddRemote(name, url); err != nil {
|
if err := l.git.AddRemote(name, url); err != nil {
|
||||||
|
@@ -457,3 +457,43 @@ func (g *Git) Pull() error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clone clones a repository from the given URL
|
||||||
|
func (g *Git) Clone(url string) error {
|
||||||
|
// Remove the directory if it exists to ensure clean clone
|
||||||
|
if err := os.RemoveAll(g.repoPath); err != nil {
|
||||||
|
return fmt.Errorf("failed to remove existing directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create parent directory
|
||||||
|
parentDir := filepath.Dir(g.repoPath)
|
||||||
|
if err := os.MkdirAll(parentDir, 0755); err != nil {
|
||||||
|
return fmt.Errorf("failed to create parent directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone the repository
|
||||||
|
cmd := exec.Command("git", "clone", url, g.repoPath)
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("git clone failed: %w\nOutput: %s", err, string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up upstream tracking for main branch
|
||||||
|
cmd = exec.Command("git", "branch", "--set-upstream-to=origin/main", "main")
|
||||||
|
cmd.Dir = g.repoPath
|
||||||
|
_, err = cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
// If main doesn't exist, try master
|
||||||
|
cmd = exec.Command("git", "branch", "--set-upstream-to=origin/master", "master")
|
||||||
|
cmd.Dir = g.repoPath
|
||||||
|
_, err = cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
// If that also fails, try to set upstream for current branch
|
||||||
|
cmd = exec.Command("git", "branch", "--set-upstream-to=origin/HEAD")
|
||||||
|
cmd.Dir = g.repoPath
|
||||||
|
_, _ = cmd.CombinedOutput() // Ignore error as this is best effort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user