Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:20:25 +08:00
commit 0d6226e0d8
69 changed files with 20934 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
#!/usr/bin/env bash
# ================================================================
# Script: changes-detector.sh
# Purpose: Check for changes to commit (staged, unstaged, untracked)
# Version: 1.0.0
# Usage: ./changes-detector.sh
# Returns: JSON with change counts
# Exit Codes:
# 0 = Success (with or without changes)
# 1 = Not a git repository
# 2 = Script error
# ================================================================
set -euo pipefail
# Function to output JSON
output_json() {
local has_changes=$1
local staged=$2
local unstaged=$3
local untracked=$4
local total=$5
cat <<EOF
{
"has_changes": $has_changes,
"staged_count": $staged,
"unstaged_count": $unstaged,
"untracked_count": $untracked,
"total_changes": $total,
"checked_at": "$(date -Iseconds)"
}
EOF
}
# Main logic
main() {
# Verify we're in a git repository
if ! git rev-parse --git-dir &>/dev/null; then
output_json false 0 0 0 0
exit 1
fi
# Count staged changes (added to index)
STAGED_COUNT=$(git diff --cached --numstat | wc -l)
# Count unstaged changes (modified but not staged)
UNSTAGED_COUNT=$(git diff --numstat | wc -l)
# Count untracked files
UNTRACKED_COUNT=$(git ls-files --others --exclude-standard | wc -l)
# Total changes
TOTAL=$((STAGED_COUNT + UNSTAGED_COUNT + UNTRACKED_COUNT))
# Determine if there are any changes
if [ "$TOTAL" -gt 0 ]; then
HAS_CHANGES=true
else
HAS_CHANGES=false
fi
# Output JSON
output_json "$HAS_CHANGES" "$STAGED_COUNT" "$UNSTAGED_COUNT" "$UNTRACKED_COUNT" "$TOTAL"
exit 0
}
# Run main function
main "$@"

View File

@@ -0,0 +1,173 @@
#!/usr/bin/env python3
"""
================================================================
Script: conflict-detector.py
Purpose: Detect and report merge conflicts
Version: 1.0.0
Usage: ./conflict-detector.py
Returns: JSON with conflict information
Exit Codes:
0 = Success (conflicts may or may not exist)
1 = Not a git repository
2 = Script error
================================================================
"""
import json
import subprocess
import sys
from datetime import datetime
from pathlib import Path
def run_git_command(command):
"""Run a git command and return output."""
try:
result = subprocess.run(
command,
shell=True,
capture_output=True,
text=True,
check=False
)
return result.returncode, result.stdout.strip(), result.stderr.strip()
except Exception as e:
return -1, "", str(e)
def check_repo_validity():
"""Check if current directory is a git repository."""
returncode, _, _ = run_git_command("git rev-parse --git-dir")
return returncode == 0
def get_conflicted_files():
"""Get list of files with merge conflicts."""
# Files with conflicts show up with 'U' status (unmerged)
returncode, stdout, _ = run_git_command("git ls-files -u")
if returncode != 0 or not stdout:
return []
# Extract unique filenames (git ls-files -u shows each stage)
conflicted_files = set()
for line in stdout.split('\n'):
if line.strip():
# Format: <mode> <object> <stage> <filename>
parts = line.split('\t')
if len(parts) > 1:
filename = parts[1]
conflicted_files.add(filename)
return sorted(conflicted_files)
def check_merge_in_progress():
"""Check if a merge operation is in progress."""
git_dir_code, git_dir, _ = run_git_command("git rev-parse --git-dir")
if git_dir_code != 0:
return False, None
git_dir_path = Path(git_dir)
# Check for various merge/rebase states
if (git_dir_path / "MERGE_HEAD").exists():
return True, "merge"
elif (git_dir_path / "REBASE_HEAD").exists():
return True, "rebase"
elif (git_dir_path / "CHERRY_PICK_HEAD").exists():
return True, "cherry-pick"
elif (git_dir_path / "REVERT_HEAD").exists():
return True, "revert"
return False, None
def get_conflict_details(files):
"""Get detailed information about conflicts in each file."""
details = []
for filepath in files:
try:
# Count conflict markers in file
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
conflict_count = content.count('<<<<<<<')
details.append({
"file": filepath,
"conflict_regions": conflict_count
})
except Exception:
# If can't read file, just include filename
details.append({
"file": filepath,
"conflict_regions": 0
})
return details
def main():
"""Main execution function."""
# Check if in git repository
if not check_repo_validity():
result = {
"has_conflicts": False,
"conflict_count": 0,
"conflicted_files": [],
"merge_in_progress": False,
"operation_type": None,
"error": "not a git repository",
"checked_at": datetime.now().isoformat()
}
print(json.dumps(result, indent=2))
sys.exit(1)
# Get conflicted files
conflicted_files = get_conflicted_files()
conflict_count = len(conflicted_files)
has_conflicts = conflict_count > 0
# Check merge status
merge_in_progress, operation_type = check_merge_in_progress()
# Get detailed conflict information
conflict_details = []
if has_conflicts:
conflict_details = get_conflict_details(conflicted_files)
# Build result
result = {
"has_conflicts": has_conflicts,
"conflict_count": conflict_count,
"conflicted_files": conflicted_files,
"conflict_details": conflict_details,
"merge_in_progress": merge_in_progress,
"operation_type": operation_type,
"error": "",
"checked_at": datetime.now().isoformat()
}
# Output JSON
print(json.dumps(result, indent=2))
sys.exit(0)
if __name__ == "__main__":
try:
main()
except Exception as e:
# Handle unexpected errors
result = {
"has_conflicts": False,
"conflict_count": 0,
"conflicted_files": [],
"merge_in_progress": False,
"operation_type": None,
"error": f"script error: {str(e)}",
"checked_at": datetime.now().isoformat()
}
print(json.dumps(result, indent=2))
sys.exit(2)

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env bash
# ================================================================
# Script: repo-checker.sh
# Purpose: Verify git repository validity
# Version: 1.0.0
# Usage: ./repo-checker.sh
# Returns: JSON with repository status
# Exit Codes:
# 0 = Valid repository
# 1 = Not a repository
# 2 = Script error
# ================================================================
set -euo pipefail
# Function to output JSON
output_json() {
local is_repo=$1
local git_dir=$2
local error=$3
cat <<EOF
{
"is_repo": $is_repo,
"git_dir": $git_dir,
"error": "$error",
"checked_at": "$(date -Iseconds)"
}
EOF
}
# Main logic
main() {
# Check if git is installed
if ! command -v git &>/dev/null; then
output_json false "null" "git not installed"
exit 2
fi
# Try to get git directory
if GIT_DIR=$(git rev-parse --git-dir 2>/dev/null); then
# Valid repository
ABSOLUTE_GIT_DIR=$(cd "$GIT_DIR" && pwd)
output_json true "\"$ABSOLUTE_GIT_DIR\"" ""
exit 0
else
# Not a repository
output_json false "null" "not a git repository"
exit 1
fi
}
# Run main function
main "$@"

