/ scripts / rewrite-commit-messages.sh
rewrite-commit-messages.sh
 1  #!/usr/bin/env bash
 2  # Normalize a single commit message (stdin -> stdout) to conventional-commit format.
 3  # Used with: git filter-branch -f --msg-filter 'scripts/rewrite-commit-messages.sh' -- --all
 4  # WARNING: Rewrites history (new SHAs). After running, use: git push --force-with-lease
 5  # Make a backup first: git clone --mirror . ../.dotfiles-backup.git
 6  
 7  # Read full message first so we can split (head/tail both read stdin)
 8  msg=$(cat)
 9  first_line=$(echo "$msg" | head -n1)
10  rest=$(echo "$msg" | tail -n +2)
11  
12  [ -z "$first_line" ] && {
13    echo
14    [ -n "$rest" ] && echo "$rest"
15    exit 0
16  }
17  
18  # Apply same normalization rules as cliff.toml commit_preprocessors (order matters).
19  # Use [ \t]* for portability (BSD/macOS sed).
20  first_line=$(echo "$first_line" | sed \
21    -e 's/^\[FIX\][ \t]*/fix: /' \
22    -e 's/^\[UPDATE\][ \t]*/chore: /' \
23    -e 's/^Merge pull request/chore: Merge pull request/' \
24    -e 's/^Merge branch/chore: Merge branch/' \
25    -e 's/^[Uu]pdates\(.*\)/chore: updates\1/' \
26    -e 's/^[Uu]pdating/chore: Updating/' \
27    -e 's/^Updated /chore: Updated /' \
28    -e 's/^Added /chore: Added /' \
29    -e 's/^Add /feat: Add /' \
30    -e 's/^Fixed /fix: Fixed /' \
31    -e 's/^Improved /chore: Improved /' \
32    -e 's/^Small /chore: Small /' \
33    -e 's/^Minor /chore: Minor /' \
34    -e 's/^remove /chore: remove /' \
35    -e 's/^Remove /chore: Remove /' \
36    -e 's/^Initial /chore: Initial /' \
37    -e 's/^Inital /chore: Initial /' \
38    -e 's/^Changed /chore: Changed /' \
39    -e 's/^Replaced /chore: Replaced /' \
40    -e 's/^Replace /chore: Replace /' \
41    -e 's/^Moved /chore: Moved /' \
42    -e 's/^Update /chore: Update /' \
43    -e 's/^Replacing /chore: Replacing /' \
44    -e 's/^add /chore: add /' \
45    -e 's/^Borrowed /chore: Borrowed /' \
46    -e 's/^Dual /chore: Dual /' \
47    -e 's/^i3block /chore: i3block /')
48  
49  # If still not conventional (type: or type(scope):), prefix with chore:
50  if ! echo "$first_line" | grep -qE '^(feat|fix|chore|doc|refactor|perf|style|test|revert|ci)(\([^)]*\))?!?:'; then
51    first_line="chore: $first_line"
52  fi
53  
54  printf '%s\n' "$first_line"
55  [ -n "$rest" ] && printf '%s\n' "$rest"
56  exit 0