Skip to content

Add pre-push hook with PR size warning#1769

Open
swasti29 with Copilot wants to merge 11 commits into
devfrom
copilot/add-local-pre-push-hook
Open

Add pre-push hook with PR size warning#1769
swasti29 with Copilot wants to merge 11 commits into
devfrom
copilot/add-local-pre-push-hook

Conversation

Copilot AI commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

Adds a local git pre-push hook that warns developers when a push exceeds 500 changed lines, prompting to split into smaller PRs. Hook fires on every push to any remote branch and is bypassable via git push --no-verify.

Files added

  • .githooks/pr-size-check.sh — Shared hook logic: resolves base branch (origin/devorigin/mainorigin/master), filters out Xcode artifacts/configs/images, counts net line changes, and prompts if over limit
  • .githooks/pre-push — Repo-specific entry point; sets EXTRA_EXCLUDES to skip IdentityCore/tests/, IdentityCore/MSIDTestsHostApp/, and scripts/, then sources the shared script
  • Makefilesetup target that writes core.hooksPath = .githooks into the local git config

Activation (one-time per clone)

make setup

After this, every git push triggers the size check automatically. Developers who already have a clone need to run make setup once; new clones require the same one-time step.

Original prompt

Summary

Add a local pre-push git hook that warns developers when their changes exceed the recommended PR size (500 lines) before pushing to any remote branch.

Files to create

1. .githooks/pr-size-check.sh — Shared reusable logic (identical across all repos)

#!/bin/bash
# ============================================================
# PR Size Check — Reusable Hook Logic
#
# Do NOT call this directly. It is sourced by the pre-push hook
# with repo-specific config already set:
#
#   MAX_LINES          — line change limit (default: 500)
#   EXTRA_EXCLUDES     — bash array of additional regex patterns
# ============================================================

MAX_LINES="${MAX_LINES:-500}"

# ── Common exclusions (all repos) ────────────────────────────
COMMON_EXCLUDES=(
  "\.pbxproj$"
  "\.xcscheme$"
  "\.xcsettings$"
  "\.xcconfig$"
  "\.xctestplan$"
  "\.xcworkspace/"
  "\.xcodeproj/"
  "\.plist$"
  "\.entitlements$"
  "\.storyboard$"
  "\.xib$"
  "\.xcassets/"
  "\.modulemap$"
  "\.xcprivacy$"
  "\.ya?ml$"
  "\.lock$"
  "\.(png|jpg|jpeg|svg|pdf|icns|gif|tiff)$"
)

# ── Merge common + repo-specific exclusions ───────────────────
ALL_EXCLUDES=("${COMMON_EXCLUDES[@]}" "${EXTRA_EXCLUDES[@]}")

# ── Build grep pattern ────────────────────────────────────────
GREP_PATTERN=$(printf "|%s" "${ALL_EXCLUDES[@]}")
GREP_PATTERN="${GREP_PATTERN:1}"

# ── Resolve base branch — always origin/dev ──────────────────
BASE_BRANCH=""
for candidate in origin/dev origin/main origin/master; do
  if git show-ref --verify --quiet "refs/remotes/${candidate}"; then
    BASE_BRANCH="$candidate"
    break
  fi
done

if [ -z "$BASE_BRANCH" ]; then
  echo "ℹ️  PR size check skipped: no remote base branch found."
  exit 0
fi

# ── Get list of changed files (excluding patterns) ───────────
CHANGED_FILES=$(git diff --name-only "$BASE_BRANCH"...HEAD 2>/dev/null | grep -vE "$GREP_PATTERN")

if [ -z "$CHANGED_FILES" ]; 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

exit 0

2. .githooks/pre-push — Repo-specific hook for MSAL Common

#!/bin/bash
# ============================================================
# Pre-push hook — PR Size Soft Warning
# Repo: microsoft-authentication-library-common-for-objc
#
# Fires on every push to any remote branch.
# Bypass with: git push --no-verify
# ============================================================

EXTRA_EXCLUDES=(
  "^IdentityCore/tests/"
  "^IdentityCore/MSIDTestsHostApp/"
  "^scripts/"
)

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/pr-size-check.sh"

3. Makefile — Add setup target (create if not exists, or append if exists)

Check if a Makefile already exists in the repo root. If it does, append the setup target. If not, create it:

setup:
	git config core.hooksPath .githooks
	@echo "✅ Git hooks configured. Pre-push PR size check is now active."

.PHONY: setup

Important notes

  • The hook fires on every push to any remote branch — not limited to dev
  • It is a soft warning — developers can bypass with git push --no-verify
  • pr-size-check.sh must be executable: chmod +x .githooks/pr-size-check.sh
  • pre-push must be executable: chmod +x .githooks/pre-push
  • Base branch for diff is always origin/dev (falling back to origin/main then origin/master)

The following is the prior conversation context from the user's chat exploration (may be truncated):

User: do we have currently any pre push

Assistant: Let me check all 4 repos.

Assistant: Here is what exists across all 4 repos:

Repo .githooks/ exists pre-push pre-commit
MSAL ❌ No .githooks/ ❌ None ❌ None
MSAL Common ❌ No .githooks/ ❌ None ❌ None
Broker ✅ Exists ❌ None ✅ Has one (clang-format check)
WorkplaceJoin ❌ No .githooks/ ❌ None ❌ None

