Repository: hub4j/github-api
Pull request #1329 (2021-11-30 08:44:17)

Add gitcommit class
# Description

Directional draft PR for #1191, very messy! Is this in line with what you were thinking? In particular, I note that the `GitCommit` class is based off of [the endpoint you found that gets a git commit](https://docs.github.com/en/rest/reference/git#get-a-commit), but currently the code changes I've made don't actually contemplate calling that endpoint - just using it for structure.

I'm also a bit concerned about the amount of info that ends up duplicated between `GitCommit` and `GHCommit` (outside of `commit`), with `parents` / `getParents` being the trickiest.

Thanks for your patience!

# Before submitting a PR:

- [x] 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](CONTRIBUTING.md) for details.
- [x] Add JavaDocs and other comments as appropriate. Consider including links in comments to relevant documentation on https://docs.github.com/en/rest .  
- [x] Add tests that cover any added or changed code. This generally requires capturing snapshot test data. See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
- [x] Run `mvn -D enable-ci clean install site` locally. If this command doesn't succeed, your change will not pass CI.
- [x] 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 #<issue number>" lines for each issue. 
  - [ ] Provide links to relevant documentation on https://docs.github.com/en/rest where possible.
- [ ] 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."
- [x] Enable "Allow edits from maintainers".



Patch:
diff --git a/src/main/java/org/kohsuke/github/GHCommit.java b/src/main/java/org/kohsuke/github/GHCommit.java
index 680814baf..38b751546 100644
--- a/src/main/java/org/kohsuke/github/GHCommit.java
+++ b/src/main/java/org/kohsuke/github/GHCommit.java
@@ -1,6 +1,5 @@
 package org.kohsuke.github;
 
-import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 import java.io.IOException;
@@ -34,86 +33,44 @@ public class GHCommit {
             value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD",
                     "UWF_UNWRITTEN_FIELD" },
             justification = "JSON API")
