{"id":48813297,"url":"https://github.com/lfit/dependamerge","last_synced_at":"2026-04-14T10:01:16.436Z","repository":{"id":296983248,"uuid":"994930941","full_name":"lfit/dependamerge","owner":"lfit","description":"Tool to merge pull requests generated by automation in GitHub","archived":false,"fork":false,"pushed_at":"2026-04-07T19:47:01.000Z","size":1540,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-07T21:27:51.114Z","etag":null,"topics":["code-review","dependabot","merge","pull-requests","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lfit.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"docs/SECURITY-IMPROVEMENTS-2026-03-25.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-06-02T17:49:55.000Z","updated_at":"2026-04-07T19:47:06.000Z","dependencies_parsed_at":"2025-06-19T14:46:07.042Z","dependency_job_id":"7ceced2f-f78d-4db0-818f-6085a7193afe","html_url":"https://github.com/lfit/dependamerge","commit_stats":null,"previous_names":["modesevenindustrialsolutions/dependamerge","tykeal/dependamerge"],"tags_count":33,"template":false,"template_full_name":null,"purl":"pkg:github/lfit/dependamerge","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lfit%2Fdependamerge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lfit%2Fdependamerge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lfit%2Fdependamerge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lfit%2Fdependamerge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lfit","download_url":"https://codeload.github.com/lfit/dependamerge/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lfit%2Fdependamerge/sbom","scorecard":{"id":996952,"data":{"date":"2025-08-27T16:47:10Z","repo":{"name":"github.com/lfit/dependamerge","commit":"47f9440f5582ee08e0dd963997238bd434422426"},"scorecard":{"version":"v5.2.1","commit":"ab2f6e92482462fe66246d9e32f642855a691dc1"},"score":5.9,"checks":[{"name":"Code-Review","score":3,"reason":"Found 3/10 approved changesets -- score normalized to 3","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#binary-artifacts"}},{"name":"Dependency-Update-Tool","score":10,"reason":"update tool detected","details":["Info: detected update tool: Dependabot: .github/dependabot.yml:1"],"documentation":{"short":"Determines if the project uses a dependency update tool.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#dependency-update-tool"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":10,"reason":"GitHub workflow tokens follow principle of least privilege","details":["Info: jobLevel 'contents' permission set to 'read': .github/workflows/build-test-release.yaml:147","Info: jobLevel 'contents' permission set to 'read': .github/workflows/build-test-release.yaml:176","Warn: jobLevel 'contents' permission set to 'write': .github/workflows/build-test-release.yaml:206","Warn: jobLevel 'contents' permission set to 'write': .github/workflows/build-test-release.yaml:240","Info: jobLevel 'contents' permission set to 'read': .github/workflows/build-test-release.yaml:22","Info: jobLevel 'contents' permission set to 'read': .github/workflows/build-test-release.yaml:59","Info: jobLevel 'contents' permission set to 'read': .github/workflows/build-test-release.yaml:92","Info: jobLevel 'contents' permission set to 'read': .github/workflows/build-test-release.yaml:119","Info: jobLevel 'contents' permission set to 'read': .github/workflows/build-test.yaml:65","Info: jobLevel 'contents' permission set to 'read': .github/workflows/build-test.yaml:92","Info: jobLevel 'contents' permission set to 'read': .github/workflows/build-test.yaml:37","Warn: jobLevel 'security-events' permission set to 'write': .github/workflows/codeql.yml:30","Info: jobLevel 'packages' permission set to 'read': .github/workflows/codeql.yml:32","Info: jobLevel 'actions' permission set to 'read': .github/workflows/codeql.yml:34","Info: jobLevel 'contents' permission set to 'read': .github/workflows/codeql.yml:35","Warn: jobLevel 'security-events' permission set to 'write': .github/workflows/openssf-scorecard.yaml:35","Warn: jobLevel 'contents' permission set to 'write': .github/workflows/release-drafter.yaml:56","Info: found token with 'none' permissions: .github/workflows/build-test-release.yaml:1","Info: found token with 'none' permissions: .github/workflows/build-test.yaml:1","Info: found token with 'none' permissions: .github/workflows/codeql.yml:1","Info: found token with 'none' permissions: .github/workflows/openssf-scorecard.yaml:1","Info: found token with 'none' permissions: .github/workflows/release-drafter.yaml:1"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"project was created within the last 90 days. Please review its contents carefully","details":["Warn: Repository was created within the last 90 days."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":10,"reason":"all dependencies are pinned","details":["Info:  10 out of  10 GitHub-owned GitHubAction dependencies pinned","Info:  24 out of  24 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#signed-releases"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#sast"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#fuzzing"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#security-policy"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#branch-protection"}},{"name":"Contributors","score":10,"reason":"project has 6 contributing companies or organizations","details":["Info: found contributions from: LF-Engineering, edgexfoundry, lfit, linux foundation modeseven, linuxfoundation, opendaylight"],"documentation":{"short":"Determines if the project has a set of contributors from multiple organizations (e.g., companies).","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#contributors"}},{"name":"CI-Tests","score":10,"reason":"16 out of 16 merged PRs checked by a CI test -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project runs tests before pull requests are merged.","url":"https://github.com/ossf/scorecard/blob/ab2f6e92482462fe66246d9e32f642855a691dc1/docs/checks.md#ci-tests"}}]},"last_synced_at":"2025-08-27T22:15:53.664Z","repository_id":296983248,"created_at":"2025-08-27T22:15:53.664Z","updated_at":"2025-08-27T22:15:53.664Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31791171,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-14T02:24:21.117Z","status":"ssl_error","status_checked_at":"2026-04-14T02:24:20.627Z","response_time":153,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["code-review","dependabot","merge","pull-requests","python"],"created_at":"2026-04-14T10:00:36.217Z","updated_at":"2026-04-14T10:01:16.424Z","avatar_url":"https://github.com/lfit.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--\nSPDX-License-Identifier: Apache-2.0\nSPDX-FileCopyrightText: 2025 The Linux Foundation\n--\u003e\n\n# Dependamerge\n\nCommand-line tool for the management of pull requests in a GitHub organization\nand changes on Gerrit Code Review servers.\n\n\u003c!-- markdownlint-disable MD013 --\u003e\n\n| Command | Description                                                              |\n| ------- | ------------------------------------------------------------------------ |\n| merge   | Bulk approve/merge pull requests (GitHub) or changes (Gerrit)            |\n| close   | Bulk close pull requests across a GitHub organization                    |\n| blocked | Reports blocked pull requests in a GitHub organization                   |\n| status  | Reports repository statistics for tags, releases, and PRs                |\n\n\u003c!-- markdownlint-enable MD013 --\u003e\n\n## Merge\n\nBulk approves/merges similar pull requests across different repositories in a\nGitHub organisation, or similar changes on a Gerrit Code Review server.\n\n### GitHub Mode\n\nBy default, bypasses code owner review requirements to enable automated merging\nof dependency updates. Supports common automation tools:\n\n- Dependabot\n- pre-commit.ci\n- Renovate\n\nAlso works for individual GitHub users when provided with an override flag.\n\nMatches pull requests based on a heuristic that considers the criteria:\n\n- Pull requests created by the same author/automation\n- Pull requests with the same title/body content\n- Pull requests containing the same package updates\n- Pull requests changing the same files\n\n### Gerrit Mode\n\nWhen provided with a Gerrit change URL, the tool will:\n\n- Detect the Gerrit server and authenticate using credentials\n- Fetch details of the source change\n- Search for similar open changes on the same server\n- Apply +2 Code-Review and submit matching changes\n\nGerrit URL format:\n\n```text\nhttps://gerrit.example.org/c/project/name/+/12345\nhttps://gerrit.example.org/base/c/project/name/+/12345\n```\n\nThe tool automatically detects whether a URL is for GitHub or Gerrit based on\nthe URL pattern (`/pull/` for GitHub, `/c/.../+/` for Gerrit).\n\n## Status\n\nReports repository statistics across a GitHub organization, including:\n\n- Latest tags and releases with synchronization status\n- Open and merged pull request counts\n- Pull requests affecting action files or workflow configurations\n- Separate counts for human contributors and automation tools\n\nHelps track release management and identify repositories needing\nattention. Supports both table and JSON output formats.\n\n## Blocked\n\nLists blocked pull requests across a GitHub organization. Useful when\nsuccessive merges have created conflicts or the need to rebase. Also\nlists pull requests blocked by branch protection rules, such as those\nwith failed CI jobs, tests, etc.\n\n## Close\n\nBulk closes similar pull requests across different repositories in a\nGitHub organisation. Works with the same automation tools as merge:\n\n- Dependabot\n- pre-commit.ci\n- Renovate\n\nAlso works for individual GitHub users when provided with an override flag.\n\nUses the same matching heuristic as merge to find similar PRs. Unlike merge,\nit requires PRs to be in the open state (no mergeable state checks needed).\n\n## Overview\n\nDependamerge provides four main functions:\n\n1. **Finding Blocked PRs**: Check entire GitHub organizations to identify\n   pull requests with conflicts, failing checks, or other blocking issues\n2. **Automated Merging (GitHub)**: Analyze a source pull request and find\n   similar pull requests across all repositories in the same GitHub\n   organization, then automatically approve and merge the matching PRs\n3. **Automated Merging (Gerrit)**: Analyze a source change on a Gerrit Code\n   Review server, find similar open changes, then apply +2 Code-Review and\n   submit matching changes\n4. **Bulk Closing**: Analyze a source pull request and find similar pull\n   requests across all repositories in the same GitHub organization, then\n   close all matching open PRs\n\nThis saves time on routine dependency updates, maintenance tasks, and\ncoordinated changes across all repositories while providing visibility into\nunmergeable PRs that need attention.\n\n**Works with any pull request or change** regardless of author, automation\ntool, or origin. The tool automatically detects whether a URL is for GitHub\nor Gerrit based on the URL pattern.\n\n## Features\n\n### Blocked Pull Requests in a GitHub Organisation\n\n- **Comprehensive PR Analysis**: Checks all repositories in a GitHub\n  organization for unmergeable pull requests\n- **Blocking Reason Detection**: Identifies specific reasons preventing PR\n  merges (conflicts, failing checks, blocked reviews)\n- **Copilot Integration**: Counts unresolved GitHub Copilot feedback comments\n  (column shown when present)\n- **Smart Filtering**: Excludes standard code review requirements, focuses on\n  technical blocking issues\n- **Detailed Reporting**: Provides comprehensive tables and summaries of\n  problematic PRs\n- **Real-time Progress**: Live progress display shows checking status and\n  current operations\n\n### Bulk Approval/Merging of Similar Pull Requests Across Repositories\n\n- **Universal PR Support**: Works with any pull request regardless of author\n  or automation tool\n- **Smart Matching**: Uses content similarity algorithms to match related PRs\n  across repositories\n- **Bulk Operations**: Approve and merge related similar PRs with a single\n  command\n- **Security Features**: SHA-based authentication for non-automation PRs\n  ensures authorized bulk merges\n- **Interactive Mode by Default**: Preview what changes will apply, then\n  optionally proceed with merge\n\n### General Features\n\n- **Rich CLI Output**: Beautiful terminal output with progress indicators and\n  tables\n- **Real-time Progress**: Live progress updates for both checking and merge\n  operations\n- **Output Formats**: Support for table and JSON output formats\n- **Error Handling**: Graceful handling of API rate limits and repository\n  access issues\n\n## Supported Pull Requests\n\n- Any pull request from any author\n- Manual pull requests from developers\n- Automation tool pull requests (Dependabot, Renovate, etc.)\n- Bot-generated pull requests\n- Coordinated changes across repositories\n\n## Installation (uv + hatch)\n\nThis project uses:\n\n- hatchling + hatch-vcs for dynamic (tag-based) versioning\n- uv for environment + dependency management (produces/consumes `uv.lock`)\n\n### Quick Start (Run Without Cloning)\n\nUse `uvx` to run the latest published version directly from PyPI\n(no virtualenv management needed):\n\n```bash\n# Show help (latest release)\nuvx dependamerge --help\n\n# Run a specific tagged release\nuvx dependamerge==0.1.0 merge https://github.com/owner/repo/pull/123\n```\n\n### Local Development Install\n\n```bash\n# 1. Install uv (if not already installed)\n# macOS/Linux (script):\ncurl -LsSf https://astral.sh/uv/install.sh | sh\n# or with pipx:\npipx install uv\n\n# 2. Clone the repository\ngit clone \u003crepository-url\u003e\ncd dependamerge\n\n# 3. Create \u0026 activate a virtual environment (optional but recommended)\nuv venv .venv\nsource .venv/bin/activate  # (On Windows: .venv\\Scripts\\activate)\n\n# 4. Install project + dev dependencies (uses dependency group 'dev')\nuv sync --group dev\n```\n\nThe first sync will generate `uv.lock`. Commit that file to ensure reproducible\nbuilds.\n\n### Editable Workflow\n\n`uv sync` installs the project in editable (PEP 660) mode automatically.\nAfter making changes you can run:\n\n```bash\nuv run dependamerge --help\n```\n\n### Building \u0026 Publishing\n\nDynamic version comes from Git tags (e.g. tag `v0.2.0` → version `0.2.0`):\n\n```bash\n# Build wheel + sdist\nuv build\n\n# (Optional) Inspect dist/\nls dist/\n\n# Publish to PyPI (ensure you have credentials configured)\nuv publish\n```\n\nIf you build before tagging, a local scheme like `0.0.0+local`\n(or similar) may appear—tag first for clean releases.\n\n### Updating / Adding Dependencies\n\nEdit `pyproject.toml` and then:\n\n```bash\nuv sync\n```\n\nTo add a dev dependency:\n\n```bash\nuv add --group dev pytest-cov\n```\n\n### Running a One-Off Version (Isolation)\n\n```bash\n# Run a specific version in an ephemeral environment\nuvx dependamerge==0.1.0 --help\n```\n\n## Authentication\n\nDependamerge supports both GitHub and Gerrit platforms, each with different\nauthentication requirements.\n\n### GitHub Authentication\n\nYou need a GitHub personal access token with appropriate permissions. The tool\nperforms both read and write operations on GitHub repositories and pull\nrequests.\n\n### Configuring a GitHub Personal Access Token\n\nDependamerge supports both **classic** and **fine-grained** personal access tokens.\n\nTo configure a GitHub personal access token for use with dependamerge, go to:\n\n\u003chttps://github.com/\u003e\n\nThen:\n\nProfile → Settings → Developer settings → Personal access tokens\n\n#### Option 1: Fine-Grained Personal Access Tokens (Recommended)\n\nFine-grained tokens → Generate new token\n\n**Required Repository Permissions:**\n\n- **Contents**: Read and write (for merging PRs and accessing file changes)\n- **Pull requests**: Read and write (for creating reviews, approving, and merging)\n- **Workflows**: Read and write (for PRs that change GitHub Actions workflows)\n- **Administration**: Read access (for reading branch protection rules)\n- **Metadata**: Read access (automatically included)\n\n**Required Account Permissions:**\n\n- **Organization members**: Read access (to access organization repositories)\n\n**Repository Access:**\n\n- Select \"All repositories\" or specify which repositories to access\n\n#### Option 2: Tokens (Classic)\n\n**Required Scopes:**\n\n- `read:org` - Read organization membership, teams, and repositories\n- `workflow` - Update GitHub Actions workflows (needed for PRs modifying workflows)\n\nOne of the two options below is also needed:\n\n- `public_repo` - Access to public repositories (if working with public repos)\n- `repo` - Full control of private repositories (includes all repository permissions)\n\n#### What the tool does with these permissions\n\n- **Read Operations**: Access PR details, file changes, reviews, commits, check\n  runs, and repository lists\n- **Write Operations**: Create PR reviews (approvals), merge pull requests,\n  update PR branches\n\n**Important Notes for Branch Protection:**\n\n- If repositories have **branch protection rules** enabled, these\n  requirements may apply:\n  - **Required status checks**: All CI/CD workflows must pass before merging\n  - **Required reviews**: PRs may need approval from code owners or specific teams\n  - **Up-to-date branches**: PRs may need to be current with the base branch\n  - **Copilot review resolution**: When using `--dismiss-copilot`, the tool automatically\n    handles all review types using dismissal or thread resolution as appropriate\n- **Default behavior**: By default, dependamerge uses `--force=code-owners` to bypass\n  code owner review requirements for automation PRs\n- For repositories with **strict branch protection**, use `--force=protection-rules`\n  or `--force=all`, though the token owner may need **admin permissions** on\n  individual repositories to bypass certain rules\n\n### Setting Up Authentication\n\nSet the token as an environment variable:\n\n```bash\nexport GITHUB_TOKEN=your_token_here\n```\n\nOr pass it directly to the command using `--token`.\n\n### Permission Verification\n\nTo verify your token has the correct permissions:\n\n```bash\n# Test basic access\ncurl -H \"Authorization: token $GITHUB_TOKEN\" https://api.github.com/user\n\n# Test organization access\ncurl -H \"Authorization: token $GITHUB_TOKEN\" \\\n  https://api.github.com/orgs/YOUR_ORG/repos\n```\n\n### Gerrit Authentication\n\nGerrit Code Review servers require HTTP credentials for API access. These are\ntypically different from your SSO/LDAP login credentials.\n\n#### Obtaining Gerrit HTTP Credentials\n\n1. Log into your Gerrit server web interface\n2. Navigate to: **Settings** → **HTTP Credentials**\n3. Generate a new HTTP password if you don't have one\n4. Note your HTTP username (often your email or username)\n\n#### Setting Up Gerrit Authentication\n\nSet the credentials as environment variables:\n\n```bash\nexport GERRIT_USERNAME=\"your_username\"\nexport GERRIT_PASSWORD=\"your_http_password\"\n```\n\nAlternative environment variable names are also supported for compatibility:\n\n```bash\nexport GERRIT_HTTP_USER=\"your_username\"\nexport GERRIT_HTTP_PASSWORD=\"your_http_password\"\n```\n\n**Note:** The `GERRIT_USERNAME`/`GERRIT_PASSWORD` variables take precedence\nover `GERRIT_HTTP_USER`/`GERRIT_HTTP_PASSWORD` when you configure both.\n\n#### Gerrit Permission Verification\n\nTo verify your Gerrit credentials:\n\n```bash\n# Test basic access (replace with your Gerrit server)\ncurl -u \"$GERRIT_USERNAME:$GERRIT_PASSWORD\" \\\n  \"https://gerrit.example.org/a/accounts/self\"\n```\n\nA successful response returns your account details in JSON format (with a\n`)]}'\\n` XSSI guard prefix that Gerrit adds to all JSON responses).\n\n#### Using .netrc Files\n\nDependamerge supports loading Gerrit credentials from `.netrc` files, following\nthe standard format used by curl and other tools.\n\n**Search order:**\n\n1. `.netrc` in the current directory\n2. `~/.netrc` in your home directory\n3. `~/_netrc` (Windows fallback)\n\n**Example `.netrc` file:**\n\n```text\nmachine gerrit.onap.org login myuser password mytoken\nmachine gerrit.opendaylight.org login myuser password anothertoken\n```\n\n**CLI options:**\n\n| Option | Description |\n| ------ | ----------- |\n| `--no-netrc` | Disable .netrc file lookup |\n| `--netrc-file PATH` | Use a specific .netrc file |\n| `--netrc-optional` | Do not fail if .netrc file is missing (default) |\n| `--netrc-required` | Require a .netrc file and fail if missing |\n\nBy default, `.netrc` lookup is optional (`--netrc-optional`): if no `.netrc`\nfile exists, the tool continues and falls back to environment variables.\nUse `--netrc-required` to enforce that a `.netrc` file must be present.\n\nWhen a `.netrc` file is present, the tool loads credentials automatically.\nExplicit environment variables or CLI arguments take precedence over `.netrc`\nentries.\n\n## Usage\n\n### Closing Pull Requests\n\nClose pull requests across an entire GitHub organization:\n\n```bash\n# Close similar PRs from automation tools (dependabot, pre-commit.ci)\ndependamerge close https://github.com/myorg/repo1/pull/45\n\n# Close with no confirmation (immediate closing)\ndependamerge close https://github.com/myorg/repo1/pull/45 --no-confirm\n\n# Close with custom similarity threshold\ndependamerge close https://github.com/myorg/repo1/pull/45 --threshold 0.9\n\n# Close user-generated PRs with override SHA\ndependamerge close https://github.com/myorg/repo1/pull/45 --override a1b2c3d4e5f6g7h8\n```\n\nThe close command will:\n\n- Analyze the provided PR\n- Find similar PRs across the organization\n- Close all matching PRs that are in the open state\n- Skip PRs that are already closed or are drafts\n\n**Note:** Unlike the merge command, the close command does not need to check\nmergeable state or branch protection rules. It requires PRs to be in the open\nstate.\n\n### Repository Status\n\nReport statistics for tags, releases, and pull requests:\n\n```bash\n# Basic organization status check\ndependamerge status myorganization\n\n# Using full GitHub URL\ndependamerge status https://github.com/myorganization/\n\n# Check with JSON output\ndependamerge status myorganization --format json\n\n# Disable real-time progress display\ndependamerge status myorganization --no-progress\n```\n\nThe status command will:\n\n- Scan all repositories in the organization\n- Report latest tags and releases with sync status indicators\n- Count open and merged PRs (split by human/automation)\n- Identify PRs affecting action files or workflow configurations\n\nStatus icons:\n\n- ✅ Tag has matching release\n- ⚠️ Tag exists but no matching release\n- ❌ Release is more recent than tag\n\n### Finding Blocked PRs\n\nFind blocked pull requests in an entire GitHub organization:\n\n```bash\n# Basic organization check for blocked PRs\ndependamerge blocked myorganization\n\n# Using full GitHub URL\ndependamerge blocked https://github.com/myorganization/\n\n# Check with JSON output\ndependamerge blocked myorganization --format json\n\n# Disable real-time progress display\ndependamerge blocked myorganization --no-progress\n```\n\nThe blocked command will:\n\n- Analyze all repositories in the organization\n- Identify PRs with technical blocking issues\n- Report blocking reasons (merge conflicts, failing workflows, etc.)\n- Count unresolved GitHub Copilot feedback comments (displayed when present)\n- Exclude standard code review requirements from blocking reasons\n\n### Merging Pull Requests\n\nThe merge command supports both GitHub PRs and Gerrit changes. The platform\nis automatically detected from the URL format.\n\n#### GitHub Pull Requests\n\n```bash\ndependamerge merge \\\n  https://github.com/lfreleng-actions/python-project-name-action/pull/22\n```\n\n#### Gerrit Changes\n\n```bash\n# Set credentials first\nexport GERRIT_USERNAME=\"your_username\"\nexport GERRIT_PASSWORD=\"your_http_password\"\n\n# Merge similar changes\ndependamerge merge \\\n  https://gerrit.linuxfoundation.org/infra/c/releng/lftools/+/12345\n```\n\n### Optional Security Validation\n\nFor extra security, you can use the --override flag with SHA-based validation:\n\n```bash\ndependamerge merge https://github.com/owner/repo/pull/123 \\\n  --override a1b2c3d4e5f6g7h8\n```\n\nThe SHA hash derives from:\n\n- The PR author's GitHub username\n- The first line of the commit message\n- This provides an extra layer of validation for sensitive operations\n\n### Interactive Preview Mode\n\nBy default, dependamerge runs in interactive mode showing you what PRs the tool\nwill merge, then prompts you to continue:\n\n```bash\ndependamerge merge https://github.com/owner/repo/pull/123\n```\n\n**Interactive Flow:**\n\n1. Analyzes and shows similar PRs that the tool will merge\n2. Displays merge evaluation results\n3. Generates a unique SHA for security validation\n4. Prompts you to enter the SHA to proceed with actual merging\n5. Merges PRs that appear as \"mergeable\" in the preview\n\n**Example Output:**\n\n```bash\n🔍 Dependamerge Evaluation\n\n✅ Approve/merge: https://github.com/org/repo1/pull/45\n⏭️ Skipped: https://github.com/org/repo2/pull/67 [cannot update protected ref]\n✅ Approve/merge: https://github.com/org/repo3/pull/89\n\n▶️ Mergeable 2/3 PRs\n\n➡️ To proceed with merging enter: abc123def456\nEnter the string above to continue (or press Enter to cancel):\n\n🔨 Merging 2 mergeable pull requests...\n✅ Success: https://github.com/org/repo1/pull/45\n✅ Success: https://github.com/org/repo3/pull/89\n\n🚀 Final Results: 2 merged, 0 failed\n```\n\n### Custom Merge Options\n\n```bash\ndependamerge merge https://github.com/owner/repo/pull/123 \\\n  --threshold 0.9 \\\n  --merge-method squash \\\n  --no-fix \\\n  --no-progress \\\n  --token your_github_token\n```\n\n### Command Options\n\n#### Status Command Options\n\n- `--format TEXT`: Output format - table or json (default: table)\n- `--progress/--no-progress`: Show real-time progress updates (default:\n  progress)\n- `--token TEXT`: GitHub token (alternative to GITHUB_TOKEN env var)\n\n#### Blocked Command Options\n\n- `--format TEXT`: Output format - table or json (default: table)\n- `--progress/--no-progress`: Show real-time progress updates (default:\n  progress)\n- `--token TEXT`: GitHub token (alternative to GITHUB_TOKEN env var)\n\n#### Merge Command Options\n\n**General Options:**\n\n- `--no-confirm`: Skip confirmation prompt and merge without delay (default is\n  interactive mode)\n- `--threshold FLOAT`: Similarity threshold for matching PRs/changes (0.0-1.0,\n  default: 0.8)\n- `--progress/--no-progress`: Show real-time progress updates (default:\n  progress)\n- `--verbose`, `-v`: Enable verbose debug logging\n\n**GitHub-Specific Options:**\n\n- `--merge-method TEXT`: Merge method - merge, squash, or rebase (default:\n  merge)\n- `--no-fix`: Disable automatic fixing of out-of-date branches\n  (default: automatic fixing enabled)\n- `--dismiss-copilot`: Automatically resolve unresolved GitHub Copilot reviews\n  (dismissal + thread resolution)\n- `--force TEXT`: Override level for bypassing safety checks - `none` (default),\n  `code-owners`, `protection-rules`, or `all`. See [Force Override System](docs/FORCE_OVERRIDE_SYSTEM.md)\n  for detailed documentation\n- `--token TEXT`: GitHub token (alternative to GITHUB_TOKEN env var)\n- `--override TEXT`: SHA hash for extra security validation\n\n**Gerrit Environment Variables:**\n\n- `GERRIT_USERNAME`: HTTP username for Gerrit authentication\n- `GERRIT_PASSWORD`: HTTP password for Gerrit authentication\n\nFallback variables:\n\n- `GERRIT_HTTP_USER`: HTTP username (fallback)\n- `GERRIT_HTTP_PASSWORD`: HTTP password (fallback)\n\n#### Close Command Options\n\n- `--no-confirm`: Skip confirmation prompt and close without preview\n- `--threshold FLOAT`: Similarity threshold for matching PRs (0.0-1.0,\n  default: 0.8)\n- `--progress/--no-progress`: Show real-time progress updates (default:\n  progress)\n- `--debug-matching`: Show detailed scoring information for PR matching\n- `--token TEXT`: GitHub token (alternative to GITHUB_TOKEN env var)\n- `--override TEXT`: SHA hash to override non-automation PR restriction\n\n## How It Works\n\n### Pull Request Processing\n\n1. **Parse Source PR**: Analyzes the provided pull request URL and extracts\n   metadata\n2. **Organization Check**: Lists all repositories in the same GitHub\n   organization\n3. **PR Discovery**: Finds all open pull requests in each repository\n4. **Content Matching**: Compares PRs using different similarity metrics:\n   - Title similarity (normalized to remove version numbers)\n   - File change patterns\n   - Author matching\n5. **Optional Validation**: If `--override` provided, validates SHA for extra\n   security\n6. **Approval \u0026 Merge**: For matching PRs above the threshold:\n   - Adds an approval review\n   - Merges the pull request\n7. **Source PR Merge**: Merges the original source PR that served as the\n   baseline\n\n## Similarity Matching\n\nThe tool uses different algorithms to determine if PRs are similar:\n\n### Title Normalization\n\n- Removes version numbers (e.g., \"1.2.3\", \"v2.0.0\")\n- Removes commit hashes\n- Removes dates\n- Normalizes whitespace\n\n### File Change Analysis\n\n- Compares changed filenames using Jaccard similarity\n- Accounts for path normalization\n- Ignores version-specific filename differences\n\n### Confidence Scoring\n\nCombines different factors:\n\n- Title similarity score\n- File change similarity score\n- Author matching (same automation tool)\n\n## Examples\n\n### Example: Finding Blocked PRs\n\n```bash\n# Check organization for blocked PRs\ndependamerge blocked myorganization\n\n# Get detailed JSON output\ndependamerge blocked myorganization --format json \u003e unmergeable_prs.json\n\n# Check without progress display\ndependamerge blocked myorganization --no-progress\n```\n\n### Example: Automated Merging\n\n#### Dependency Update PR\n\n```bash\n# Merge a dependency update across all repos\ndependamerge merge https://github.com/myorg/repo1/pull/45\n```\n\n#### Documentation Update PR\n\n```bash\n# Merge documentation updates\ndependamerge merge https://github.com/myorg/repo1/pull/12 --threshold 0.85\n```\n\n#### Feature PR with Security Validation\n\n```bash\n# Merge with optional security validation\ndependamerge merge https://github.com/myorg/repo1/pull/89 \\\n  --override f1a2b3c4d5e6f7g8\n```\n\n### Example: Gerrit Merge\n\n#### Basic Gerrit Change Merge\n\n```bash\n# Set up Gerrit credentials\nexport GERRIT_USERNAME=\"your_username\"\nexport GERRIT_PASSWORD=\"your_http_password\"\n\n# Merge similar changes on a Gerrit server\ndependamerge merge \\\n  https://gerrit.linuxfoundation.org/infra/c/releng/lftools/+/12345\n```\n\n#### Gerrit Change with Custom Threshold\n\n```bash\n# Merge with higher similarity threshold\ndependamerge merge \\\n  https://gerrit.example.org/c/project/name/+/67890 \\\n  --threshold 0.9\n```\n\n#### Non-Interactive Gerrit Merge\n\n```bash\n# Skip confirmation prompt for automation\ndependamerge merge --no-confirm \\\n  https://gerrit.linuxfoundation.org/infra/c/releng/global-jjb/+/74080\n```\n\n### Example: GitHub Merge (continued)\n\n#### Resolving Copilot Comments\n\nThe `--dismiss-copilot` flag automatically resolves blocking Copilot reviews\nusing the most appropriate method:\n\n```bash\n# Merge with automatic Copilot review resolution (interactive mode)\ndependamerge merge https://github.com/myorg/repo1/pull/67 --dismiss-copilot\n\n# Interactive mode to see which Copilot items the tool will resolve, then\n# choose to proceed (default behavior)\ndependamerge merge https://github.com/myorg/repo1/pull/67 --dismiss-copilot\n\n# Skip confirmation and merge without delay with Copilot dismissal\ndependamerge merge https://github.com/myorg/repo1/pull/67 --dismiss-copilot --no-confirm\n```\n\n**Comprehensive Resolution Strategy**: The tool automatically uses the most\nappropriate method for each Copilot review:\n\n- ✅ **APPROVED reviews** → Dismissed via GitHub API\n- ✅ **CHANGES_REQUESTED reviews** → Dismissed via GitHub API\n- ✅ **COMMENTED reviews** → Individual review threads resolved automatically\n- ✅ **Automatic fallback** → No manual intervention required\n\nThe tool intelligently handles GitHub API limitations by automatically falling\nback to thread-level resolution for COMMENTED reviews, ensuring comprehensive\ncoverage without requiring user intervention.\n\n#### Interactive Preview with Fix Option\n\n```bash\n# See what changes will apply (default: fix out-of-date branches)\ndependamerge merge https://github.com/myorg/repo1/pull/78 \\\n  --threshold 0.9 --progress\n```\n\n#### Bypassing Branch Protection with Force Levels\n\nThe `--force` option provides tiered override levels for bypassing safety checks\nwhen you have appropriate permissions.\n\n```bash\n# Bypass code owner review requirements (you are a code owner)\ndependamerge merge https://github.com/myorg/repo1/pull/45 --force=code-owners\n\n# Bypass branch protection validation (you have admin/bypass permissions)\ndependamerge merge https://github.com/myorg/repo1/pull/67 --force=protection-rules\n\n# Emergency override - attempt merge despite most warnings (use with caution)\ndependamerge merge https://github.com/myorg/repo1/pull/89 --force=all\n```\n\n**Force Levels**:\n\n- `none` (default): Respect all protections\n- `code-owners`: Bypass code owner review requirements\n- `protection-rules`: Bypass branch protection checks (requires permissions)\n- `all`: Attempt merge despite most warnings (not recommended)\n\n**⚠️ Important**: Force levels bypass tool-level checks. GitHub API will still\nenforce actual merge restrictions based on your permissions. In some cases,\nand when branch protection rules are in place, this will result in failed merge\nattempts.\n\n## Safety Features\n\n### Force Levels\n\nDependamerge provides configurable safety levels to handle different repository\nprotection scenarios:\n\n- **`none`**: Respect all protections and requirements\n- **`code-owners`**: Bypass code owner review requirements (default)\n- **`protection-rules`**: Bypass branch protection checks (requires permissions)\n- **`all`**: Attempt merge despite most warnings (not recommended)\n\nThe default `code-owners` level allows automated merging of dependency updates\neven when repositories require code owner reviews, which is the most common\nblocking scenario for automation PRs.\n\n**Examples:**\n\n```bash\n# Use full safety (respect all protections)\ndependamerge merge https://github.com/owner/repo/pull/123 --force=none\n\n# Default behavior (bypass code owner requirements)\ndependamerge merge https://github.com/owner/repo/pull/123\n\n# Bypass branch protection rules (requires admin permissions)\ndependamerge merge https://github.com/owner/repo/pull/123 \\\n  --force=protection-rules\n\n# Force merge despite most warnings (use with extreme caution)\ndependamerge merge https://github.com/owner/repo/pull/123 --force=all\n```\n\n### For All PRs\n\n- **Mergeable Check**: Verifies PRs are in a mergeable state before attempting\n  merge\n- **Auto-Fix**: Automatically update out-of-date branches by default\n  (use `--no-fix` to disable)\n- **Detailed Status**: Shows specific reasons preventing PR merges (conflicts,\n  blocked by checks, etc.)\n- **Similarity Threshold**: Configurable confidence threshold prevents incorrect\n  matches\n- **Interactive Mode by Default**: Shows results then lets you choose to\n  proceed (use `--no-confirm` to skip)\n- **Detailed Logging**: Shows which PRs match and why they match\n\n### Security for All PRs\n\n- **SHA-Based Validation**: Provides unique SHA hash for security\n- **Author Isolation**: When using SHA validation, processes PRs from the same\n  author as source PR\n- **Commit Binding**: SHA changes if commit message changes, preventing replay\n  attacks\n- **Cross-Author Protection**: When enabled, one author's SHA cannot work for\n  another author's PRs\n\n## Enhanced URL Support\n\nThe tool now supports GitHub PR URLs with path segments:\n\n```bash\n# These URL formats now work:\ndependamerge merge https://github.com/owner/repo/pull/123\ndependamerge merge https://github.com/owner/repo/pull/123/\ndependamerge merge https://github.com/owner/repo/pull/123/files\ndependamerge merge https://github.com/owner/repo/pull/123/commits\ndependamerge merge https://github.com/owner/repo/pull/123/files/diff\n```\n\nThis enhancement allows you to copy URLs directly from GitHub's PR pages\nwithout worrying about the specific tab you're viewing.\n\n## Development\n\n### Setup Development Environment\n\n(If you already followed the Installation section, you can skip these repeated\nsteps.)\n\n```bash\ngit clone \u003crepository-url\u003e\ncd dependamerge\nuv venv .venv\nsource .venv/bin/activate\nuv sync --group dev\n```\n\nThe `dev` dependency group mirrors the legacy `.[dev]` extra.\n\n### Running Tests\n\n```bash\nuv run pytest\n```\n\nYou can pass args as usual:\n\n```bash\nuv run pytest -k \"similarity and not slow\" -vv\n```\n\n#### Pre-commit Integration\n\nTests are automatically integrated into pre-commit hooks and run on every commit.\nThis project uses [prek](https://github.com/j178/prek) (a faster, Rust-based\ndrop-in replacement for pre-commit) as the local hook runner:\n\n```bash\n# Install prek git hooks (tests will run automatically on commits)\nprek install\n\n# Run all checks including tests manually\nprek run --all-files\n\n# Run the pytest hook\nprek run pytest\n```\n\nNote: The pytest hook runs automatically on every commit to ensure code quality.\n\n### Code Quality\n\n```bash\n# Format (Black)\nuv run black src tests\n\n# Lint (Flake8 – still present)\nuv run flake8 src tests\n\n# Type checking\nuv run mypy src\n\n# (Optional) Ruff (if/when added)\n# uv run ruff check .\n```\n\n## Contributing\n\n1. Fork the repository\n2. Create a feature branch\n3. Add tests for new functionality\n4. Ensure all tests pass\n5. Submit a pull request\n\n## License\n\nApache-2.0 License - see LICENSE file for details.\n\n## Troubleshooting\n\n### Common Issues\n\n#### Authentication Error\n\n```text\nError: GitHub token needed\n```\n\nSolution: Set `GITHUB_TOKEN` environment variable or use `--token` flag.\n\n### Permission Error\n\n```text\nFailed to fetch organization repositories\n```\n\nSolution: Ensure your token has the required permissions:\n\n- **Classic tokens**: `read:org` scope\n- **Fine-grained tokens**: \"Organization members: Read access\" permission\n\n### Write Permission Error\n\n```text\n403 Forbidden during merge attempt\n```\n\nSolution: Ensure your token has write permissions:\n\n- **Classic tokens**: `repo` scope (or `public_repo` for public repositories)\n- **Fine-grained tokens**: \"Contents: Read and write\" permission\n\n### Pull Request Review Permission Error\n\n```text\nFailed to approve PR: Missing 'Pull requests: Read and write' permission\n```\n\nSolution: Ensure your token can create PR reviews:\n\n- **Classic tokens**: `repo` scope (includes PR review permissions)\n- **Fine-grained tokens**: \"Pull requests: Read and write\" permission\n\n### Actions/Checks Access Error\n\n```text\nFailed to check PR status\n```\n\nSolution: Add workflow/actions permissions:\n\n- **Classic tokens**: `workflow` scope\n- **Fine-grained tokens**: \"Workflows: Read and write\" permission\n\n#### No Similar PRs Found\n\n- Check that other repositories have open automation PRs\n- Try lowering the similarity threshold with `--threshold 0.7`\n- Use interactive mode (default) to see detailed matching information and\n  optionally proceed with merge\n\n#### Merge Failures\n\n- Ensure PRs are in mergeable state (no conflicts)\n- Check that you have write permissions to the target repositories\n- Verify the repository settings permit the merge method\n\n### Gerrit Authentication Error\n\n```text\n❌ Gerrit credentials not found in environment.\n```\n\nSolution: Set the required environment variables:\n\n```bash\nexport GERRIT_USERNAME=\"your_username\"\nexport GERRIT_PASSWORD=\"your_http_password\"\n```\n\nNote: These are your **HTTP credentials** from Gerrit, not your SSO/LDAP\npassword. Generate them in Gerrit under Settings → HTTP Credentials.\n\n### Gerrit API Error\n\n```text\n❌ Gerrit authentication failed\n```\n\nSolution:\n\n- Verify your HTTP credentials are correct\n- Check that you have permission to access the Gerrit server\n- Ensure the server URL is correct and accessible\n- Try testing with curl:\n\n```bash\ncurl -u \"$GERRIT_USERNAME:$GERRIT_PASSWORD\" \\\n  \"https://gerrit.example.org/a/accounts/self\"\n```\n\n### Gerrit URL Not Recognized\n\n```text\n❌ Invalid URL: Cannot determine platform for URL\n```\n\nSolution: Ensure your Gerrit URL follows the correct format:\n\n- `https://gerrit.example.org/c/project/name/+/12345`\n- `https://gerrit.example.org/base/c/project/name/+/12345`\n\nThe URL must contain `/c/` and `/+/` for the tool to recognize it as a Gerrit change.\n\n### Getting Help\n\n- Check the command help (local dev): `uv run dependamerge --help`\n- For PyPI usage: `uvx dependamerge --help`\n- Enable verbose output with `--verbose` or `-v`\n- Review similarity scoring in interactive mode (default behavior)\n\n## Security Considerations\n\n### Credential Protection\n\nDependamerge handles authentication tokens (GitHub PATs, Gerrit HTTP\ncredentials) and applies the following layers of protection to prevent\naccidental exposure:\n\n- **Token Redaction** — All git operations redact tokens from logs and\n  error messages. The `git_ops` module recognises GitHub classic tokens\n  (`ghp_`), fine-grained tokens (`github_pat_`), App installation tokens\n  (`ghs_`), user-to-server tokens (`ghu_`), GitLab tokens (`glpat-`),\n  basic-auth URLs, and `x-access-token` URLs\n- **Safe Object Representation** — All classes that store credentials\n  (`GitHubClient`, `GitHubAsync`, `AsyncMergeManager`, `AsyncCloseManager`,\n  `GerritRestClient`, `GerritCredentials`, `NetrcCredentials`) define\n  `__repr__` methods that mask sensitive values\n- **Log Hygiene** — Credential values (tokens, passwords, or usernames)\n  never appear in log output at any level. Debug logs record credential\n  *sources* (e.g., \"environment variables\", \".netrc\") but never the\n  credential values themselves\n- **Scoped Debug Logging** — The `--verbose` flag enables DEBUG level\n  logging for the `dependamerge.*` namespace; third-party libraries\n  (including `httpx`, which logs request headers at TRACE level) remain\n  at WARNING\n- **Exception Sanitisation** — Git command errors redact token patterns\n  from all stored attributes (`args_vec`, `stdout`, `stderr`)\n\n### URL Validation\n\nURL hostname checks use `urlparse()`-based exact matching via the\n`_host_matches()` function, not substring checks. This prevents bypass\nattacks where a crafted hostname (e.g., `evil-github.com.attacker.net`)\ncould fool a naïve substring check into trusting a malicious host.\n\n### GitHub\n\n- Store GitHub tokens securely (environment variables, not in code)\n- Use tokens with minimal required permissions for your use case\n- Rotate access tokens periodically\n- Review PR changes in interactive preview mode first\n- Be cautious with low similarity thresholds\n- Consider using repository-specific tokens instead of organisation-wide\n  access when possible\n- Audit token permissions and revoke unused tokens periodically\n\n### Gerrit\n\n- Store Gerrit HTTP credentials securely (environment variables, not in code)\n- Use HTTP credentials generated specifically for API access, not your main\n  password\n- Regenerate HTTP credentials periodically in Gerrit Settings → HTTP Credentials\n- Review changes in interactive preview mode before submitting\n- Be aware that the tool performs +2 Code-Review and submit operations using\n  your credentials\n- Ensure you have appropriate permissions on the Gerrit server before running\n  bulk operations\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flfit%2Fdependamerge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flfit%2Fdependamerge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flfit%2Fdependamerge/lists"}