feat: add parallel execution with -j/--jobs flag
- Add -j/--jobs N flag to run N commits in parallel - Use xargs -P for reliable parallel execution - Refactor to use process_commit wrapper function - Falls back to sequential when -j 1 or not specified - Should provide 3-4x speedup when checking multiple commits
This commit is contained in:
117
check-history.sh
117
check-history.sh
@@ -2,21 +2,51 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Usage:
|
# Usage:
|
||||||
# check-history.sh <start_offset> <count> [--verbose]
|
# check-history.sh <start_offset> <count> [OPTIONS]
|
||||||
# Example:
|
# Example:
|
||||||
# check-history.sh 2 4 # checks commits 2,3,4,5
|
# check-history.sh 2 4 # checks commits 2,3,4,5
|
||||||
# check-history.sh 5 1 # checks only commit 5
|
# check-history.sh 5 1 -v # verbose mode
|
||||||
# check-history.sh 1 1 --verbose # verbose mode
|
# check-history.sh 2 28 -j4 # check 28 commits, 4 at a time
|
||||||
|
|
||||||
VERBOSE=0
|
VERBOSE=0
|
||||||
if [ $# -lt 2 ] || [ $# -gt 3 ]; then
|
JOBS=1
|
||||||
echo "Usage: $0 <start_offset_from_head> <count> [--verbose|-v]"
|
|
||||||
|
# Parse arguments
|
||||||
|
if [ $# -lt 2 ]; then
|
||||||
|
echo "Usage: $0 <start_offset_from_head> <count> [--verbose|-v] [-j|--jobs N]"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $# -eq 3 ] && { [ "$3" = "--verbose" ] || [ "$3" = "-v" ]; }; then
|
START_ARG="$1"
|
||||||
|
COUNT_ARG="$2"
|
||||||
|
shift 2
|
||||||
|
|
||||||
|
# Parse optional flags
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
-v|--verbose)
|
||||||
VERBOSE=1
|
VERBOSE=1
|
||||||
fi
|
shift
|
||||||
|
;;
|
||||||
|
-j|--jobs)
|
||||||
|
if [ $# -lt 2 ]; then
|
||||||
|
echo "Error: -j/--jobs requires a number" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
JOBS="$2"
|
||||||
|
if ! [[ "$JOBS" =~ ^[0-9]+$ ]] || [ "$JOBS" -lt 1 ]; then
|
||||||
|
echo "Error: -j/--jobs must be a positive integer" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Error: Unknown option $1" >&2
|
||||||
|
echo "Usage: $0 <start_offset_from_head> <count> [--verbose|-v] [-j|--jobs N]"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
# Debug logging helper
|
# Debug logging helper
|
||||||
debug() {
|
debug() {
|
||||||
@@ -26,8 +56,8 @@ debug() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Convert user-friendly 1-indexed "Nth from HEAD" into 0-indexed offset
|
# Convert user-friendly 1-indexed "Nth from HEAD" into 0-indexed offset
|
||||||
START_OFFSET="$(( $1 - 1 ))"
|
START_OFFSET="$(( START_ARG - 1 ))"
|
||||||
COUNT="$2"
|
COUNT="$COUNT_ARG"
|
||||||
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
||||||
TMP_BASE="/tmp/check-history-$$"
|
TMP_BASE="/tmp/check-history-$$"
|
||||||
WORKTREES_DIR="$TMP_BASE/worktrees"
|
WORKTREES_DIR="$TMP_BASE/worktrees"
|
||||||
@@ -40,7 +70,11 @@ debug "PNPM_STORE: $PNPM_STORE"
|
|||||||
|
|
||||||
mkdir -p "$WORKTREES_DIR" "$LOGS_DIR" "$PNPM_STORE"
|
mkdir -p "$WORKTREES_DIR" "$LOGS_DIR" "$PNPM_STORE"
|
||||||
|
|
||||||
echo "Checking commits from offset $1 (HEAD~$START_OFFSET) for $COUNT commits…"
|
if [ "$JOBS" -gt 1 ]; then
|
||||||
|
echo "Checking commits from offset $START_ARG (HEAD~$START_OFFSET) for $COUNT commits with $JOBS parallel jobs…"
|
||||||
|
else
|
||||||
|
echo "Checking commits from offset $START_ARG (HEAD~$START_OFFSET) for $COUNT commits…"
|
||||||
|
fi
|
||||||
echo
|
echo
|
||||||
|
|
||||||
# Build list of commits as if by `git rebase -i HEAD~N` (HEAD=0 newest) as if by `git rebase -i HEAD~N`
|
# Build list of commits as if by `git rebase -i HEAD~N` (HEAD=0 newest) as if by `git rebase -i HEAD~N`
|
||||||
@@ -153,7 +187,9 @@ build_frontend() {
|
|||||||
# -----------------------------
|
# -----------------------------
|
||||||
cleanup() {
|
cleanup() {
|
||||||
echo "
|
echo "
|
||||||
Caught interrupt, cleaning worktrees…" >&2
|
Caught interrupt, killing background jobs and cleaning worktrees…" >&2
|
||||||
|
# Kill all background jobs
|
||||||
|
jobs -p | xargs -r kill 2>/dev/null || true
|
||||||
git worktree prune >/dev/null 2>&1 || true
|
git worktree prune >/dev/null 2>&1 || true
|
||||||
rm -rf "$TMP_BASE" >/dev/null 2>&1 || true
|
rm -rf "$TMP_BASE" >/dev/null 2>&1 || true
|
||||||
exit 130
|
exit 130
|
||||||
@@ -215,36 +251,57 @@ check_commit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Sequential execution
|
# Execution (parallel or sequential)
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
FAIL=0
|
FAIL=0
|
||||||
i=1
|
|
||||||
TOTAL=${#COMMITS[@]}
|
TOTAL=${#COMMITS[@]}
|
||||||
for commit in "${COMMITS[@]}"; do
|
|
||||||
worktree="$WORKTREES_DIR/$commit"
|
|
||||||
log="$LOGS_DIR/$commit.log"
|
|
||||||
|
|
||||||
debug "===== Processing commit $i/$TOTAL ====="
|
# Export functions and variables for parallel execution
|
||||||
debug "Commit: $commit"
|
export -f check_commit build_frontend debug
|
||||||
debug "Worktree: $worktree"
|
export VERBOSE REPO_ROOT PNPM_STORE WORKTREES_DIR LOGS_DIR
|
||||||
debug "Log file: $log"
|
|
||||||
|
|
||||||
echo "[ $i / $TOTAL ] Checking $commit…"
|
# Process a single commit (for xargs)
|
||||||
if ! check_commit "$commit" "$worktree" "$log"; then
|
process_commit() {
|
||||||
|
local idx="$1"
|
||||||
|
local commit="$2"
|
||||||
|
local worktree="$WORKTREES_DIR/$commit"
|
||||||
|
local log="$LOGS_DIR/$commit.log"
|
||||||
|
|
||||||
|
echo "[ $((idx + 1)) / $TOTAL ] Checking $commit…"
|
||||||
|
|
||||||
|
if check_commit "$commit" "$worktree" "$log"; then
|
||||||
|
echo "✓ [ $((idx + 1)) / $TOTAL ] $commit passed"
|
||||||
|
sed 's/^/ /' "$log"
|
||||||
|
git worktree remove --force "$worktree" >/dev/null 2>&1 || true
|
||||||
|
echo
|
||||||
|
return 0
|
||||||
|
else
|
||||||
echo "❌ FAILED at commit $commit"
|
echo "❌ FAILED at commit $commit"
|
||||||
sed 's/^/ /' "$log"
|
sed 's/^/ /' "$log"
|
||||||
|
echo
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
export -f process_commit
|
||||||
|
export TOTAL
|
||||||
|
|
||||||
|
if [ "$JOBS" -eq 1 ]; then
|
||||||
|
# Sequential execution
|
||||||
|
for i in "${!COMMITS[@]}"; do
|
||||||
|
if ! process_commit "$i" "${COMMITS[$i]}"; then
|
||||||
FAIL=1
|
FAIL=1
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
debug "Commit $commit passed"
|
done
|
||||||
sed 's/^/ /' "$log"
|
else
|
||||||
|
# Parallel execution using xargs
|
||||||
|
debug "Starting parallel execution with $JOBS jobs"
|
||||||
|
|
||||||
debug "Removing worktree at $worktree"
|
# Create input for xargs: "index commit"
|
||||||
git worktree remove --force "$worktree" >/dev/null 2>&1
|
for i in "${!COMMITS[@]}"; do
|
||||||
|
echo "$i ${COMMITS[$i]}"
|
||||||
((i++))
|
done | xargs -P "$JOBS" -n 2 bash -c 'process_commit "$@"' _ || FAIL=1
|
||||||
echo
|
fi
|
||||||
done
|
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Final output
|
# Final output
|
||||||
|
|||||||
Reference in New Issue
Block a user