This is a GitHub Action that detects potential merge conflicts between open pull requests by analyzing overlapping file and line changes across repositories. It scans your organization or specified repositories, identifies PRs that modify the same files and overlapping line ranges, and generates detailed reports so authors can resolve conflicts before they become a problem.
This action, developed by GitHub OSPO for our internal use, is open-sourced for your potential benefit. Feel free to inquire about its usage by creating an issue in this repository.
- Scans open pull requests (including drafts) in the specified organization or repositories
- Fetches changed files and line ranges for each PR
- Performs pairwise comparison to find PRs that modify overlapping lines in the same files
- Filters out same-author conflicts - PRs by the same developer conflicting with each other are excluded
- Deduplicates alerts - Tracks conflict history to only notify on new or changed conflicts
- Optionally verifies conflicts using GitHub's merge simulation API
- Generates reports in Markdown and JSON format
- Opens issues in affected repositories to notify teams
- Posts PR comments - Optionally comments directly on conflicting PRs with details
- Sends targeted Slack notifications - One message per conflict with @mentions for affected authors
- As an OSPO team managing a large organization, I want early warning of merge conflicts across many active pull requests so that I can reduce developer friction.
- As a development team, I want to know when two PRs are modifying the same code before either one is merged so that we can coordinate our changes.
- As a CI/CD pipeline owner, I want to proactively detect cross-PR conflicts so that merge failures are caught before they block deployments.
- As a maintainer, I want authors to be notified of potential conflicts via Slack so that they can resolve overlapping changes promptly.
The action generates a Markdown report with a table of detected conflicts:
| PR A | PR B | Conflicting Files | Overlapping Lines | Authors |
|---|---|---|---|---|
| #123 Add new feature | #456 Refactor module | src/main.py |
L10-L25 | @alice, @bob |
| #789 Update config | #456 Refactor module | config/settings.yml |
L3-L8 | @carol, @bob |
If you need support using this project or have questions about it, please open up an issue in this repository. Requests made directly to GitHub staff or support team will be redirected here to open an issue. GitHub SLAs and support/services contracts do not apply to this repository.
All feedback regarding our GitHub Actions, as a whole, should be communicated through issues on our github-ospo repository.
- Create a repository to host this GitHub Action or select an existing repository.
- Select a best fit workflow file from the examples below.
- Copy that example into your repository (from step 1) and into the proper directory for GitHub Actions:
.github/workflows/directory with the file extension.yml(ie..github/workflows/pr-conflict-detector.yml) - Edit the environment variables from the sample workflow with your information. See the Configuration section for details on each option.
- Update the value of
GH_TOKEN. Do this by creating a GitHub API token with the required permissions, then create a repository secret where the name of the secret isGH_TOKENand the value is the API token. - Commit the workflow file to the default branch (often
masterormain). - Wait for the action to trigger based on the
scheduleentry or manually trigger the workflow as shown in the documentation.
Below are the allowed configuration options:
This action can be configured to authenticate with GitHub App Installation or Personal Access Token (PAT). If all configuration options are provided, the GitHub App Installation configuration has precedence. You can choose one of the following methods to authenticate:
| field | required | default | description |
|---|---|---|---|
GH_APP_ID |
True | "" |
GitHub Application ID. See documentation for more details. |
GH_APP_INSTALLATION_ID |
True | "" |
GitHub Application Installation ID. See documentation for more details. |
GH_APP_PRIVATE_KEY |
True | "" |
GitHub Application Private Key. See documentation for more details. |
GITHUB_APP_ENTERPRISE_ONLY |
False | false |
Set this input to true if your app is created in GHE and communicates with GHE. |
The required GitHub App permissions under Repository permissions are:
Pull Requests- Read and Write (Read: scan open pull requests and their changed files; Write: post PR comments whenENABLE_PR_COMMENTSis enabled)Contents- Read (needed to fetch file diffs and line ranges)Issues- Read and Write (needed to create conflict report issues)
| field | required | default | description |
|---|---|---|---|
GH_TOKEN |
True | "" |
The GitHub Token used to scan repositories. Must have read access to pull requests and contents, and write access to issues and pull requests for all repositories in scope. |
| field | required | default | description |
|---|---|---|---|
GH_ENTERPRISE_URL |
False | "" |
The URL of a GitHub Enterprise instance to use for authentication instead of github.com. Example: https://github.example.com |
ORGANIZATION |
True* | "" |
The name of the GitHub organization to scan for open pull requests. ie. github.com/github would be github |
REPOSITORY |
True* | "" |
A comma-separated list of repositories to scan in owner/repo format. ie. github-community-projects/pr-conflict-detector or owner/repo1,owner/repo2 |
INCLUDE_DRAFTS |
False | true |
If set to true, draft pull requests will be included in the conflict analysis. Set to false to skip draft PRs. |
VERIFY_CONFLICTS |
False | false |
If set to true, enables merge simulation verification using GitHub's API. Provides higher confidence results but requires additional API calls. |
EXEMPT_REPOS |
False | "" |
A comma-separated list of repositories to exclude from scanning. Example: owner/repo-to-skip,owner/another-repo |
EXEMPT_PRS |
False | "" |
A comma-separated list of PR numbers to exclude from conflict analysis. Example: 123,456,789 |
DRY_RUN |
False | false |
If set to true, the action will generate reports but skip issue creation, Slack notifications, PR comments, and state file modifications. Useful for testing. |
REPORT_TITLE |
False | PR Conflict Report |
The title used for the generated conflict report and any issues created. |
OUTPUT_FILE |
False | pr_conflict_report.md |
The filename for the generated Markdown report. |
SLACK_WEBHOOK_URL |
False | "" |
Slack incoming webhook URL for sending conflict notifications. See the Slack Integration section for setup instructions. |
SLACK_CHANNEL |
False | "" |
Override the default Slack channel configured in the webhook. Example: #pr-conflicts |
ENABLE_GITHUB_ACTIONS_STEP_SUMMARY |
False | true |
If set to true, the conflict report will be written to the GitHub Actions workflow summary for easy viewing in the Actions UI. |
FILTER_AUTHORS |
False | "" |
A comma-separated list of GitHub usernames. When set, only PRs authored by these users will be analyzed for conflicts. Useful for incremental rollout to specific teams. Example: alice,bob,charlie |
ENABLE_PR_COMMENTS |
False | false |
If set to true, the action will post comments on PRs about detected conflicts. Comments include conflicting files, line ranges, and links to the other PR. See PR Comments for details. |
*One of ORGANIZATION or REPOSITORY must be set.
This workflow runs on weekdays at 9 AM UTC and scans all repositories in the specified organization:
name: PR Conflict Detection
on:
schedule:
- cron: "0 9 * * 1-5" # Weekdays at 9 AM UTC
workflow_dispatch:
permissions:
contents: read
issues: write
pull-requests: write
jobs:
detect-conflicts:
name: Detect PR conflicts
runs-on: ubuntu-latest
steps:
- name: Detect PR Conflicts
uses: github-community-projects/pr-conflict-detector@v1
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
ORGANIZATION: my-org
INCLUDE_DRAFTS: "true"
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}name: PR Conflict Detection
on:
schedule:
- cron: "0 9 * * 1-5"
workflow_dispatch:
permissions:
contents: read
issues: write
pull-requests: write
jobs:
detect-conflicts:
name: Detect PR conflicts
runs-on: ubuntu-latest
steps:
- name: Detect PR Conflicts
uses: github-community-projects/pr-conflict-detector@v1
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
REPOSITORY: owner/repo-namename: PR Conflict Detection
on:
schedule:
- cron: "0 9 * * 1-5"
workflow_dispatch:
permissions:
contents: read
issues: write
pull-requests: write
jobs:
detect-conflicts:
name: Detect PR conflicts
runs-on: ubuntu-latest
steps:
- name: Detect PR Conflicts
uses: github-community-projects/pr-conflict-detector@v1
env:
GH_APP_ID: ${{ secrets.GH_APP_ID }}
GH_APP_INSTALLATION_ID: ${{ secrets.GH_APP_INSTALLATION_ID }}
GH_APP_PRIVATE_KEY: ${{ secrets.GH_APP_PRIVATE_KEY }}
REPOSITORY: "owner/repo1,owner/repo2,owner/repo3"
EXEMPT_REPOS: "owner/repo-to-skip"
VERIFY_CONFLICTS: "true"If you have a large monorepo with many contributors, you can use FILTER_AUTHORS to limit conflict detection to your team's PRs. This lets you roll out the action incrementally without affecting other teams:
name: PR Conflict Detection (My Team)
on:
schedule:
- cron: "0 9 * * 1-5"
workflow_dispatch:
permissions:
contents: read
issues: write
pull-requests: write
jobs:
detect-conflicts:
name: Detect PR conflicts
runs-on: ubuntu-latest
steps:
- name: Detect PR Conflicts
uses: github-community-projects/pr-conflict-detector@v1
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
REPOSITORY: my-org/company-monolith
FILTER_AUTHORS: "alice,bob,charlie,dana"
DRY_RUN: "true"As confidence grows, expand the author list or remove FILTER_AUTHORS entirely to cover all PRs.
The action uses a hybrid approach combining efficient file-based grouping with optional merge simulation:
For each open pull request, the action fetches the list of changed files and their modified line ranges. It then groups PRs by the files they modify and checks for overlapping line ranges within each group. This approach is efficient — grouping is O(n) and pairwise comparison only occurs within file groups — avoiding the cost of comparing every PR against every other PR.
When VERIFY_CONFLICTS is set to true, the action uses GitHub's API to attempt merge simulations for candidate conflicts identified in Phase 1. This provides higher confidence results by confirming that the overlapping changes would actually produce a merge conflict, but requires additional API calls.
- Scales to hundreds of open PRs per repository
- File grouping algorithm avoids O(n²) pairwise comparison across all PRs
- Rate limit aware with graceful handling of GitHub API limits
The action tracks conflict history in a .pr-conflict-state.json file committed to your repository. This prevents alert fatigue by only notifying about new or changed conflicts.
Each conflict is fingerprinted by:
- Repository name
- PR numbers (A and B)
- List of conflicting files
- First detection timestamp
On each run, the action:
- Loads the previous state file
- Compares current conflicts against historical state
- Categorizes conflicts as:
- New - Not seen before → Slack notification sent
- Changed - Same PR pair but different files → Slack notification sent
- Unchanged - Same PRs, same files → No notification (already alerted)
- Resolved - Was in state but not detected now → Logged for reference
- Updates the state file with current conflicts
- Auto-prunes fingerprints older than 42 days
Run 1: Detects 10 conflicts → All are new → 10 Slack messages sent
Run 2: Same 10 conflicts → All unchanged → No Slack messages
Run 3: 9 unchanged, 1 changed files → 1 Slack message for the changed conflict
Run 4: 2 conflicts resolved, 1 new → 1 Slack message for the new conflictConflicts where both PRs are authored by the same person are automatically filtered out. If Alice has PR #123 and PR #456 that conflict, she likely already knows about both and can manage the conflict herself when merging.
- Location:
.pr-conflict-state.jsonin the repository root - Format: JSON with conflict fingerprints
- Persistence: Committed to the repository after each run
- Dry run: When
DRY_RUN=true, the state file is not modified
The state file is automatically managed by the action - no manual intervention required.
The action sends targeted Slack notifications with @mentions to alert PR authors about conflicts. Each conflict gets its own message to avoid notification overload.
- Create a Slack incoming webhook for your workspace
- Add the webhook URL as a repository secret (e.g.,
SLACK_WEBHOOK_URL) - Set the
SLACK_WEBHOOK_URLenvironment variable in your workflow - Optionally set
SLACK_CHANNELto override the default channel configured in the webhook
For simple 2-PR conflicts:
<@alice> <@bob> Your PRs may conflict:
github/repo-name
#123 (Add authentication) ↔ #456 (Refactor auth module)
Files:
• `src/auth.py` (L10-L25, L42-L55)
• `src/middleware.py` (L100-L120)For multi-PR clusters (3+ PRs conflicting on same files):
<@alice> <@bob> <@charlie> Your PRs may conflict:
github/repo-name — Cluster: 3 PRs, 3 conflict pair(s)
PRs:
• #123 Add authentication
• #456 Refactor auth module
• #789 Update auth tests
Shared files: `src/auth.py`, `src/middleware.py`- One message per conflict - Users only see conflicts relevant to them
- @mentions - Authors are mentioned (assumes GitHub username = Slack username)
- Line ranges - Shows exactly where overlaps occur
- Deduplication - Only sends for new or changed conflicts (see Deduplication)
- Same-author filtering - No notifications for conflicts between your own PRs
The action can post comments directly on pull requests to notify authors about conflicts. This provides in-context notifications that developers see when reviewing their PRs.
- Ensure your GitHub token has write access to pull requests
- Set
ENABLE_PR_COMMENTS=truein your workflow environment variables - The action will automatically post comments on both PRs in each conflict pair
Each PR receives a comment like this:
## ⚠️ Potential Merge Conflict Detected
This PR may conflict with [#456](https://github.com/org/repo/pull/456) (Refactor auth module).
### Conflicting Files
- `src/auth.py` (lines: L10-L25, L42-L55)
- `src/middleware.py` (lines: L100-L120)
### What to do
- Review the overlapping changes in the files above
- Coordinate with @bob to resolve conflicts
- Consider rebasing or merging to test compatibility
This is an automated notification from pr-conflict-detector.- Each comment includes a hidden bot signature (
<!-- pr-conflict-detector-bot -->) - Before posting, the action checks if a comment already exists for this specific conflict
- If found (signature + other PR number present), the comment is skipped
- Works with deduplication: only comments on new or changed conflicts
- Two-way notification - Both PRs in the conflict get a comment
- Detailed context - Shows exact files and line ranges
- Smart deduplication - Won't spam PRs with duplicate comments
- Actionable guidance - Tells developers what to do next
- Graceful error handling - If comment check fails, assumes no duplicate to avoid blocking
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
ORGANIZATION: my-org
ENABLE_PR_COMMENTS: "true" # Enable PR comments
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}# Clone the repository
git clone https://github.com/github-community-projects/pr-conflict-detector.git
cd pr-conflict-detector
# Set up environment
cp .env-example .env
# Edit .env with your configuration
# Install dependencies
uv sync
# Run locally
uv run pr_conflict_detector.py
# Run tests
make test
# Run linting
make lintPlease see CONTRIBUTING.md for details on how to contribute to this project, including information on reporting bugs, suggesting enhancements, and submitting pull requests.
This project uses Conventional Commits for commit messages.