Skip to content

Instantly share code, notes, and snippets.

@mikegwhit
Created June 21, 2025 04:34
Show Gist options
  • Save mikegwhit/b7eebcd868acb8655f539e47f771347e to your computer and use it in GitHub Desktop.
Save mikegwhit/b7eebcd868acb8655f539e47f771347e to your computer and use it in GitHub Desktop.
#!/bin/bash
# git-diff-json.sh - Get Git diff information in JSON format
#
# Usage:
# ./git-diff-json.sh [directory] [options]
#
# Options:
# --compare-branch, -c Branch to compare against (default: main)
# --no-local-changes, -n Don't include local changes in comparison
# --name-only Return only file names instead of full diff
# --summary, -s Return only summary of changes
# --output, -o Output file (default: .git-diff.json in CWD)
# --help, -h Show this help message
# Default values
COMPARE_BRANCH="main"
INCLUDE_LOCAL_CHANGES=true
NAME_ONLY=false
SUMMARY=false
OUTPUT_FILE=""
DIRECTORY="$(pwd)"
# Function to show help
show_help() {
grep '^#' "$0" | grep -v '#!/bin/bash' | sed 's/^# \?//'
exit 0
}
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--compare-branch|-c)
COMPARE_BRANCH="$2"
shift 2
;;
--no-local-changes|-n)
INCLUDE_LOCAL_CHANGES=false
shift
;;
--name-only)
NAME_ONLY=true
shift
;;
--summary|-s)
SUMMARY=true
shift
;;
--output|-o)
OUTPUT_FILE="$2"
shift 2
;;
--help|-h)
show_help
;;
*)
# If the argument doesn't start with a dash, treat it as a directory
if [[ ! "$1" =~ ^- ]]; then
DIRECTORY="$1"
else
echo "Unknown option: $1"
exit 1
fi
shift
;;
esac
done
# Set default output file if not specified
if [ -z "$OUTPUT_FILE" ]; then
OUTPUT_FILE="$DIRECTORY/.git-diff.json"
fi
# Check if directory exists and is a git repository
if [ ! -d "$DIRECTORY" ]; then
echo "Error: Directory '$DIRECTORY' does not exist" >&2
exit 1
fi
# Save current directory to return to it later
ORIGINAL_DIR=$(pwd)
# Move to the specified directory
cd "$DIRECTORY" || { echo "Error: Cannot change to directory '$DIRECTORY'" >&2; exit 1; }
# Check if this is a git repository
if ! git rev-parse --git-dir > /dev/null 2>&1; then
echo "Error: Not a git repository: '$DIRECTORY'" >&2
cd "$ORIGINAL_DIR"
exit 1
fi
# Get current branch
CURRENT_BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse HEAD)
# Build diff command options
DIFF_OPTIONS=""
if [ "$NAME_ONLY" = true ]; then
DIFF_OPTIONS="$DIFF_OPTIONS --name-only"
fi
if [ "$SUMMARY" = true ]; then
DIFF_OPTIONS="$DIFF_OPTIONS --stat"
fi
# Get diff result based on options
if [ "$INCLUDE_LOCAL_CHANGES" = true ]; then
# Compare working tree to specified branch
DIFF_RESULT=$(git diff $DIFF_OPTIONS "$COMPARE_BRANCH")
DIFF_MODE="local-to-branch"
else
# Compare current branch to specified branch without local changes
DIFF_RESULT=$(git diff $DIFF_OPTIONS "${COMPARE_BRANCH}...${CURRENT_BRANCH}")
DIFF_MODE="branch-to-branch"
fi
# Get repository information
REPO_DIR=$(git rev-parse --show-toplevel)
REPO_NAME=$(basename "$REPO_DIR")
REMOTE_URL=$(git remote get-url origin 2>/dev/null || echo "")
# Create JSON output
# Escape JSON special characters
DIFF_RESULT_ESCAPED=$(echo "$DIFF_RESULT" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | sed 's/\n/\\n/g' | tr -d '\r')
JSON_OUTPUT="{
\"repository\": \"$REPO_NAME\",
\"directory\": \"$REPO_DIR\",
\"remote\": \"$REMOTE_URL\",
\"currentBranch\": \"$CURRENT_BRANCH\",
\"compareBranch\": \"$COMPARE_BRANCH\",
\"mode\": \"$DIFF_MODE\",
\"includeLocalChanges\": $INCLUDE_LOCAL_CHANGES,
\"timestamp\": \"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\",
\"diff\": \"$DIFF_RESULT_ESCAPED\"
}"
# Output JSON
if [ -z "$OUTPUT_FILE" ] || [ "$OUTPUT_FILE" = "-" ]; then
echo "$JSON_OUTPUT"
else
echo "$JSON_OUTPUT" > "$OUTPUT_FILE"
echo "Diff information saved to $OUTPUT_FILE"
fi
# Return to original directory
cd "$ORIGINAL_DIR"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment