feat: add pre-push PR size check local hook#2939
Conversation
Add local pre-push hook that mirrors the remote PR Size Check workflow. Warns when push exceeds 500 LOC (excluding tests, config, and non-source files). Exclusions match pr-size-check-reusable.yml exactly. Setup: run 'make setup' to enable the hook. Bypass: git push --no-verify Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| @@ -0,0 +1,91 @@ | |||
| #!/usr/bin/env bash | |||
There was a problem hiding this comment.
This pull request does not update CHANGELOG.md.
Please consider if this change would be noticeable to a partner or user and either update CHANGELOG.md or resolve this conversation.
There was a problem hiding this comment.
Pull request overview
This PR adds local Git hooks to provide an early, local version of the repository’s PR Size Check before developers push changes, plus a make setup helper to enable the hooks.
Changes:
- Add a reusable
pr-size-check.shhook script with common exclusion patterns and a line-change threshold. - Add a repo-specific
pre-pushhook that supplies MSAL-specific path exclusions and sources the reusable logic. - Add a
Makefiletarget (make setup) to configurecore.hooksPathto.githooks.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| Makefile | Adds setup target intended to configure core.hooksPath for local hooks. |
| .githooks/pre-push | Defines MSAL-specific exclusions and sources the reusable size-check logic. |
| .githooks/pr-size-check.sh | Implements local line-change counting and exclusion filtering with an interactive warning when over limit. |
| git config core.hooksPath .githooks | ||
| @echo "✅ Git hooks configured. Pre-push PR size check is now active." |
There was a problem hiding this comment.
Issue: The Makefile recipe lines under setup are not indented with a tab, so make setup will error with “missing separator”.
Impact: The documented setup step won’t work, so contributors won’t be able to enable the hooks via make setup.
Recommendation: Indent each recipe line with a literal tab (or set .RECIPEPREFIX and use that prefix consistently) so the target executes correctly.
| git config core.hooksPath .githooks | |
| @echo "✅ Git hooks configured. Pre-push PR size check is now active." | |
| git config core.hooksPath .githooks | |
| @echo "✅ Git hooks configured. Pre-push PR size check is now active." |
| fi | ||
|
|
||
| # ── Get list of changed files (excluding patterns) ─────────── | ||
| mapfile -t CHANGED_FILES < <(git diff --name-only "$BASE_BRANCH"...HEAD 2>/dev/null | grep -vE "$GREP_PATTERN") |
There was a problem hiding this comment.
Issue: mapfile -t is used to read changed files, but mapfile is not available in the default macOS /bin/bash (3.2).
Impact: The pre-push hook will fail for many macOS developers unless they’ve installed a newer Bash and it’s first in PATH.
Recommendation: Replace mapfile with a Bash 3.2-compatible loop (e.g., while IFS= read -r ...; do ...; done) or explicitly document/enforce a minimum Bash version and resolve Bash path accordingly.
| mapfile -t CHANGED_FILES < <(git diff --name-only "$BASE_BRANCH"...HEAD 2>/dev/null | grep -vE "$GREP_PATTERN") | |
| CHANGED_FILES=() | |
| while IFS= read -r changed_file; do | |
| CHANGED_FILES+=("$changed_file") | |
| done < <(git diff --name-only "$BASE_BRANCH"...HEAD 2>/dev/null | grep -vE "$GREP_PATTERN") |
| echo " To push anyway: git push --no-verify" | ||
| echo " To cancel: Ctrl+C" | ||
| echo "" | ||
| read -r -p " Push anyway? [y/N]: " response |
There was a problem hiding this comment.
Issue: The confirmation prompt uses read from stdin, but for pre-push hooks stdin is the ref-update list provided by Git (not the terminal). When over the limit, read will hit EOF / consume hook input and default to cancelling without giving the user a chance to answer.
Impact: Large pushes can be incorrectly blocked (or behave unpredictably) even when a user intends to proceed.
Recommendation: Read from the controlling terminal (e.g., read ... < /dev/tty) and add a non-interactive fallback (e.g., if no TTY, print the warning and either allow the push or fail with a clear message).
| read -r -p " Push anyway? [y/N]: " response | |
| response="" | |
| if [ -r /dev/tty ]; then | |
| read -r -p " Push anyway? [y/N]: " response < /dev/tty | |
| else | |
| echo "Non-interactive environment detected; cannot prompt for confirmation." | |
| echo "Push cancelled." | |
| exit 1 | |
| fi |
| # ── Common exclusions (all repos) ──────────────────────────── | ||
| COMMON_EXCLUDES=( | ||
| "\.pbxproj$" | ||
| "\.xcscheme$" | ||
| "\.xcsettings$" | ||
| "\.xcconfig$" | ||
| "\.xctestplan$" | ||
| "\.xcworkspace/" | ||
| "\.xcodeproj/" | ||
| "\.plist$" | ||
| "\.entitlements$" | ||
| "\.storyboard$" | ||
| "\.xib$" | ||
| "\.xcassets/" | ||
| "\.modulemap$" | ||
| "\.xcprivacy$" | ||
| "\.strings$" | ||
| "\.stringsdict$" | ||
| "\.xcstrings$" | ||
| "\.ya?ml$" | ||
| "\.lock$" | ||
| "\.(png|jpg|jpeg|svg|pdf|icns|gif|tiff)$" | ||
| "\.md$" | ||
| "\.mdx$" | ||
| ) |
There was a problem hiding this comment.
Issue: The local hook’s COMMON_EXCLUDES doesn’t match the repo’s “single source of truth” workflow exclusions (e.g., it additionally excludes *.strings, *.stringsdict, and *.xcstrings).
Impact: Local warnings won’t mirror the GitHub PR Size Check results, which can lead to false negatives (no local warning but CI fails) or inconsistent expectations.
Recommendation: Align this list exactly with .github/workflows/pr-size-check-reusable.yml common exclusions, or clearly document any intentional differences in the hook header comment.
| fi | ||
|
|
||
| # ── Get list of changed files (excluding patterns) ─────────── | ||
| mapfile -t CHANGED_FILES < <(git diff --name-only "$BASE_BRANCH"...HEAD 2>/dev/null | grep -vE "$GREP_PATTERN") |
There was a problem hiding this comment.
Issue: Exclusion matching is case-sensitive (grep -vE), while the GitHub workflow compiles exclusions with the /i flag (case-insensitive).
Impact: The local size check can count files the workflow would exclude (or vice versa) if filenames use different casing (e.g., README.MD).
Recommendation: Make the exclusion filter case-insensitive (e.g., use grep -viE or normalize filenames) to better mirror the workflow behavior.
| read -r -p " Push anyway? [y/N]: " response | ||
| if [[ ! "$response" =~ ^[Yy]$ ]]; then | ||
| echo "Push cancelled." | ||
| exit 1 |
There was a problem hiding this comment.
In a pre-push hook, stdin is used by Git to pass the list of refs being pushed. The prompt read -r -p … response will read from that stdin (not from the user’s terminal), so when over the limit the hook will typically auto-decline and cancel the push.
Impact: pushes over the threshold can be blocked unexpectedly, and the user may never see a usable prompt.
Recommendation: read user input from /dev/tty (and/or only prompt when a TTY is available; otherwise just print a warning and allow the push).
| read -r -p " Push anyway? [y/N]: " response | |
| if [[ ! "$response" =~ ^[Yy]$ ]]; then | |
| echo "Push cancelled." | |
| exit 1 | |
| if [ -r /dev/tty ]; then | |
| printf " Push anyway? [y/N]: " > /dev/tty | |
| read -r response < /dev/tty | |
| if [[ ! "$response" =~ ^[Yy]$ ]]; then | |
| echo "Push cancelled." | |
| exit 1 | |
| fi | |
| else | |
| echo "ℹ️ No interactive terminal available; allowing push to continue." |
| # ── Get list of changed files (excluding patterns) ─────────── | ||
| mapfile -t CHANGED_FILES < <(git diff --name-only "$BASE_BRANCH"...HEAD 2>/dev/null | grep -vE "$GREP_PATTERN") | ||
|
|
||
| if [ "${#CHANGED_FILES[@]}" -eq 0 ]; then | ||
| exit 0 | ||
| fi | ||
|
|
||
| # ── Count lines changed ─────────────────────────────────────── | ||
| TOTAL_LINES=$(git diff "$BASE_BRANCH"...HEAD -- "${CHANGED_FILES[@]}" 2>/dev/null \ | ||
| | grep -E "^\+|^-" \ | ||
| | grep -vE "^\+\+\+|^---" \ | ||
| | wc -l \ | ||
| | tr -d ' ') | ||
|
|
||
| # ── Warn if over limit ──────────────────────────────────────── | ||
| if [ "$TOTAL_LINES" -gt "$MAX_LINES" ]; then | ||
| echo "" | ||
| echo "⚠️ WARNING: Your push contains ~${TOTAL_LINES} line changes (limit: ${MAX_LINES})." | ||
| echo " Consider splitting this into smaller PRs." | ||
| echo "" | ||
| echo " To push anyway: git push --no-verify" | ||
| echo " To cancel: Ctrl+C" | ||
| echo "" | ||
| read -r -p " Push anyway? [y/N]: " response | ||
| if [[ ! "$response" =~ ^[Yy]$ ]]; then | ||
| echo "Push cancelled." | ||
| exit 1 | ||
| fi | ||
| fi | ||
|
|
There was a problem hiding this comment.
The diff comparisons are hard-coded to ...HEAD, but a pre-push hook can push refs that aren’t the currently checked-out HEAD (e.g., git push origin other-branch, multiple refs, tags). This means the size check can run against the wrong commit range.
Impact: false positives/negatives (blocking or allowing pushes incorrectly).
Recommendation: consume the ref list provided on stdin by pre-push and compute the diff for the actual local SHA(s) being pushed (or skip non-branch pushes explicitly).
| # ── Get list of changed files (excluding patterns) ─────────── | |
| mapfile -t CHANGED_FILES < <(git diff --name-only "$BASE_BRANCH"...HEAD 2>/dev/null | grep -vE "$GREP_PATTERN") | |
| if [ "${#CHANGED_FILES[@]}" -eq 0 ]; then | |
| exit 0 | |
| fi | |
| # ── Count lines changed ─────────────────────────────────────── | |
| TOTAL_LINES=$(git diff "$BASE_BRANCH"...HEAD -- "${CHANGED_FILES[@]}" 2>/dev/null \ | |
| | grep -E "^\+|^-" \ | |
| | grep -vE "^\+\+\+|^---" \ | |
| | wc -l \ | |
| | tr -d ' ') | |
| # ── Warn if over limit ──────────────────────────────────────── | |
| if [ "$TOTAL_LINES" -gt "$MAX_LINES" ]; then | |
| echo "" | |
| echo "⚠️ WARNING: Your push contains ~${TOTAL_LINES} line changes (limit: ${MAX_LINES})." | |
| echo " Consider splitting this into smaller PRs." | |
| echo "" | |
| echo " To push anyway: git push --no-verify" | |
| echo " To cancel: Ctrl+C" | |
| echo "" | |
| read -r -p " Push anyway? [y/N]: " response | |
| if [[ ! "$response" =~ ^[Yy]$ ]]; then | |
| echo "Push cancelled." | |
| exit 1 | |
| fi | |
| fi | |
| # ── Evaluate the actual refs being pushed from pre-push stdin ──────────────── | |
| while read -r local_ref local_sha remote_ref remote_sha; do | |
| if [ -z "$local_ref" ] || [ -z "$local_sha" ]; then | |
| continue | |
| fi | |
| # Skip deleted refs and non-branch pushes (for example, tags). | |
| if [ "$local_sha" = "0000000000000000000000000000000000000000" ]; then | |
| continue | |
| fi | |
| if [[ "$local_ref" != refs/heads/* ]]; then | |
| continue | |
| fi | |
| # ── Get list of changed files for the ref being pushed (excluding patterns) ─ | |
| mapfile -t CHANGED_FILES < <(git diff --name-only "$BASE_BRANCH"..."$local_sha" 2>/dev/null | grep -vE "$GREP_PATTERN") | |
| if [ "${#CHANGED_FILES[@]}" -eq 0 ]; then | |
| continue | |
| fi | |
| # ── Count lines changed for the ref being pushed ──────────────────────────── | |
| TOTAL_LINES=$(git diff "$BASE_BRANCH"..."$local_sha" -- "${CHANGED_FILES[@]}" 2>/dev/null \ | |
| | grep -E "^[+-]" \ | |
| | grep -vE "^\+\+\+|^---" \ | |
| | wc -l \ | |
| | tr -d ' ') | |
| # ── Warn if over limit ────────────────────────────────────────────────────── | |
| if [ "$TOTAL_LINES" -gt "$MAX_LINES" ]; then | |
| echo "" | |
| echo "⚠️ WARNING: Push for ${local_ref#refs/heads/} contains ~${TOTAL_LINES} line changes (limit: ${MAX_LINES})." | |
| echo " Consider splitting this into smaller PRs." | |
| echo "" | |
| echo " To push anyway: git push --no-verify" | |
| echo " To cancel: Ctrl+C" | |
| echo "" | |
| read -r -p " Push anyway? [y/N]: " response | |
| if [[ ! "$response" =~ ^[Yy]$ ]]; then | |
| echo "Push cancelled." | |
| exit 1 | |
| fi | |
| fi | |
| done |
| "\.xcstrings$" | ||
| "\.ya?ml$" | ||
| "\.lock$" | ||
| "\.(png|jpg|jpeg|svg|pdf|icns|gif|tiff)$" |
There was a problem hiding this comment.
The common exclusion patterns here don’t match the updated reusable workflow’s exclusions (e.g., the workflow now excludes a broader set of image formats plus audio/video extensions, and uses slightly different tiff?/jpe?g patterns). This conflicts with the PR goal of mirroring the remote PR Size Check logic.
Impact: local pre-push results can disagree with CI, reducing trust in the early feedback.
Recommendation: update the COMMON_EXCLUDES list to stay in sync with .github/workflows/pr-size-check-reusable.yml (ideally keep a single shared source or add a sync check).
| "\.(png|jpg|jpeg|svg|pdf|icns|gif|tiff)$" | |
| "\.(png|apng|avif|bmp|gif|heic|heif|icns|ico|jpe?g|jp2|pdf|svg|tiff?|webp|aac|flac|m4a|mp3|ogg|wav|avi|m4v|mkv|mov|mp4|webm)$" |
3b76195 to
acefd3f
Compare
- Add extended image formats (bmp, webp, heic, heif, ico) - Add audio/video exclusions (mp4, mov, mp3, wav, etc.) - Add binary/submodule detection (warns on non-excluded binaries) - Use git diff --numstat for accurate line counting - Handle non-interactive shells gracefully Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| # ── Common exclusions (all repos) ──────────────────────────── | ||
| # Kept in sync with .github/workflows/pr-size-check-reusable.yml | ||
| COMMON_EXCLUDES=( | ||
| "\.pbxproj$" | ||
| "\.xcscheme$" | ||
| "\.xcsettings$" | ||
| "\.xcconfig$" | ||
| "\.xctestplan$" | ||
| "\.xcworkspace/" | ||
| "\.xcodeproj/" | ||
| "\.plist$" | ||
| "\.entitlements$" | ||
| "\.storyboard$" | ||
| "\.xib$" | ||
| "\.xcassets/" | ||
| "\.modulemap$" | ||
| "\.xcprivacy$" | ||
| "\.strings$" | ||
| "\.stringsdict$" | ||
| "\.xcstrings$" | ||
| "\.ya?ml$" | ||
| "\.lock$" | ||
| "\.(png|jpe?g|gif|bmp|svg|webp|heic|heif|tiff?|ico|pdf|icns)$" | ||
| "\.(mp4|mov|m4v|avi|mpe?g|webm|mp3|wav|aiff?|m4a)$" | ||
| "\.md$" | ||
| "\.mdx$" |
There was a problem hiding this comment.
The comment says COMMON_EXCLUDES is “Kept in sync with .github/workflows/pr-size-check-reusable.yml”, but the patterns don’t currently match (e.g., the workflow excludes only a subset of image extensions and does not list .strings/.stringsdict/.xcstrings, while this hook does; the workflow’s media list differs as well).
Impact: Local pre-push warnings may disagree with CI PR Size Check results, creating confusion and reducing trust in the hook.
Recommendation: Update COMMON_EXCLUDES to exactly mirror the workflow’s COMMON_EXCLUDE list, or generate both from a single source of truth to prevent drift.
| if [ -t 1 ] && [ -r /dev/tty ]; then | ||
| read -r -p " Push anyway? [y/N]: " response < /dev/tty | ||
| if [[ ! "$response" =~ ^[Yy]$ ]]; then | ||
| echo "Push cancelled." | ||
| exit 1 | ||
| fi | ||
| else | ||
| echo " Non-interactive shell detected; allowing push." |
There was a problem hiding this comment.
Interactive detection uses -t 1 (stdout) but the prompt reads from /dev/tty. In cases where stdout is redirected (but a controlling TTY exists), this will incorrectly treat the shell as non-interactive and allow the push without prompting.
Impact: The “confirm or cancel” behavior becomes unreliable, especially in scripted or wrapped git invocations that still have a TTY.
Recommendation: Gate prompting on the TTY you actually read from (e.g., check -r /dev/tty and/or -t 0 / -t 2, or test -t /dev/tty where supported) rather than -t 1.
| if [ -t 1 ] && [ -r /dev/tty ]; then | |
| read -r -p " Push anyway? [y/N]: " response < /dev/tty | |
| if [[ ! "$response" =~ ^[Yy]$ ]]; then | |
| echo "Push cancelled." | |
| exit 1 | |
| fi | |
| else | |
| echo " Non-interactive shell detected; allowing push." | |
| if [ -r /dev/tty ] && [ -t 0 ] < /dev/tty; then | |
| read -r -p " Push anyway? [y/N]: " response < /dev/tty | |
| if [[ ! "$response" =~ ^[Yy]$ ]]; then | |
| echo "Push cancelled." | |
| exit 1 | |
| fi | |
| else | |
| echo " No interactive terminal available; allowing push." |
| MERGE_BASE=$(git merge-base HEAD "$BASE_BRANCH" 2>/dev/null) | ||
| if [ -z "$MERGE_BASE" ]; then | ||
| echo "ℹ️ PR size check skipped: no common ancestor with ${BASE_BRANCH}." | ||
| exit 0 | ||
| fi | ||
|
|
||
| # ── Count lines changed (excluding patterns & binaries) ────── | ||
| TOTAL_LINES=0 | ||
| while IFS=$'\t' read -r added deleted filepath; do | ||
| [ -z "$filepath" ] && continue | ||
|
|
||
| # Check exclusion first | ||
| excluded=false | ||
| for pattern in "${ALL_EXCLUDES[@]}"; do | ||
| if [[ "$filepath" =~ $pattern ]]; then | ||
| excluded=true | ||
| break | ||
| fi | ||
| done | ||
| [ "$excluded" = true ] && continue | ||
|
|
||
| # Binary/submodule: git diff --numstat reports "-" for these | ||
| if [ "$added" = "-" ] || [ "$deleted" = "-" ]; then | ||
| echo "⚠️ Binary or submodule change in non-excluded file: $filepath" | ||
| continue | ||
| fi | ||
|
|
||
| TOTAL_LINES=$((TOTAL_LINES + added + deleted)) | ||
| done < <(git diff --numstat "$MERGE_BASE"...HEAD 2>/dev/null) | ||
|
|
||
| # ── Warn if over limit ──────────────────────────────────────── | ||
| if [ "$TOTAL_LINES" -gt "$MAX_LINES" ]; then | ||
| echo "" | ||
| echo "⚠️ WARNING: Your push contains ~${TOTAL_LINES} line changes (limit: ${MAX_LINES})." |
There was a problem hiding this comment.
This pre-push logic always computes changes for HEAD (git merge-base HEAD … and git diff …HEAD). However, pre-push can be invoked when pushing refs that are not the currently checked-out branch (or multiple refs), and Git provides the local/remote refs and SHAs on stdin.
Impact: The hook can under/over-report the size of what’s actually being pushed, which defeats the purpose of a pre-push guard.
Recommendation: Parse the pre-push stdin lines and run the diff calculation for each local_sha being pushed (or at least use the SHA of the current ref being pushed) instead of hard-coding HEAD.
| MERGE_BASE=$(git merge-base HEAD "$BASE_BRANCH" 2>/dev/null) | |
| if [ -z "$MERGE_BASE" ]; then | |
| echo "ℹ️ PR size check skipped: no common ancestor with ${BASE_BRANCH}." | |
| exit 0 | |
| fi | |
| # ── Count lines changed (excluding patterns & binaries) ────── | |
| TOTAL_LINES=0 | |
| while IFS=$'\t' read -r added deleted filepath; do | |
| [ -z "$filepath" ] && continue | |
| # Check exclusion first | |
| excluded=false | |
| for pattern in "${ALL_EXCLUDES[@]}"; do | |
| if [[ "$filepath" =~ $pattern ]]; then | |
| excluded=true | |
| break | |
| fi | |
| done | |
| [ "$excluded" = true ] && continue | |
| # Binary/submodule: git diff --numstat reports "-" for these | |
| if [ "$added" = "-" ] || [ "$deleted" = "-" ]; then | |
| echo "⚠️ Binary or submodule change in non-excluded file: $filepath" | |
| continue | |
| fi | |
| TOTAL_LINES=$((TOTAL_LINES + added + deleted)) | |
| done < <(git diff --numstat "$MERGE_BASE"...HEAD 2>/dev/null) | |
| # ── Warn if over limit ──────────────────────────────────────── | |
| if [ "$TOTAL_LINES" -gt "$MAX_LINES" ]; then | |
| echo "" | |
| echo "⚠️ WARNING: Your push contains ~${TOTAL_LINES} line changes (limit: ${MAX_LINES})." | |
| # ── Read refs being pushed from pre-push stdin ──────────────── | |
| PUSH_REFS=() | |
| while IFS=' ' read -r local_ref local_sha remote_ref remote_sha; do | |
| [ -z "$local_ref" ] && continue | |
| PUSH_REFS+=("${local_ref}|${local_sha}|${remote_ref}|${remote_sha}") | |
| done | |
| if [ "${#PUSH_REFS[@]}" -eq 0 ]; then | |
| PUSH_REFS+=("HEAD|$(git rev-parse HEAD 2>/dev/null)||") | |
| fi | |
| # ── Count lines changed (excluding patterns & binaries) per ref ── | |
| TOTAL_LINES=0 | |
| MAX_PUSH_LINES=0 | |
| MAX_PUSH_REF="" | |
| PROCESSED_REFS=0 | |
| for push_ref in "${PUSH_REFS[@]}"; do | |
| IFS='|' read -r local_ref local_sha remote_ref remote_sha <<< "$push_ref" | |
| # Deleted ref; nothing local to diff. | |
| if [[ "$local_sha" =~ ^0+$ ]]; then | |
| continue | |
| fi | |
| MERGE_BASE=$(git merge-base "$local_sha" "$BASE_BRANCH" 2>/dev/null) | |
| if [ -z "$MERGE_BASE" ]; then | |
| echo "ℹ️ PR size check skipped for ${local_ref}: no common ancestor with ${BASE_BRANCH}." | |
| continue | |
| fi | |
| REF_TOTAL_LINES=0 | |
| while IFS=$'\t' read -r added deleted filepath; do | |
| [ -z "$filepath" ] && continue | |
| # Check exclusion first | |
| excluded=false | |
| for pattern in "${ALL_EXCLUDES[@]}"; do | |
| if [[ "$filepath" =~ $pattern ]]; then | |
| excluded=true | |
| break | |
| fi | |
| done | |
| [ "$excluded" = true ] && continue | |
| # Binary/submodule: git diff --numstat reports "-" for these | |
| if [ "$added" = "-" ] || [ "$deleted" = "-" ]; then | |
| echo "⚠️ Binary or submodule change in non-excluded file for ${local_ref}: $filepath" | |
| continue | |
| fi | |
| REF_TOTAL_LINES=$((REF_TOTAL_LINES + added + deleted)) | |
| done < <(git diff --numstat "$MERGE_BASE"...$local_sha 2>/dev/null) | |
| PROCESSED_REFS=$((PROCESSED_REFS + 1)) | |
| if [ "$REF_TOTAL_LINES" -gt "$MAX_PUSH_LINES" ]; then | |
| MAX_PUSH_LINES=$REF_TOTAL_LINES | |
| MAX_PUSH_REF="$local_ref" | |
| fi | |
| done | |
| if [ "$PROCESSED_REFS" -eq 0 ]; then | |
| echo "ℹ️ PR size check skipped: no pushable local refs with a common ancestor to ${BASE_BRANCH}." | |
| exit 0 | |
| fi | |
| TOTAL_LINES=$MAX_PUSH_LINES | |
| # ── Warn if over limit ──────────────────────────────────────── | |
| if [ "$TOTAL_LINES" -gt "$MAX_LINES" ]; then | |
| echo "" | |
| echo "⚠️ WARNING: Your push includes ${MAX_PUSH_REF} with ~${TOTAL_LINES} line changes (limit: ${MAX_LINES})." |
Adds a local git pre-push hook that warns developers when a push exceeds 500 changed lines, prompting to confirm or cancel. Fires on every push to any remote branch; bypassable via
git push --no-verify.New files
.githooks/pr-size-check.sh— Shared hook logic: resolves base branch, filters excluded file types, counts diff lines, prompts if over limit..githooks/pre-push— MSAL-specific entry point; setsEXTRA_EXCLUDESfor test dirs, IdentityCore submodule, and Samples/.Makefile—make setuptarget to activate hooks per clone.Developer setup
make setup # run once per clone