Key finding — Broker already has a pre-commit hook

#!/bin/sh

if git rev-parse --verify HEAD >/dev/null 2>&1
then
    against=HEAD
else
    against=$(git hash-object -t tree /dev/null)
fi

# Test clang-format
clangformatout=$(git clang-format -q)

exec 1>&2

if ...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

Comment thread .githooks/pr-size-check.sh Outdated
@@ -0,0 +1,86 @@
#!/bin/bash

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pull request does not update changelog.txt.

Please consider if this change would be noticeable to a partner or user and either update changelog.txt or resolve this conversation.

Copilot AI changed the title [WIP] Add a local pre-push git hook for PR size warning Add pre-push hook with PR size warning Apr 14, 2026
Copilot AI requested a review from swasti29 April 14, 2026 02:42
Add missing exclusions: .strings, .stringsdict, .xcstrings, .md, .mdx
to match pr-size-check-reusable.yml exactly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@swasti29
swasti29 marked this pull request as ready for review April 17, 2026 02:13
@swasti29
swasti29 requested a review from a team as a code owner April 17, 2026 02:13
@swasti29
swasti29 requested review from Copilot and sidudiki April 17, 2026 02:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a repo-local Git pre-push hook (enabled via core.hooksPath) to warn developers when a push is likely to result in an oversized PR, encouraging smaller/split PRs in this codebase.

Changes:

  • Added reusable .githooks/pr-size-check.sh to compute changed-line “churn” vs a base branch with file-pattern exclusions.
  • Added .githooks/pre-push entrypoint configuring repo-specific exclusions and sourcing the shared logic.
  • Added a Makefile setup target to configure core.hooksPath = .githooks locally.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
Makefile Adds make setup to enable local git hooks via core.hooksPath.
.githooks/pre-push Hook entrypoint that sets repo-specific exclusions and sources shared logic.
.githooks/pr-size-check.sh Implements base-branch selection, file filtering, line-change counting, and warning/prompt behavior.

Comment thread .githooks/pr-size-check.sh Outdated
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")

Copilot AI Apr 17, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mapfile isn't available in the default /bin/bash on macOS (bash 3.2), so this hook will fail for many developers. Consider replacing this with a bash-3-compatible approach (e.g., a while IFS= read -r loop or using CHANGED_FILES=( $(...) ) with proper quoting) so the hook works on stock macOS shells.

Suggested change
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")

Copilot uses AI. Check for mistakes.
Comment thread .githooks/pr-size-check.sh Outdated
Comment on lines +84 to +87
read -r -p " Push anyway? [y/N]: " response
if [[ ! "$response" =~ ^[Yy]$ ]]; then
echo "Push cancelled."
exit 1

Copilot AI Apr 17, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The prompt uses read from stdin, but pre-push hooks receive the refs-to-push on stdin. If the size exceeds the limit, this read will consume the first ref line (not user input) and typically cancel the push unexpectedly. Read from /dev/tty (when available) or fall back to a non-interactive warning-only behavior when no TTY is present.

Suggested change
read -r -p " Push anyway? [y/N]: " response
if [[ ! "$response" =~ ^[Yy]$ ]]; then
echo "Push cancelled."
exit 1
if [ -r /dev/tty ] && [ -w /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; continuing without confirmation."

Copilot uses AI. Check for mistakes.
Comment on lines +47 to +54
# ── Resolve base branch — tries origin/dev, origin/main, origin/master ──────
BASE_BRANCH=""
for candidate in origin/dev origin/main origin/master; do
if git show-ref --verify --quiet "refs/remotes/${candidate}"; then
BASE_BRANCH="$candidate"
break
fi
done

Copilot AI Apr 17, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Base branch resolution is hardcoded to origin/*, but the pre-push hook is invoked with the target remote name as $1 and can be run against remotes other than origin. As written, pushes to a non-origin remote may incorrectly skip the check even if that remote has dev/main/master. Consider using the provided remote name (with a fallback to origin) when building refs/remotes/<remote>/... for the base branch candidates.

Copilot uses AI. Check for mistakes.
Comment thread .githooks/pr-size-check.sh Outdated
Comment on lines +68 to +73
# ── Count lines changed ───────────────────────────────────────
TOTAL_LINES=$(git diff "$BASE_BRANCH"...HEAD -- "${CHANGED_FILES[@]}" 2>/dev/null \
| grep -E "^\+|^-" \
| grep -vE "^\+\+\+|^---" \
| wc -l \
| tr -d ' ')

Copilot AI Apr 17, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This counts total diff hunk lines (additions + deletions), not “net line changes”. If the PR description intends net change, the counting logic needs to be adjusted; otherwise, update wording (e.g., “total line churn”) so the warning matches what’s being measured.

Copilot uses AI. Check for mistakes.
Swasti Gupta and others added 6 commits April 16, 2026 21:19
- 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>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Aligns caller workflow exclude_paths with local pre-push hook.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Local hook EXTRA_EXCLUDES should match the caller workflow's exclude_paths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants