Compare commits
5 Commits
6e983f08e8
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a74e14514b | |||
| 01f552b7ec | |||
| b6c05ced0a | |||
| 02922ccc55 | |||
| 9fecfeee46 |
@@ -23,30 +23,42 @@ The entire implementation is a single Babashka script (`commitly`) with these ke
|
||||
- `has-changes?` - Uses `git status --porcelain` to detect uncommitted changes
|
||||
- `commit-changes` - Executes `git add -A` and `git commit -m` for a repo
|
||||
- `push-changes` - Executes `git push` for a repo
|
||||
- `commitly` - Main orchestration function that coordinates the workflow
|
||||
- `pull-changes` - Executes `git pull` for a repo
|
||||
- `status-all` - Shows git status for all modified repositories
|
||||
- `commitly` - Main orchestration function that coordinates commit workflow
|
||||
- `push-all` - Pushes all repositories without committing
|
||||
- `pull-all` - Pulls changes for all repositories
|
||||
- `-main` - CLI argument parsing and entry point
|
||||
|
||||
### Workflow
|
||||
|
||||
1. Scan current directory for subdirectories with `.git` folders
|
||||
2. Filter to only repos with uncommitted changes
|
||||
2. Filter to only repos with uncommitted changes (for commit operations)
|
||||
3. For each modified repo: stage all changes and commit with provided message
|
||||
4. If `-p` flag is present, push each successfully committed repo
|
||||
4. If `push` subcommand is used, push each successfully committed repo (or all repos if no commit message)
|
||||
5. Report success/failure for each repository
|
||||
|
||||
## Common Commands
|
||||
|
||||
### Basic Usage
|
||||
|
||||
**Note:** These examples assume `commitly` is in your PATH.
|
||||
|
||||
```bash
|
||||
# Commit changes across all modified subrepos
|
||||
./commitly "commit message"
|
||||
# Show status of all modified subrepos
|
||||
commitly status
|
||||
|
||||
# Commit changes across all modified subrepos (no push)
|
||||
commitly "commit message"
|
||||
|
||||
# Commit and push changes
|
||||
./commitly -p "commit message"
|
||||
commitly push "commit message"
|
||||
|
||||
# Push only (no commit) - useful after manual commits
|
||||
./commitly -p
|
||||
commitly push
|
||||
|
||||
# Pull changes for all subrepos
|
||||
commitly pull
|
||||
```
|
||||
|
||||
### Running via Babashka Tasks
|
||||
@@ -61,7 +73,7 @@ bb run "commit message"
|
||||
The script is self-contained with no external dependencies beyond Babashka standard library (`babashka.process`, `babashka.fs`). To modify:
|
||||
|
||||
1. Edit the `commitly` script directly
|
||||
2. Test changes by running `./commitly` with test arguments
|
||||
2. Test changes by running `commitly` with test arguments (or `./commitly` if running from the repo directory)
|
||||
3. The script is executable via shebang `#!/usr/bin/env bb`
|
||||
|
||||
## Important Implementation Details
|
||||
@@ -73,9 +85,9 @@ The script is self-contained with no external dependencies beyond Babashka stand
|
||||
- Non-zero exit code (1) if any operations fail
|
||||
- Empty commit message validation prevents accidental empty commits
|
||||
|
||||
### Special `-p` Flag Behavior
|
||||
### Push Subcommand Behavior
|
||||
|
||||
The `-p` flag has dual behavior:
|
||||
The `push` subcommand has dual behavior:
|
||||
- With commit message: commits then pushes
|
||||
- Without commit message: pushes only (skips committing)
|
||||
|
||||
@@ -85,7 +97,7 @@ This allows workflows like:
|
||||
cd service-manager && git commit -m "specific message"
|
||||
cd ../www && git commit -m "different message"
|
||||
cd ..
|
||||
./commitly -p # Push all repos
|
||||
commitly push # Push all repos
|
||||
```
|
||||
|
||||
### Process Execution
|
||||
|
||||
@@ -10,14 +10,31 @@ A CLI tool for making commits to many subrepos after a distributed change.
|
||||
|
||||
- Automatically detects which subrepos have uncommitted changes
|
||||
- Creates commits with a single message across all modified subrepos
|
||||
- Push changes to remote repositories after committing
|
||||
- Pull changes from remote repositories across all subrepos
|
||||
- View status of all modified repositories
|
||||
- Reports commit status for each subrepo
|
||||
- Written in Babashka for fast startup and easy distribution
|
||||
|
||||
## Usage
|
||||
|
||||
**Note:** These examples assume `commitly` is in your PATH (e.g., installed via bbin or symlinked).
|
||||
|
||||
```bash
|
||||
# Show status of all modified subrepos
|
||||
commitly status
|
||||
|
||||
# Commit changes across all modified subrepos
|
||||
./commitly "Your commit message here"
|
||||
commitly "Your commit message here"
|
||||
|
||||
# Commit and push changes
|
||||
commitly push "Your commit message here"
|
||||
|
||||
# Push only (without committing)
|
||||
commitly push
|
||||
|
||||
# Pull changes for all subrepos
|
||||
commitly pull
|
||||
```
|
||||
|
||||
## Requirements
|
||||
@@ -26,19 +43,52 @@ A CLI tool for making commits to many subrepos after a distributed change.
|
||||
|
||||
## Installation
|
||||
|
||||
### Using bbin (Recommended)
|
||||
|
||||
Install directly from Gitea using SSH:
|
||||
|
||||
```bash
|
||||
# Install from your Gitea repository via SSH
|
||||
bbin install git@git.ajet.fyi:ajet-industries/commitly.git
|
||||
|
||||
# Install a specific version using a git tag
|
||||
bbin install git@git.ajet.fyi:ajet-industries/commitly.git --git/tag v1.0.0
|
||||
|
||||
# Install the latest commit
|
||||
bbin install git@git.ajet.fyi:ajet-industries/commitly.git --latest-sha
|
||||
```
|
||||
|
||||
This will install `commitly` to `~/.local/bin/commitly` (make sure `~/.local/bin` is in your PATH).
|
||||
|
||||
**Note:** Requires [bbin](https://github.com/babashka/bbin) to be installed first:
|
||||
```bash
|
||||
bash < <(curl -s https://raw.githubusercontent.com/babashka/bbin/main/bbin)
|
||||
```
|
||||
|
||||
### Manual Installation
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone git@git.ajet.fyi:ajet-industries/commitly.git
|
||||
cd commitly
|
||||
|
||||
# Make the script executable
|
||||
chmod +x commitly
|
||||
|
||||
# Optionally, symlink to a directory in your PATH
|
||||
ln -s $(pwd)/commitly /usr/local/bin/commitly
|
||||
# Symlink to a directory in your PATH
|
||||
ln -s $(pwd)/commitly ~/.local/bin/commitly
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
1. Scans parent directory for git repositories
|
||||
2. Checks each repository for uncommitted changes (staged or unstaged)
|
||||
3. Commits changes in each modified repository with the provided message
|
||||
3. Depending on the command:
|
||||
- `status`: Shows git status for all modified repositories
|
||||
- `<message>`: Commits changes in each modified repository with the provided message
|
||||
- `push <message>`: Commits and pushes changes to remote
|
||||
- `push` (no message): Pushes all repositories without committing
|
||||
- `pull`: Pulls changes from remote for all repositories
|
||||
4. Reports success/failure for each repository
|
||||
|
||||
## Use Case
|
||||
|
||||
@@ -18,11 +18,16 @@
|
||||
(not (str/blank? (:out result)))))
|
||||
|
||||
(defn find-subrepos [parent-dir]
|
||||
"Find all git repositories in parent directory"
|
||||
(->> (fs/list-dir parent-dir)
|
||||
(filter fs/directory?)
|
||||
(filter git-repo?)
|
||||
(map str)))
|
||||
"Find all git repositories in parent directory, including parent itself"
|
||||
(let [child-repos (->> (fs/list-dir parent-dir)
|
||||
(filter fs/directory?)
|
||||
(filter git-repo?)
|
||||
(map str))
|
||||
parent-is-repo? (git-repo? parent-dir)
|
||||
repos (if parent-is-repo?
|
||||
(cons (str parent-dir) child-repos)
|
||||
child-repos)]
|
||||
repos))
|
||||
|
||||
(defn commit-changes [repo-path message]
|
||||
"Commit all changes in repository with given message"
|
||||
@@ -57,6 +62,20 @@
|
||||
(catch Exception e
|
||||
{:success false :repo repo-path :error (.getMessage e)})))
|
||||
|
||||
(defn pull-changes [repo-path]
|
||||
"Pull changes from remote repository"
|
||||
(try
|
||||
(let [pull-result (process/shell {:dir repo-path
|
||||
:out :string
|
||||
:err :string
|
||||
:continue true}
|
||||
"git pull")]
|
||||
(if (zero? (:exit pull-result))
|
||||
{:success true :repo repo-path}
|
||||
{:success false :repo repo-path :error (:err pull-result)}))
|
||||
(catch Exception e
|
||||
{:success false :repo repo-path :error (.getMessage e)})))
|
||||
|
||||
(defn show-status [repo-path]
|
||||
"Show git status for a repository"
|
||||
(let [result (process/shell {:dir (str repo-path)
|
||||
@@ -126,50 +145,85 @@
|
||||
(println (format "\n%d repositories failed to push" (count failed)))
|
||||
(System/exit 1))))))))
|
||||
|
||||
(defn push-all []
|
||||
"Push all repositories without committing"
|
||||
(let [current-dir (fs/cwd)
|
||||
subrepos (find-subrepos current-dir)]
|
||||
(println "\nPushing changes...")
|
||||
(let [push-results (map push-changes subrepos)]
|
||||
(doseq [result push-results]
|
||||
(if (:success result)
|
||||
(println (format "✓ %s" (fs/file-name (:repo result))))
|
||||
(println (format "✗ %s: %s" (fs/file-name (:repo result)) (:error result)))))
|
||||
(let [failed (filter #(not (:success %)) push-results)]
|
||||
(when (seq failed)
|
||||
(println (format "\n%d repositories failed to push" (count failed)))
|
||||
(System/exit 1))))))
|
||||
|
||||
(defn pull-all []
|
||||
"Pull changes for all repositories"
|
||||
(let [repos (find-subrepos (fs/cwd))
|
||||
pull-results (map pull-changes repos)
|
||||
successful (filter :success pull-results)
|
||||
failed (filter #(not (:success %)) pull-results)]
|
||||
|
||||
;; Report successes
|
||||
(doseq [result successful]
|
||||
(println (format "✓ %s" (fs/file-name (:repo result)))))
|
||||
|
||||
;; Report failures
|
||||
(when (seq failed)
|
||||
(println (format "\n%d repositories failed to pull:" (count failed)))
|
||||
(doseq [result failed]
|
||||
(println (format "✗ %s" (fs/file-name (:repo result))))
|
||||
(println (format " Error: %s" (:error result))))
|
||||
(System/exit 1))
|
||||
|
||||
(println (format "\nSuccessfully pulled %d repositories." (count successful)))))
|
||||
|
||||
;; CLI entry point
|
||||
(defn -main [& args]
|
||||
(if (empty? args)
|
||||
(do
|
||||
(println "Usage: commitly [-p] <commit-message>")
|
||||
(println "Usage: commitly <commit-message>")
|
||||
(println " commitly status")
|
||||
(println " commitly push [<commit-message>]")
|
||||
(println " commitly pull")
|
||||
(println "")
|
||||
(println "Options:")
|
||||
(println " -p Push changes after committing (or just push if no message)")
|
||||
(println " status Show git status for all subrepos with changes")
|
||||
(println "Commands:")
|
||||
(println " status Show git status for all subrepos with changes")
|
||||
(println " push Push all repositories without committing")
|
||||
(println " push <commit-message> Commit and push changes across all modified subrepos")
|
||||
(println " pull Pull changes for all repositories")
|
||||
(println " <commit-message> Commit changes across all modified subrepos (no push)")
|
||||
(System/exit 1))
|
||||
(let [first-arg (first args)]
|
||||
(let [first-arg (first args)
|
||||
rest-args (rest args)]
|
||||
(cond
|
||||
;; Status command
|
||||
(= "status" first-arg)
|
||||
(status-all)
|
||||
|
||||
;; Commit/push command
|
||||
;; Pull command
|
||||
(= "pull" first-arg)
|
||||
(pull-all)
|
||||
|
||||
;; Push command
|
||||
(= "push" first-arg)
|
||||
(if (empty? rest-args)
|
||||
;; Push only (no commit)
|
||||
(push-all)
|
||||
;; Commit and push
|
||||
(let [commit-message (str/join " " rest-args)]
|
||||
(commitly commit-message :push? true)))
|
||||
|
||||
;; Commit command (no push)
|
||||
:else
|
||||
(let [push? (some #(= "-p" %) args)
|
||||
message-args (remove #(= "-p" %) args)
|
||||
commit-message (str/join " " message-args)]
|
||||
(if (and (str/blank? commit-message) push?)
|
||||
;; If -p is enabled and no message, just push without committing
|
||||
(do
|
||||
(println "No commit message provided with -p flag. Pushing only...")
|
||||
(let [current-dir (fs/cwd)
|
||||
subrepos (find-subrepos current-dir)]
|
||||
(println "\nPushing changes...")
|
||||
(let [push-results (map push-changes subrepos)]
|
||||
(doseq [result push-results]
|
||||
(if (:success result)
|
||||
(println (format "✓ %s" (fs/file-name (:repo result))))
|
||||
(println (format "✗ %s: %s" (fs/file-name (:repo result)) (:error result)))))
|
||||
(let [failed (filter #(not (:success %)) push-results)]
|
||||
(when (seq failed)
|
||||
(println (format "\n%d repositories failed to push" (count failed)))
|
||||
(System/exit 1))))))
|
||||
;; Otherwise, require commit message
|
||||
(do
|
||||
(when (str/blank? commit-message)
|
||||
(println "Error: commit message cannot be empty")
|
||||
(System/exit 1))
|
||||
(commitly commit-message :push? push?))))))))
|
||||
(let [commit-message (str/join " " args)]
|
||||
(when (str/blank? commit-message)
|
||||
(println "Error: commit message cannot be empty")
|
||||
(System/exit 1))
|
||||
(commitly commit-message :push? false))))))
|
||||
|
||||
(when (= *file* (System/getProperty "babashka.file"))
|
||||
(apply -main *command-line-args*))
|
||||
|
||||
+45
-13
@@ -4,41 +4,73 @@ Use this skill when you need to commit and/or push changes across multiple git r
|
||||
|
||||
## What is Commitly?
|
||||
|
||||
Commitly is a Babashka script located at `commitly/commitly` that commits and pushes changes across all modified git subrepositories in the ajet-industries monorepo.
|
||||
Commitly is a Babashka CLI tool that commits and pushes changes across all modified git subrepositories in the ajet-industries monorepo.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Install via bbin (recommended)
|
||||
bbin install git@git.ajet.fyi:ajet-industries/commitly.git
|
||||
```
|
||||
|
||||
This installs `commitly` to `~/.local/bin/commitly` (ensure `~/.local/bin` is in your PATH).
|
||||
|
||||
## Usage
|
||||
|
||||
**Note:** These examples assume `commitly` is in your PATH.
|
||||
|
||||
```bash
|
||||
./commitly/commitly [-p] <commit-message>
|
||||
commitly status # Show status of modified repos
|
||||
commitly <commit-message> # Commit changes (no push)
|
||||
commitly push [<commit-message>] # Push (and optionally commit) changes
|
||||
commitly pull # Pull changes for all repos
|
||||
```
|
||||
|
||||
### Options
|
||||
### Commands
|
||||
|
||||
- `-p` - Push changes after committing. If no commit message is provided with `-p`, it will skip committing and just push all subrepos.
|
||||
- `status` - Show git status for all modified repositories
|
||||
- `<commit-message>` - Commit changes across all modified repositories (no push)
|
||||
- `push` - Push all repositories without committing
|
||||
- `push <commit-message>` - Commit and push changes across all modified repositories
|
||||
- `pull` - Pull changes from remote for all repositories
|
||||
|
||||
### Examples
|
||||
|
||||
**Commit all modified repos with a message:**
|
||||
**Show status of all modified repos:**
|
||||
```bash
|
||||
./commitly/commitly "fix: update configuration"
|
||||
commitly status
|
||||
```
|
||||
|
||||
**Commit all modified repos with a message (no push):**
|
||||
```bash
|
||||
commitly "fix: update configuration"
|
||||
```
|
||||
|
||||
**Commit and push all modified repos:**
|
||||
```bash
|
||||
./commitly/commitly -p "feat: add new feature"
|
||||
commitly push "feat: add new feature"
|
||||
```
|
||||
|
||||
**Just push all repos (no commit):**
|
||||
```bash
|
||||
./commitly/commitly -p
|
||||
commitly push
|
||||
```
|
||||
|
||||
**Pull changes for all repos:**
|
||||
```bash
|
||||
commitly pull
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **Discovery**: Scans the current directory for subdirectories containing `.git` folders
|
||||
2. **Detection**: Checks each repository for uncommitted changes using `git status --porcelain`
|
||||
3. **Commit**: Runs `git add -A` and `git commit -m "<message>"` for each modified repo
|
||||
4. **Push**: If `-p` flag is present, runs `git push` for each successfully committed repo (or all repos if no message provided)
|
||||
3. **Action**: Depending on the command:
|
||||
- `status`: Shows full `git status` output for each modified repo
|
||||
- `<message>`: Runs `git add -A` and `git commit -m "<message>"` for each modified repo (no push)
|
||||
- `push <message>`: Commits and then runs `git push` for each successfully committed repo
|
||||
- `push` (no message): Runs `git push` for all repos without committing
|
||||
- `pull`: Runs `git pull` for all repos
|
||||
|
||||
## When to Use
|
||||
|
||||
@@ -51,18 +83,18 @@ The monorepo contains multiple independent git repositories:
|
||||
- `www/` - Dashboard website
|
||||
- `gateway/` - Nginx reverse proxy
|
||||
- `commitly/` - This tool itself
|
||||
- `just-vibes/` - Other projects
|
||||
- Other service repositories as needed
|
||||
|
||||
Each has its own `.git` directory and is deployed independently via Gitea Actions when pushed to main.
|
||||
|
||||
## Error Handling
|
||||
|
||||
- If any repository fails to commit, commitly exits with code 1 and reports which repos failed
|
||||
- If any repository fails to push (when `-p` is used), commitly exits with code 1 and reports which repos failed
|
||||
- If any repository fails to push (when `push` subcommand is used), commitly exits with code 1 and reports which repos failed
|
||||
- Repos are processed independently - one failure doesn't stop others from being processed
|
||||
|
||||
## Requirements
|
||||
|
||||
- Babashka must be installed (`bb` command available)
|
||||
- The script must be executable: `chmod +x commitly/commitly`
|
||||
- `commitly` must be in your PATH (install via bbin or manual symlink)
|
||||
- Each subrepo must have a git remote configured for pushing
|
||||
|
||||
Reference in New Issue
Block a user