-    public static class ShortInfo {
-        private GHAuthor author;
-        private GHAuthor committer;
+    public static class ShortInfo extends GitCommit {
 
-        private String message;
-
-        private int comment_count;
-
-        private GHVerification verification;
-
-        static class Tree {
-            String sha;
-        }
-
-        private Tree tree;
-
-        /**
-         * Gets author.
-         *
-         * @return the author
-         */
-        @WithBridgeMethods(value = GHAuthor.class, castRequired = true)
-        public GitUser getAuthor() {
-            return author;
-        }
+        private int comment_count = -1;
 
         /**
-         * Gets authored date.
+         * Gets comment count.
          *
-         * @return the authored date
+         * @return the comment count
          */
-        public Date getAuthoredDate() {
-            return author.getDate();
+        public int getCommentCount() throws GHException {
+            if (comment_count < 0) {
+                throw new GHException("Not available on this endpoint.");
+            }
+            return comment_count;
         }
 
         /**
-         * Gets committer.
-         *
-         * @return the committer
+         * Creates instance of {@link GHCommit.ShortInfo}.
          */
-        @WithBridgeMethods(value = GHAuthor.class, castRequired = true)
-        public GitUser getCommitter() {
-            return committer;
-        }
+        public ShortInfo() {
+            // Empty constructor required for Jackson binding
+        };
 
-        /**
-         * Gets commit date.
-         *
-         * @return the commit date
-         */
-        public Date getCommitDate() {
-            return committer.getDate();
+        ShortInfo(GitCommit commit) {
+            // Inherited copy constructor, used for bridge method from {@link GitCommit},
+            // which is used in {@link GHContentUpdateResponse}) to {@link GHCommit}.
+            super(commit);
         }
 
-        /**
-         * Gets message.
-         *
-         * @return Commit message.
-         */
-        public String getMessage() {
-            return message;
-        }
-
-        /**
-         * Gets comment count.
-         *
-         * @return the comment count
-         */
-        public int getCommentCount() {
-            return comment_count;
+        @Override
+        public List<String> getParentSHA1s() {
+            List<String> shortInfoParents = super.getParentSHA1s();
+            if (shortInfoParents == null) {
+                throw new GHException("Not available on this endpoint. Try calling getParentSHA1s from outer class.");
+            }
+            return shortInfoParents;
         }
 
-        /**
-         * Gets Verification Status.
-         *
-         * @return the Verification status
-         */
-        public GHVerification getVerification() {
-            return verification;
-        }
     }
 
     /**
@@ -121,7 +78,15 @@ public class GHCommit {
      *
      * @deprecated Use {@link GitUser} instead.
      */
+    @Deprecated
     public static class GHAuthor extends GitUser {
+        public GHAuthor() {
+            super();
+        }
+
+        public GHAuthor(GitUser user) {
+            super(user);
+        }
     }
 
     /**
@@ -263,6 +228,27 @@ public class GHCommit {
     List<Parent> parents;
     User author, committer;
 
+    /**
+     * Creates an instance of {@link GHCommit}.
+     */
+    public GHCommit() {
+        // empty constructor needed for Jackson binding
+    }
+
+    @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "acceptable")
+    GHCommit(ShortInfo shortInfo) {
+        // Constructs a (relatively sparse) GHCommit from a GitCommit. Used for
+        // bridge method from {@link GitCommit}, which is used in
+        // {@link GHContentUpdateResponse}) to {@link GHCommit}.
+        commit = shortInfo;
+
+        owner = commit.getOwner();
+        html_url = commit.getHtmlUrl();
+        sha = commit.getSha();
+        url = commit.getUrl();
+        parents = commit.getParents();
+    }
+
     /**
      * Gets commit short info.
      *
@@ -330,7 +316,7 @@ public class GHCommit {
      *             on error
      */
     public GHTree getTree() throws IOException {
-        return owner.getTree(getCommitShortInfo().tree.sha);
+        return owner.getTree(getCommitShortInfo().getTreeSHA1());
     }
 
     /**
@@ -379,7 +365,7 @@ public class GHCommit {
      * @return SHA1 of parent commit objects.
      */
     public List<String> getParentSHA1s() {
-        if (parents == null)
+        if (parents == null || parents.size() == 0)
             return Collections.emptyList();
         return new AbstractList<String>() {
             @Override
@@ -596,4 +582,5 @@ public class GHCommit {
         this.owner = owner;
         return this;
     }
+
 }
diff --git a/src/main/java/org/kohsuke/github/GHContentUpdateResponse.java b/src/main/java/org/kohsuke/github/GHContentUpdateResponse.java
index 0121e2395..444f4c075 100644
--- a/src/main/java/org/kohsuke/github/GHContentUpdateResponse.java
+++ b/src/main/java/org/kohsuke/github/GHContentUpdateResponse.java
@@ -1,5 +1,6 @@
 package org.kohsuke.github;
 
+import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 /**
@@ -7,7 +8,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
  */
 public class GHContentUpdateResponse {
     private GHContent content;
-    private GHCommit commit;
+    private GitCommit commit;
 
     /**
      * Gets content.
@@ -25,7 +26,14 @@ public class GHContentUpdateResponse {
      * @return the commit
      */
     @SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Expected behavior")
-    public GHCommit getCommit() {
+    @WithBridgeMethods(value = GHCommit.class, adapterMethod = "gitCommitToGHCommit")
+    public GitCommit getCommit() {
         return commit;
     }
+
+    @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", justification = "bridge method of getCommit")
+    private Object gitCommitToGHCommit(GitCommit commit, Class targetType) {
+        return new GHCommit(new GHCommit.ShortInfo(commit));
+    }
+
 }
diff --git a/src/main/java/org/kohsuke/github/GitCommit.java b/src/main/java/org/kohsuke/github/GitCommit.java
new file mode 100644
index 000000000..b0de7f0c5
--- /dev/null
+++ b/src/main/java/org/kohsuke/github/GitCommit.java
@@ -0,0 +1,220 @@
+package org.kohsuke.github;
+
+import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+import java.util.AbstractList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author Emily Xia-Reinert
+ * @see GHContentUpdateResponse#getCommit() GHContentUpdateResponse#getCommit()
+ */
+
+@SuppressFBWarnings(value = { "NP_UNWRITTEN_FIELD", "UWF_UNWRITTEN_FIELD" }, justification = "JSON API")
+public class GitCommit {
+    private GHRepository owner;
+    private String sha, node_id, url, html_url;
+    private GitUser author;
+    private GitUser committer;
+
+    private String message;
+
+    private GHVerification verification;
+
+    static class Tree {
+        String url;
+        String sha;
+
+        public String getUrl() {
+            return url;
+        }
+
+        public String getSha() {
+            return sha;
+        }
+
+    }
+
+    private Tree tree;
+
+    private List<GHCommit.Parent> parents;
+
+    public GitCommit() {
+        // empty constructor for Jackson binding
+    };
+
+    GitCommit(GitCommit commit) {
+        // copy constructor used to cast to GitCommit.ShortInfo and from there
+        // to GHCommit, for GHContentUpdateResponse bridge method to GHCommit
+        this.owner = commit.getOwner();
+        this.sha = commit.getSha();
+        this.node_id = commit.getNodeId();
+        this.url = commit.getUrl();
+        this.html_url = commit.getHtmlUrl();
+        this.author = commit.getAuthor();
+        this.committer = commit.getCommitter();
+        this.message = commit.getMessage();
+        this.verification = commit.getVerification();
+        this.tree = commit.getTree();
+        this.parents = commit.getParents();
+    }
+
+    /**
+     * Gets owner.
+     *
+     * @return the repository that contains the commit.
+     */
+    @SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Expected behavior")
+    public GHRepository getOwner() {
+        return owner;
+    }
+
+    /**
+     * Gets SHA1.
+     *
+     * @return The SHA1 of this commit
+     */
+    public String getSHA1() {
+        return sha;
+    }
+
+    /**
+     * Gets SHA.
+     *
+     * @return The SHA of this commit
+     */
+    public String getSha() {
+        return sha;
+    }
+
+    /**
+     * Gets node id.
+     *
+     * @return The node id of this commit
+     */
+    public String getNodeId() {
+        return node_id;
+    }
+
+    /**
+     * Gets URL.
+     *
+     * @return The URL of this commit
+     */
+    public String getUrl() {
+        return url;
+    }
+
+    /**
+     * Gets HTML URL.
+     *
+     * @return The HTML URL of this commit
+     */
+    public String getHtmlUrl() {
+        return html_url;
+    }
+
+    /**
+     * Gets author.
+     *
+     * @return the author
+     */
+    @WithBridgeMethods(value = GHCommit.GHAuthor.class, adapterMethod = "gitUserToGHAuthor")
+    public GitUser getAuthor() {
+        return author;
+    }
+
+    @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
+            justification = "bridge method of getAuthor & getCommitter")
+    private Object gitUserToGHAuthor(GitUser author, Class targetType) {
+        return new GHCommit.GHAuthor(author);
+    }
+
+    /**
+     * Gets authored date.
+     *
+     * @return the authored date
+     */
+    public Date getAuthoredDate() {
+        return author.getDate();
+    }
+
+    /**
+     * Gets committer.
+     *
+     * @return the committer
+     */
+    @WithBridgeMethods(value = GHCommit.GHAuthor.class, adapterMethod = "gitUserToGHAuthor")
+    public GitUser getCommitter() {
+        return committer;
+    }
+
+    /**
+     * Gets commit date.
+     *
+     * @return the commit date
+     */
+    public Date getCommitDate() {
+        return committer.getDate();
+    }
+
+    /**
+     * Gets message.
+     *
+     * @return Commit message.
+     */
+    public String getMessage() {
+        return message;
+    }
+
+    /**
+     * Gets Verification Status.
+     *
+     * @return the Verification status
+     */
+    public GHVerification getVerification() {
+        return verification;
+    }
+
+    Tree getTree() {
+        return tree;
+    }
+
+    public String getTreeSHA1() {
+        return tree.getSha();
+    }
+
+    public String getTreeUrl() {
+        return tree.getUrl();
+    }
+
+    @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "acceptable")
+    List<GHCommit.Parent> getParents() {
+        return parents;
+    }
+
+    public List<String> getParentSHA1s() {
+        if (parents == null || parents.size() == 0)
+            return Collections.emptyList();
+        return new AbstractList<String>() {
+            @Override
+            public String get(int index) {
+                return parents.get(index).sha;
+            }
+
+            @Override
+            public int size() {
+                return parents.size();
+            }
+        };
+    }
+
+    GitCommit wrapUp(GHRepository owner) {
+        this.owner = owner;
+        return this;
+    }
+
+}
diff --git a/src/main/java/org/kohsuke/github/GitUser.java b/src/main/java/org/kohsuke/github/GitUser.java
index bf0b2e6b2..3e7597590 100644
--- a/src/main/java/org/kohsuke/github/GitUser.java
+++ b/src/main/java/org/kohsuke/github/GitUser.java
@@ -55,4 +55,16 @@ public class GitUser {
     public Date getDate() {
         return GitHubClient.parseDate(date);
     }
+
+    public GitUser() {
+        // Empty constructor for Jackson binding
+    }
+
+    public GitUser(GitUser user) {
+        // Copy constructor to convert to GHCommit.GHAuthor
+        name = user.getName();
+        email = user.getEmail();
+        date = user.getDate().toString();
+        username = user.getUsername();
+    }
 }


Test patch:
diff --git a/src/test/java/org/kohsuke/github/GHContentIntegrationTest.java b/src/test/java/org/kohsuke/github/GHContentIntegrationTest.java
index 6f8416ba1..fa732db16 100644
--- a/src/test/java/org/kohsuke/github/GHContentIntegrationTest.java
+++ b/src/test/java/org/kohsuke/github/GHContentIntegrationTest.java
@@ -2,12 +2,15 @@ package org.kohsuke.github;
 
 import org.apache.commons.io.IOUtils;
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.nio.charset.StandardCharsets;
 import java.util.List;
 
@@ -94,48 +97,16 @@ public class GHContentIntegrationTest extends AbstractGitHubWireMockTest {
         GHContent createdContent = created.getContent();
 
         assertThat(mockGitHub.getRequestCount(), equalTo(expectedRequestCount));
-        assertThat(created.getCommit(), notNullValue());
-        assertThat(created.getContent(), notNullValue());
 
+        expectedRequestCount = checkCreatedCommits(created.getCommit(), getGHCommit(created), expectedRequestCount);
+
+        assertThat(created.getContent(), notNullValue());
         assertThat(createdContent.getPath(), equalTo(createdFilename));
         assertThat(mockGitHub.getRequestCount(), equalTo(expectedRequestCount));
-
         assertThat(createdContent.getContent(), notNullValue());
         assertThat(createdContent.getContent(), equalTo("this is an awesome file I created\n"));
-
-        ;
         assertThat(mockGitHub.getRequestCount(), equalTo(expectedRequestCount += 1));
 
-        assertThat(created.getCommit().getSHA1(), notNullValue());
-        assertThat(mockGitHub.getRequestCount(), equalTo(expectedRequestCount));
-        assertThat(created.getCommit().getUrl().toString(),
-                endsWith(
-                        "/repos/hub4j-test-org/GHContentIntegrationTest/git/commits/" + created.getCommit().getSHA1()));
-
-        assertThat(mockGitHub.getRequestCount(), equalTo(expectedRequestCount));
-
-        assertThat(created.getCommit().getCommitShortInfo().getMessage(),
-                equalTo("Creating a file for integration tests."));
-
-        assertThat(mockGitHub.getRequestCount(), equalTo(expectedRequestCount += 1));
-
-        assertThat(created.getCommit().getAuthor().getName(), equalTo("Liam Newman"));
-        assertThat(created.getCommit().getAuthor().getEmail(), equalTo("bitwiseman@gmail.com"));
-        assertThat(created.getCommit().getCommitter().getName(), equalTo("Liam Newman"));
-        assertThat(created.getCommit().getCommitter().getEmail(), equalTo("bitwiseman@gmail.com"));
-
-        assertThat("Resolving GHUser", mockGitHub.getRequestCount(), equalTo(expectedRequestCount += 1));
-
-        assertThat(created.getCommit().getTree().getSha(), notNullValue());
-
-        assertThat("Resolving GHTree", mockGitHub.getRequestCount(), equalTo(expectedRequestCount += 1));
-
-        assertThat(created.getCommit().getTree().getUrl().toString(),
-                endsWith("/repos/hub4j-test-org/GHContentIntegrationTest/git/trees/"
-                        + created.getCommit().getTree().getSha()));
-
-        assertThat("Resolving GHTree is not cached", mockGitHub.getRequestCount(), equalTo(expectedRequestCount += 2));
-
         GHContent content = repo.getFileContent(createdFilename);
         assertThat(content, is(notNullValue()));
         assertThat(content.getSha(), equalTo(createdContent.getSha()));
@@ -159,7 +130,6 @@ public class GHContentIntegrationTest extends AbstractGitHubWireMockTest {
 
         assertThat(mockGitHub.getRequestCount(), equalTo(expectedRequestCount));
 
-        assertThat(updatedContentResponse.getCommit(), notNullValue());
         assertThat(updatedContentResponse.getContent(), notNullValue());
 
         assertThat(mockGitHub.getRequestCount(), equalTo(expectedRequestCount));
@@ -171,40 +141,14 @@ public class GHContentIntegrationTest extends AbstractGitHubWireMockTest {
 
         assertThat(mockGitHub.getRequestCount(), equalTo(expectedRequestCount += 1));
 
-        assertThat(updatedContentResponse.getCommit().getSHA1(), notNullValue());
-        assertThat(updatedContentResponse.getCommit().getUrl().toString(),
-                endsWith("/repos/hub4j-test-org/GHContentIntegrationTest/git/commits/"
-                        + updatedContentResponse.getCommit().getSHA1()));
-
-        assertThat(mockGitHub.getRequestCount(), equalTo(expectedRequestCount));
-
-        assertThat(updatedContentResponse.getCommit().getCommitShortInfo().getMessage(),
-                equalTo("Updated file for integration tests."));
-
-        assertThat(mockGitHub.getRequestCount(), equalTo(expectedRequestCount += 1));
-
-        assertThat(updatedContentResponse.getCommit().getAuthor().getName(), equalTo("Liam Newman"));
-        assertThat(updatedContentResponse.getCommit().getAuthor().getEmail(), equalTo("bitwiseman@gmail.com"));
-        assertThat(updatedContentResponse.getCommit().getCommitter().getName(), equalTo("Liam Newman"));
-        assertThat(updatedContentResponse.getCommit().getCommitter().getEmail(), equalTo("bitwiseman@gmail.com"));
-
-        assertThat("Resolving GHUser - was already resolved",
-                mockGitHub.getRequestCount(),
-                equalTo(expectedRequestCount));
-
-        assertThat(updatedContentResponse.getCommit().getTree().getSha(), notNullValue());
-
-        assertThat("Resolving GHTree", mockGitHub.getRequestCount(), equalTo(expectedRequestCount += 1));
-
-        assertThat(updatedContentResponse.getCommit().getTree().getUrl().toString(),
-                endsWith("/repos/hub4j-test-org/GHContentIntegrationTest/git/trees/"
-                        + updatedContentResponse.getCommit().getTree().getSha()));
-
-        assertThat("Resolving GHTree is not cached", mockGitHub.getRequestCount(), equalTo(expectedRequestCount + 2));
+        expectedRequestCount = checkUpdatedContentResponseCommits(updatedContentResponse.getCommit(),
+                getGHCommit(updatedContentResponse),
+                expectedRequestCount);
 
         GHContentUpdateResponse deleteResponse = updatedContent.delete("Enough of this foolishness!");
 
         assertThat(deleteResponse.getCommit(), notNullValue());
+
         assertThat(deleteResponse.getContent(), nullValue());
 
         try {
@@ -217,6 +161,162 @@ public class GHContentIntegrationTest extends AbstractGitHubWireMockTest {
         }
     }
 
+    int checkCreatedCommits(GitCommit gitCommit, GHCommit ghCommit, int expectedRequestCount) throws Exception {
+        expectedRequestCount = checkBasicCommitInfo(gitCommit, ghCommit, expectedRequestCount);
+        assertThat(mockGitHub.getRequestCount(), equalTo(expectedRequestCount));
+
+        assertThat(gitCommit.getMessage(), equalTo("Creating a file for integration tests."));
+        assertThat(gitCommit.getAuthoredDate(), equalTo(GitHubClient.parseDate("2021-06-28T20:37:49Z")));
+        assertThat(gitCommit.getCommitDate(), equalTo(GitHubClient.parseDate("2021-06-28T20:37:49Z")));
+
+        assertThat(ghCommit.getCommitShortInfo().getMessage(), equalTo("Creating a file for integration tests."));
+        assertThat("Message already resolved", mockGitHub.getRequestCount(), equalTo(expectedRequestCount));
+        Assert.assertThrows(GHException.class, () -> ghCommit.getCommitShortInfo().getCommentCount());
+
+        ghCommit.populate();
+        assertThat("Populate GHCommit", mockGitHub.getRequestCount(), equalTo(expectedRequestCount += 1));
+
+        expectedRequestCount = checkCommitUserInfo(gitCommit, ghCommit, expectedRequestCount);
+        assertThat("Resolved GHUser for GHCommit", mockGitHub.getRequestCount(), equalTo(expectedRequestCount += 1));
+
+        expectedRequestCount = checkCommitTree(gitCommit, ghCommit, expectedRequestCount);
+
+        expectedRequestCount = checkCommitParents(gitCommit, ghCommit, expectedRequestCount);
+
+        return expectedRequestCount;
+    }
+
+    GHCommit getGHCommit(GHContentUpdateResponse resp) throws Exception {
+        for (Method method : resp.getClass().getMethods()) {
+            if (method.getName().equals("getCommit") && method.getReturnType().equals(GHCommit.class)) {
+                return (GHCommit) method.invoke(resp);
+            }
+        }
+        System.out.println("Unable to find bridge method");
+        return null;
+    }
+
+    int checkUpdatedContentResponseCommits(GitCommit gitCommit, GHCommit ghCommit, int expectedRequestCount)
+            throws Exception {
+
+        expectedRequestCount = checkBasicCommitInfo(gitCommit, ghCommit, expectedRequestCount);
+        assertThat(mockGitHub.getRequestCount(), equalTo(expectedRequestCount));
+
+        assertThat(gitCommit.getMessage(), equalTo("Updated file for integration tests."));
+        assertThat(gitCommit.getAuthoredDate(), equalTo(GitHubClient.parseDate("2021-06-28T20:37:51Z")));
+        assertThat(gitCommit.getCommitDate(), equalTo(GitHubClient.parseDate("2021-06-28T20:37:51Z")));
+
+        assertThat(ghCommit.getCommitShortInfo().getMessage(), equalTo("Updated file for integration tests."));
+        assertThat("Message already resolved", mockGitHub.getRequestCount(), equalTo(expectedRequestCount));
+
+        ghCommit.populate();
+        assertThat("Populate GHCommit", mockGitHub.getRequestCount(), equalTo(expectedRequestCount += 1));
+
+        expectedRequestCount = checkCommitUserInfo(gitCommit, ghCommit, expectedRequestCount);
+        assertThat("GHUser already resolved", mockGitHub.getRequestCount(), equalTo(expectedRequestCount));
+
+        expectedRequestCount = checkCommitTree(gitCommit, ghCommit, expectedRequestCount);
+
+        return expectedRequestCount;
+    }
+
+    int checkBasicCommitInfo(GitCommit gitCommit, GHCommit ghCommit, int expectedRequestCount) throws IOException {
+        assertThat(gitCommit, notNullValue());
+        assertThat(gitCommit.getSHA1(), notNullValue());
+        assertThat(gitCommit.getUrl().toString(),
+                endsWith("/repos/hub4j-test-org/GHContentIntegrationTest/git/commits/" + gitCommit.getSHA1()));
+        assertThat(gitCommit.getNodeId(), notNullValue());
+        assertThat(gitCommit.getHtmlUrl().toString(),
+                equalTo("https://github.com/hub4j-test-org/GHContentIntegrationTest/commit/" + gitCommit.getSHA1()));
+        assertThat(gitCommit.getVerification(), notNullValue());
+
+        assertThat(ghCommit, notNullValue());
+        assertThat(ghCommit.getSHA1(), notNullValue());
+        assertThat(ghCommit.getUrl().toString(),
+                endsWith("/repos/hub4j-test-org/GHContentIntegrationTest/git/commits/" + ghCommit.getSHA1()));
+
+        return expectedRequestCount;
+    }
+
+    int checkCommitUserInfo(GitCommit gitCommit, GHCommit ghCommit, int expectedRequestCount) throws Exception {
+        assertThat(gitCommit.getAuthor().getName(), equalTo("Liam Newman"));
+        assertThat(gitCommit.getAuthor().getEmail(), equalTo("bitwiseman@gmail.com"));
+
+        // Check that GHCommit.GHAuthor bridge method still works
+        assertThat(getGHAuthor(gitCommit).getName(), equalTo("Liam Newman"));
+        assertThat(getGHAuthor(gitCommit).getEmail(), equalTo("bitwiseman@gmail.com"));
+
+        assertThat(gitCommit.getAuthor().getName(), equalTo("Liam Newman"));
+        assertThat(gitCommit.getAuthor().getEmail(), equalTo("bitwiseman@gmail.com"));
+        assertThat(gitCommit.getCommitter().getName(), equalTo("Liam Newman"));
+        assertThat(gitCommit.getCommitter().getEmail(), equalTo("bitwiseman@gmail.com"));
+        assertThat("GHUser already resolved", mockGitHub.getRequestCount(), equalTo(expectedRequestCount));
+
+        assertThat(ghCommit.getAuthor().getName(), equalTo("Liam Newman"));
+        assertThat(ghCommit.getAuthor().getEmail(), equalTo("bitwiseman@gmail.com"));
+
+        // Check that GHCommit.GHAuthor bridge method still works
+        assertThat(getGHAuthor(ghCommit.getCommitShortInfo()).getName(), equalTo("Liam Newman"));
+        assertThat(getGHAuthor(ghCommit.getCommitShortInfo()).getEmail(), equalTo("bitwiseman@gmail.com"));
+
+        assertThat(ghCommit.getCommitter().getName(), equalTo("Liam Newman"));
+        assertThat(ghCommit.getCommitter().getEmail(), equalTo("bitwiseman@gmail.com"));
+
+        return expectedRequestCount;
+    }
+
+    GHCommit.GHAuthor getGHAuthor(GitCommit commit)
+            throws GHException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+        for (Method method : commit.getClass().getMethods()) {
+            if (method.getName().equals("getAuthor") && method.getReturnType().equals(GHCommit.GHAuthor.class)) {
+                return (GHCommit.GHAuthor) method.invoke(commit);
+            }
+        }
+        System.out.println("Unable to find bridge method");
+        return null;
+    }
+
+    GHCommit.GHAuthor getGHAuthor(GHCommit.ShortInfo commit)
+            throws GHException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+        for (Method method : commit.getClass().getMethods()) {
+            if (method.getName().equals("getAuthor") && method.getReturnType().equals(GHCommit.GHAuthor.class)) {
+                return (GHCommit.GHAuthor) method.invoke(commit);
+            }
+        }
+        System.out.println("Unable to find bridge method");
+        return null;
+    }
+
+    int checkCommitTree(GitCommit gitCommit, GHCommit ghCommit, int expectedRequestCount) throws IOException {
+        assertThat(gitCommit.getTreeSHA1(), notNullValue());
+        assertThat(gitCommit.getTreeUrl(),
+                endsWith("/repos/hub4j-test-org/GHContentIntegrationTest/git/trees/" + gitCommit.getTree().getSha()));
+        assertThat("GHTree already resolved", mockGitHub.getRequestCount(), equalTo(expectedRequestCount));
+
+        assertThat(ghCommit.getTree().getSha(), notNullValue());
+        assertThat("GHCommit has to resolve GHTree", mockGitHub.getRequestCount(), equalTo(expectedRequestCount += 1));
+        assertThat(ghCommit.getTree().getUrl().toString(),
+                endsWith("/repos/hub4j-test-org/GHContentIntegrationTest/git/trees/" + ghCommit.getTree().getSha()));
+        assertThat("GHCommit resolving GHTree is not cached",
+                mockGitHub.getRequestCount(),
+                equalTo(expectedRequestCount += 2));
+
+        return expectedRequestCount;
+    }
+
+    int checkCommitParents(GitCommit gitCommit, GHCommit ghCommit, int expectedRequestCount) throws IOException {
+        assertThat(gitCommit.getParentSHA1s().size(), is(greaterThan(0)));
+        assertThat(ghCommit.getParentSHA1s().size(), is(greaterThan(0)));
+        assertThat(gitCommit.getParentSHA1s().get(0), notNullValue());
+        assertThat(ghCommit.getParentSHA1s().get(0), notNullValue());
+        return expectedRequestCount;
+    }
+
+    // @Test
+    // public void testGitCommit2GHCommitExceptions() {
+
+    // }
+
     @Test
     public void testMIMESmall() throws IOException {
         GHRepository ghRepository = getTempRepository();
@@ -278,4 +378,5 @@ public class GHContentIntegrationTest extends AbstractGitHubWireMockTest {
         // this needs special handling and will 404 from GitHub
         // assertThat(IOUtils.toString(fileContent.read(), StandardCharsets.UTF_8), is(""));
     }
+
 }