Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 44 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
git_scripts
===========
# git_scripts

These are some scripts that I use constantly when dealing with multiple git repositories.

To use, them, simply add the cloned folder to the `PATH` env var!

## run_all.sh
----------

---

This script is typically run from the common parent directory.

With the latest update, the `run_all.sh` script can be configured to run against any level of subdirectory. Examples:
Expand All @@ -30,24 +31,59 @@ To see more features based on env vars (and a more complete description), please

Note: **Any command can be run** (whether they are specific to the shell that you are currently using) or git commands. These commands are run within the context of each git repository that is matched after applying the filters.

### Command-line Tool: run-all

A standalone command-line tool version is available in the [run-all](run-all/) directory. Install it system-wide for easier access:

```bash
cd run-all
./install.sh
```

Then use from anywhere:

```bash
run-all ls -la
run-all git status
FOLDER=~/dev run-all git fetch
```

See [run-all/README.md](run-all/README.md) for more details.

![run_all.sh in action](./images/Screenshot.png "Screenshot of run_all.sh")

## clean_from_git_history
----------

---

Script to permanently delete files/folders from your git repository. To use it, cd to your repository's root and then run the script with a list of paths you want to delete, e.g., `git-delete-history path1 path2`

## cleanup_all_gems
----------

---

Script to cleanup all installed gems (if you do not want to simply delete the gem install directory)

## delete_merged_branches
----------

---

After merging local changes into branches and pushing them to the respective remote-tracking branches, you might want to clean your local machine. This script is used to ensure the first is done and then will cleanly delete the synced branches from local.

## file_sizes
----------

---

Script to see how much disk space each folder/item in your git repo is holding

## run_all.ps1
----------

---

Powershell clone of the `run_all.sh` - but, as yet, unmaintained since I do not have access to a Windows machine.

## Contributors

---

- [nvnkmrm](https://github.com/nvnkmrm)
43 changes: 43 additions & 0 deletions run-all/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# run-all

Run any command across multiple repositories.

## Installation

```bash
./install.sh
```

Or manually:

```bash
chmod +x run-all
sudo cp run-all /usr/local/bin/
```

## Usage

```bash
run-all <command>
```

### Options

- `FOLDER=<path>` - Starting folder (default: current directory)
- `MINDEPTH=<n>` - Minimum search depth (default: 1)
- `MAXDEPTH=<n>` - Maximum search depth (default: 3)
- `FILTER=<regex>` - Filter repositories by pattern

### Examples

```bash
run-all ls -la
run-all pwd
run-all git status
run-all git fetch
FOLDER=~/dev run-all git pull
FILTER='frontend|backend' run-all npm install
run-all make clean
```

For more options: `run-all --help`
63 changes: 63 additions & 0 deletions run-all/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env bash

# Installation script for run-all command-line tool

set -e

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SOURCE_FILE="${SCRIPT_DIR}/run-all"
INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}"
TARGET_FILE="${INSTALL_DIR}/run-all"

echo -e "${YELLOW}Installing run-all command-line tool...${NC}"

# Check if source file exists
if [ ! -f "${SOURCE_FILE}" ]; then
echo -e "${RED}Error: run-all file not found at ${SOURCE_FILE}${NC}"
exit 1
fi

# Check if install directory exists
if [ ! -d "${INSTALL_DIR}" ]; then
echo -e "${RED}Error: Install directory ${INSTALL_DIR} does not exist${NC}"
exit 1
fi

# Check if we need sudo
if [ ! -w "${INSTALL_DIR}" ]; then
SUDO="sudo"
echo -e "${YELLOW}Note: Installing to ${INSTALL_DIR} requires sudo privileges${NC}"
else
SUDO=""
fi

# Make source file executable
echo "Making run-all executable..."
chmod +x "${SOURCE_FILE}"

# Copy to install directory
echo "Copying run-all to ${INSTALL_DIR}..."
${SUDO} cp "${SOURCE_FILE}" "${TARGET_FILE}"

# Verify installation
if [ -f "${TARGET_FILE}" ] && [ -x "${TARGET_FILE}" ]; then
echo -e "${GREEN}✓ Successfully installed run-all to ${TARGET_FILE}${NC}"
echo ""
echo -e "${GREEN}You can now use 'run-all' from anywhere!${NC}"
echo ""
echo "Examples:"
echo " run-all git status"
echo " FOLDER=~/dev run-all git fetch"
echo " FILTER='project' run-all git pull"
echo ""
echo "For more information, run: run-all --help"
else
echo -e "${RED}Error: Installation failed${NC}"
exit 1
fi
133 changes: 133 additions & 0 deletions run-all/run-all
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#!/usr/bin/env bash

