Skip to content

fix: resolve GHCommitPointer commit when the head repository is deleted - #2340

Open
mvanhorn wants to merge 3 commits into
hub4j:mainfrom
mvanhorn:fix/2035-commit-pointer-null-repository
Open

mvanhorn wants to merge 3 commits into
hub4j:mainfrom
mvanhorn:fix/2035-commit-pointer-null-repository

Conversation

@mvanhorn

@mvanhorn mvanhorn commented Oct 22, 2026 •

Copy link
Copy Markdown

Description

When a pull request's head fork has been deleted, GitHub returns head.repo as null, and GHCommitPointer.getCommit() threw a NullPointerException because it called getRepository().getCommit(getSha()).

The head commit is still reachable in the base repository, since GitHub keeps pull request commits there. GHPullRequest.getHead() and getBase() now link each pointer back to its pull request, and getCommit() looks the SHA up in the pull request's repository when the pointer has none. getRepository() still returns null in that case, and its javadoc says so. An IOException is left only for a pointer with neither a repository nor a pull request.

GHCommitPointerTest.getCommitFromDeletedHeadRepository replays a snapshot of checkstyle/checkstyle#1, the example from the issue, and checks that the head repository is null and that getCommit() returns 4889a777. GHCommitPointerTest, GHPullRequestTest, GHEventPayloadTest and ArchTests pass, and spotless:check and clean install -DskipTests pass.

Fixes #2035

Before submitting a PR:

  • Changes must not break binary backwards compatibility. If you are unclear on how to make the change you think is needed while maintaining backward compatibility, CONTRIBUTING.md for details.
  • Add JavaDocs and other comments explaining the behavior.
  • When adding or updating methods that fetch entities, add @link JavaDoc entries to the relevant documentation on https://docs.github.com/en/rest .
  • Add tests that cover any added or changed code. This generally requires capturing snapshot test data. See CONTRIBUTING.md for details.
  • Run mvn -D enable-ci clean install site "-Dsurefire.argLine=--add-opens java.base/java.net=ALL-UNNAMED" locally. If this command doesn't succeed, your change will not pass CI.
  • Push your changes to a branch other than main. You will create your PR from that branch.

When creating a PR:

  • Fill in the "Description" above with clear summary of the changes. This includes:
    • If this PR fixes one or more issues, include "Fixes #" lines for each issue.
    • Provide links to relevant documentation on https://docs.github.com/en/rest where possible. If not including links, explain why not.
  • All lines of new code should be covered by tests as reported by code coverage. Any lines that are not covered must have PR comments explaining why they cannot be covered. For example, "Reaching this particular exception is hard and is not a particular common scenario."
  • Enable "Allow edits from maintainers".

getCommit() now takes the repository and throws IOException with the
message "Repository information is unavailable for this commit pointer"
when it is null. Javadoc on getCommit() and getRepository() states that
the repository can be null and that getCommit() throws IOException in
that case. A WireMock test stubs a pull request whose head repo is JSON
null, then asserts getRepository() is null and that getCommit() throws
that IOException.

Fixes hub4j#2035
@rnveach

rnveach commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

This looks like an exact duplicate of #2265 and I commented at #2265 (comment) that this wasn't a fix. It just replaces NPE with IOE.

When the head repository of a pull request has been deleted, GitHub
reports head.repo as null, but the head commit is still reachable in the
base repository. GHPullRequest now binds its head and base pointers to
itself, and getCommit() falls back to the pull request's repository when
the pointer has none. Only a pointer with neither still throws.

The test replays checkstyle/checkstyle#1, the case from the issue.

Fixes hub4j#2035
@mvanhorn mvanhorn changed the title fix: handle a missing GHCommitPointer repository fix: resolve GHCommitPointer commit when the head repository is deleted Oct 23, 2026
@mvanhorn

Copy link
Copy Markdown
Author

@rnveach You're right. The first version only swapped the NPE for an IOException, which is the same thing #2265 did. I've reworked it in 90e9ae6 so getCommit() actually returns the commit.

When a fork is deleted, GitHub sends head.repo as null. The head commit is still in the base repository, though, because GitHub keeps PR commits there (refs/pull/N/head). For checkstyle/checkstyle#1, GET /repos/checkstyle/checkstyle/commits/4889a777... returns the head commit.

So now GHPullRequest.getHead() and getBase() link each pointer back to its pull request. When the pointer has no repository, GHCommitPointer.getCommit() looks the SHA up in the pull request's repository. getRepository() still returns null in that case, because the fork really is gone. The javadoc now says so. The IOException is only left for a pointer that has no repository and no pull request either, such as one built by hand.

The new test is a WireMock snapshot of checkstyle/checkstyle#1, your example from the issue. It checks that getHead().getRepository() is null and that getHead().getCommit() returns 4889a777 from checkstyle/checkstyle.

@codecov

codecov Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.45%. Comparing base (f351e37) to head (9ce33d1).

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #2340      +/-   ##
============================================
+ Coverage     85.41%   85.45%   +0.03%     
- Complexity     2568     2576       +8     
============================================
  Files           242      242              
  Lines          7577     7584       +7     
  Branches        399      401       +2     
============================================
+ Hits           6472     6481       +9     
+ Misses          869      867       -2     
  Partials        236      236              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Codecov reported three partial branches on this PR: the null checks in
GHPullRequest.getHead()/getBase() and the repository check in
GHCommitPointer.getCommit(). Add a base-pointer getCommit() case that
resolves through the base repository, and a test for a pull request
whose head and base are unset. Production code is unchanged.
@mvanhorn

Copy link
Copy Markdown
Author

Added tests in 9ce33d1 for the three partial branches Codecov flagged: a base-pointer getCommit() case that resolves through the base repository, and a pull request whose head and base are unset. Production code is unchanged. mvn -Dtest=GHCommitPointerTest test and mvn spotless:check pass locally.

This branch has not been deployed

No deployments
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.

GHCommitPointer throws NPE on null repository

2 participants