Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 68 additions & 9 deletions scripts/find_git_conflicts.sh
Original file line number Diff line number Diff line change
@@ -1,16 +1,75 @@
#!/bin/bash
#
# Find every open pull request that has git merge conflicts with the base
# branch and label it "git merge conflict".
#
# Why this is not a one-liner: GitHub computes PR mergeability *asynchronously*,
# so `gh pr list --json mergeable` frequently reports UNKNOWN for PRs it has not
# recomputed yet. Viewing a PR individually nudges GitHub to compute the value,
# so we re-query only the UNKNOWN PRs a few times before giving up. We also
# avoid `--limit 500`, which silently skips any PR past the 500th (this repo has
# well over 500 open PRs).
#
# Usage:
# scripts/find_git_conflicts.sh # label conflicting PRs
# DRY_RUN=1 scripts/find_git_conflicts.sh # list only, add no labels
#
# Environment overrides: REPO, LABEL, SLEEP (seconds between UNKNOWN retries).

# Replace with your repository (format: owner/repo)
REPO="TheAlgorithms/Python"
set -euo pipefail

REPO="${REPO:-TheAlgorithms/Python}"
LABEL="${LABEL:-git merge conflict}"
DRY_RUN="${DRY_RUN:-0}"
SLEEP="${SLEEP:-2}"

# Fetch open pull requests with conflicts into a variable
echo "Checking for pull requests with conflicts in $REPO..."

prs=$(gh pr list --repo "$REPO" --state open --json number,title,mergeable --jq '.[] | select(.mergeable == "CONFLICTING") | {number, title}' --limit 500)
# Make sure the label exists (idempotent; ignore "already exists").
if [[ "$DRY_RUN" != "1" ]]; then
gh label create "$LABEL" --repo "$REPO" \
--color "d93f0b" \
--description "This pull request has git merge conflicts with the base branch" \
2>/dev/null || true
fi

# First pass: one bulk call. Fast, but mergeable is often UNKNOWN.
mapfile -t rows < <(
gh pr list --repo "$REPO" --state open --limit 5000 \
--json number,mergeable --jq '.[] | "\(.number)\t\(.mergeable)"'
)
echo "Found ${#rows[@]} open pull requests to inspect."

conflicting=()
unknown=()
for row in "${rows[@]}"; do
number="${row%%$'\t'*}"
mergeable="${row##*$'\t'}"
case "$mergeable" in
CONFLICTING) conflicting+=("$number") ;;
UNKNOWN) unknown+=("$number") ;;
esac
done

# Process each conflicting PR
echo "$prs" | jq -c '.[]' | while read -r pr; do
PR_NUMBER=$(echo "$pr" | jq -r '.number')
PR_TITLE=$(echo "$pr" | jq -r '.title')
echo "PR #$PR_NUMBER - $PR_TITLE has conflicts."
# Second pass: re-query only the UNKNOWN PRs until GitHub finishes computing.
for pr in "${unknown[@]}"; do
mergeable="UNKNOWN"
for _ in 1 2 3; do
mergeable=$(gh pr view "$pr" --repo "$REPO" --json mergeable --jq '.mergeable')
[[ "$mergeable" != "UNKNOWN" ]] && break
sleep "$SLEEP"
done
[[ "$mergeable" == "CONFLICTING" ]] && conflicting+=("$pr")
done

# Label the conflicting PRs.
for pr in "${conflicting[@]}"; do
echo "PR #$pr has conflicts."
if [[ "$DRY_RUN" != "1" ]]; then
gh pr edit "$pr" --repo "$REPO" --add-label "$LABEL"
fi
done

# Machine-readable summary (mirrors the close_pull_requests_with_*.sh scripts).
printf 'CONFLICTING_COUNT=%d CONFLICTING_PRS=%s\n' \
"${#conflicting[@]}" "$(IFS=,; echo "${conflicting[*]}")"