# run-all - Run any command across multiple repositories
#
# Installation:
# chmod +x run-all
# sudo cp run-all /usr/local/bin/
# or: ln -s $(pwd)/run-all /usr/local/bin/run-all

colorize() {
printf "\x1b[${1}m"
}

NC=$(colorize '0') # No Color
RED=$(colorize '0;31')
GREEN=$(colorize '0;32')
YELLOW=$(colorize '1;33')
BLUE=$(colorize '0;34')
CYAN=$(colorize '0;36')

blue() {
printf "${BLUE}${1}${NC}"
}

cyan() {
printf "${CYAN}${1}${NC}"
}

green() {
printf "${GREEN}${1}${NC}"
}

red() {
printf "${RED}${1}${NC}"
}

yellow() {
printf "${YELLOW}${1}${NC}"
}

if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
cat << EOF
$(red "** run-all - Run any command across multiple repositories **")

$(yellow "USAGE:")
run-all [OPTIONS] <command>

$(yellow "DESCRIPTION:")
This tool finds all repositories within a specified folder (filtered by pattern)
and runs the specified command in each repository.

$(yellow "OPTIONS:")
FOLDER=<path> Starting folder to search (default: current directory)
MINDEPTH=<n> Minimum depth for search (default: 1)
MAXDEPTH=<n> Maximum depth for search (default: 3)
FILTER=<regex> Regex pattern to filter repository paths (default: none)

$(yellow "EXAMPLES:")
run-all ls -la
run-all pwd
run-all git status
run-all git branch -vv
FOLDER=~/dev MINDEPTH=2 run-all git status
FOLDER=~/dev MINDEPTH=2 run-all npm install
FILTER=oss run-all git fetch
FILTER='oss|zsh|omz' run-all make test

$(yellow "INSTALLATION:")
chmod +x run-all
sudo cp run-all /usr/local/bin/
# or create a symlink:
sudo ln -s \$(pwd)/run-all /usr/local/bin/run-all
EOF
exit 0
fi

if [ $# -eq 0 ]; then
echo $(red "Error: No command specified")
echo "Use 'run-all --help' for usage information"
exit 1
fi

MINDEPTH=${MINDEPTH:-1}
MAXDEPTH=${MAXDEPTH:-3}
FOLDER="${FOLDER:-.}"
FILTER="${FILTER:-}"

start_time=$(date +%s)
echo $(cyan "Script started at: $(date)")

echo $(yellow "Finding git repos starting in folder '${FOLDER}' for a min depth of ${MINDEPTH} and max depth of ${MAXDEPTH}")
[ "${FILTER}" != '' ] && echo $(yellow "Filtering with: ${FILTER}")

find_cmd=(
find "${FOLDER}"
-mindepth "${MINDEPTH}" -maxdepth "${MAXDEPTH}"
# Exclude specific directory patterns by pruning them
\( -path '*/tmp' -o -path '*/Library/Cache' \) -prune
# Or (-o), find .git directories, prune them (don't descend), and print their parent directory
-o
\( -name ".git" -type d -prune -exec dirname {} \; \)
)

# Execute find, get parent directory, filter, and sort
# Use portable method instead of mapfile for compatibility
DIR_ARRAY=()
while IFS= read -r line; do
DIR_ARRAY+=("$line")
done < <("${find_cmd[@]}" 2>/dev/null | grep -iE "${FILTER}" | sort -u)

TOTAL_COUNT=${#DIR_ARRAY[@]}

if [ ${TOTAL_COUNT} -eq 0 ]; then
echo $(red "No git repositories found matching the criteria")
exit 0
fi

COUNT=1
for dir in "${DIR_ARRAY[@]}"; do
if [ -d "${dir}" ] && [ ! -h "${dir}" ]; then
echo $(green ">>>>>>>>>>>>>>>>>>>>> [${COUNT} of ${TOTAL_COUNT}] '$*' (in '${dir}') <<<<<<<<<<<<<<<<<<<<")
# Use a subshell and "$@" for better argument handling and potentially less overhead than 'bash -c'
(cd "${dir}" && eval "$@")
COUNT=$((COUNT + 1))
fi
done

end_time=$(date +%s)
echo $(cyan "Script finished at: $(date)")

duration=$((end_time - start_time))
duration_formatted=$(printf "%02d:%02d:%02d" $((duration/3600)) $((duration%3600/60)) $((duration%60)))
echo $(cyan "Total execution time: ${duration_formatted} (HH:MM:SS)")