View File

@@ -0,0 +1,183 @@
#!/usr/bin/env bash
# ================================================================
# Script: state-analyzer.sh
# Purpose: Analyze repository state (HEAD, branch, remote status)
# Version: 1.0.0
# Usage: ./state-analyzer.sh
# Returns: JSON with repository state information
# Exit Codes:
# 0 = Success
# 1 = Not a git repository
# 2 = Script error
# ================================================================
set -euo pipefail
# Function to check HEAD state
check_head_state() {
if git symbolic-ref HEAD &>/dev/null; then
echo "attached"
else
echo "detached"
fi
}
# Function to get current branch
get_current_branch() {
local branch
branch=$(git branch --show-current 2>/dev/null)
if [ -z "$branch" ]; then
echo "null"
else
echo "\"$branch\""
fi
}
# Function to get current commit SHA
get_current_commit() {
git rev-parse --short HEAD 2>/dev/null || echo "unknown"
}
# Function to check remote status
check_remote_status() {
# Check if remote exists
if ! git remote &>/dev/null || [ -z "$(git remote)" ]; then
echo "no_remote"
return
fi
# Check if branch tracks remote
if ! git rev-parse --abbrev-ref @{upstream} &>/dev/null; then
echo "no_upstream"
return
fi
# Compare with upstream
local local_commit remote_commit
local_commit=$(git rev-parse HEAD 2>/dev/null)
remote_commit=$(git rev-parse @{upstream} 2>/dev/null)
if [ "$local_commit" = "$remote_commit" ]; then
echo "up_to_date"
else
# Check ahead/behind
local ahead behind
ahead=$(git rev-list --count @{upstream}..HEAD 2>/dev/null || echo "0")
behind=$(git rev-list --count HEAD..@{upstream} 2>/dev/null || echo "0")
if [ "$ahead" -gt 0 ] && [ "$behind" -gt 0 ]; then
echo "diverged"
elif [ "$ahead" -gt 0 ]; then
echo "ahead"
elif [ "$behind" -gt 0 ]; then
echo "behind"
else
echo "up_to_date"
fi
fi
}
# Function to get ahead/behind counts
get_ahead_behind_counts() {
if ! git rev-parse --abbrev-ref @{upstream} &>/dev/null; then
echo "0" "0"
return
fi
local ahead behind
ahead=$(git rev-list --count @{upstream}..HEAD 2>/dev/null || echo "0")
behind=$(git rev-list --count HEAD..@{upstream} 2>/dev/null || echo "0")
echo "$ahead" "$behind"
}
# Function to get remote name
get_remote_name() {
local remote
remote=$(git remote 2>/dev/null | head -1)
if [ -z "$remote" ]; then
echo "null"
else
echo "\"$remote\""
fi
}
# Function to get remote URL
get_remote_url() {
local remote_name
remote_name=$(git remote 2>/dev/null | head -1)
if [ -z "$remote_name" ]; then
echo "null"
return
fi
local url
url=$(git remote get-url "$remote_name" 2>/dev/null)
if [ -z "$url" ]; then
echo "null"
else
echo "\"$url\""
fi
}
# Function to check if working tree is clean
check_working_tree() {
if git diff-index --quiet HEAD -- 2>/dev/null; then
echo "clean"
else
echo "dirty"
fi
}
# Main function
main() {
# Verify we're in a git repository
if ! git rev-parse --git-dir &>/dev/null; then
cat <<EOF
{
"error": "not a git repository",
"is_repo": false
}
EOF
exit 1
fi
# Collect all state information
local head_state current_branch current_commit remote_status
local ahead behind remote_name remote_url working_tree
head_state=$(check_head_state)
current_branch=$(get_current_branch)
current_commit=$(get_current_commit)
remote_status=$(check_remote_status)
read -r ahead behind < <(get_ahead_behind_counts)
remote_name=$(get_remote_name)
remote_url=$(get_remote_url)
working_tree=$(check_working_tree)
# Output JSON
cat <<EOF
{
"is_repo": true,
"head_state": "$head_state",
"current_branch": $current_branch,
"current_commit": "$current_commit",
"remote_status": "$remote_status",
"ahead_by": $ahead,
"behind_by": $behind,
"remote_name": $remote_name,
"remote_url": $remote_url,
"working_tree": "$working_tree",
"checked_at": "$(date -Iseconds)"
}
EOF
exit 0
}
# Run main function
main "$@"