From 767cbf262e083191d1f06498ba42b9422a1ffc34 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 4 Sep 2026 11:06:03 +0200 Subject: [PATCH 1/4] fix(find_git_conflicts): actually label conflicting PRs and stop skipping past 500 * @priya-sundaram-dev #15181 * [x] Fix a script --- scripts/find_git_conflicts.sh | 77 +++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 9 deletions(-) diff --git a/scripts/find_git_conflicts.sh b/scripts/find_git_conflicts.sh index 8af33fa75279..3677f6163bf2 100755 --- a/scripts/find_git_conflicts.sh +++ b/scripts/find_git_conflicts.sh @@ -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[*]}")" From ce3783573684aa18e212fe1c5460965e78ccb666 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 4 Sep 2026 12:19:06 +0200 Subject: [PATCH 2/4] Workaround for mapfile Co-authored-by: priya-sundaram-dev --- scripts/find_git_conflicts.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/find_git_conflicts.sh b/scripts/find_git_conflicts.sh index 3677f6163bf2..a571da687933 100755 --- a/scripts/find_git_conflicts.sh +++ b/scripts/find_git_conflicts.sh @@ -34,7 +34,11 @@ if [[ "$DRY_RUN" != "1" ]]; then fi # First pass: one bulk call. Fast, but mergeable is often UNKNOWN. -mapfile -t rows < <( +# First pass: one bulk call. Fast, but mergeable is often UNKNOWN. +rows=() +while IFS= read -r row; do + rows+=("$row") +done < <( gh pr list --repo "$REPO" --state open --limit 5000 \ --json number,mergeable --jq '.[] | "\(.number)\t\(.mergeable)"' ) From e5281b6cf2fc37fd2daac9c6164c209fbc80da25 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 4 Sep 2026 12:19:22 +0200 Subject: [PATCH 3/4] Update scripts/find_git_conflicts.sh Co-authored-by: priya-sundaram-dev --- scripts/find_git_conflicts.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/find_git_conflicts.sh b/scripts/find_git_conflicts.sh index a571da687933..b8010d16869f 100755 --- a/scripts/find_git_conflicts.sh +++ b/scripts/find_git_conflicts.sh @@ -56,7 +56,7 @@ for row in "${rows[@]}"; do done # Second pass: re-query only the UNKNOWN PRs until GitHub finishes computing. -for pr in "${unknown[@]}"; do +for pr in ${unknown[@]+"${unknown[@]}"}; do mergeable="UNKNOWN" for _ in 1 2 3; do mergeable=$(gh pr view "$pr" --repo "$REPO" --json mergeable --jq '.mergeable') From 75e5d720c72a81fa92fabadc52ff22a68ed41199 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Fri, 4 Sep 2026 12:19:30 +0200 Subject: [PATCH 4/4] Update scripts/find_git_conflicts.sh Co-authored-by: priya-sundaram-dev --- scripts/find_git_conflicts.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/find_git_conflicts.sh b/scripts/find_git_conflicts.sh index b8010d16869f..8fe11abfe317 100755 --- a/scripts/find_git_conflicts.sh +++ b/scripts/find_git_conflicts.sh @@ -67,7 +67,7 @@ for pr in ${unknown[@]+"${unknown[@]}"}; do done # Label the conflicting PRs. -for pr in "${conflicting[@]}"; do +for pr in ${conflicting[@]+"${conflicting[@]}"}; do echo "PR #$pr has conflicts." if [[ "$DRY_RUN" != "1" ]]; then gh pr edit "$pr" --repo "$REPO" --add-label "$LABEL"