Skip to content

Commit cb3b4a6

Browse files
authored
Merge pull request #906 from JKalash/jkalash/master
Merge arbitrary branches
2 parents 67dc6d2 + 92c141c commit cb3b4a6

File tree

28 files changed

+1513
-3
lines changed

28 files changed

+1513
-3
lines changed

src/main/java/org/kohsuke/github/GHBranch.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import java.util.Collection;
1010
import java.util.Objects;
1111

12+
import javax.annotation.CheckForNull;
13+
1214
/**
1315
* A branch in a repository.
1416
*
@@ -165,6 +167,59 @@ public void enableProtection(EnforcementLevel level, Collection<String> contexts
165167
}
166168
}
167169

170+
/**
171+
* Merge a branch into this branch.
172+
*
173+
* @param headBranch
174+
* the branch whose head will be merged
175+
*
176+
* @param commitMessage
177+
* the commit message
178+
*
179+
* @return the merge {@link GHCommit} created, or {@code null} if the base already contains the head (nothing to
180+
* merge).
181+
*
182+
* @throws IOException
183+
* if merging fails
184+
*/
185+
@CheckForNull
186+
public GHCommit merge(GHBranch headBranch, String commitMessage) throws IOException {
187+
return merge(headBranch.getName(), commitMessage);
188+
}
189+
190+
/**
191+
* Merge a ref into this branch.
192+
*
193+
* @param head
194+
* the ref name that will be merged into this branch. Follows the usual ref naming rules, could be a
195+
* branch name, tag, or commit sha.
196+
*
197+
* @param commitMessage
198+
* the commit message
199+
*
200+
* @return the merge {@link GHCommit} created, or {@code null} if the base already contains the head (nothing to
201+
* merge).
202+
*
203+
* @throws IOException
204+
* if merging fails
205+
*/
206+
@CheckForNull
207+
public GHCommit merge(String head, String commitMessage) throws IOException {
208+
GHCommit result = root.createRequest()
209+
.withUrlPath(owner.getApiTailUrl("merges"))
210+
.method("POST")
211+
.with("commit_message", commitMessage)
212+
.with("base", this.name)
213+
.with("head", head)
214+
.fetch(GHCommit.class);
215+
216+
if (result != null) {
217+
result.wrapUp(owner);
218+
}
219+
220+
return result;
221+
}
222+
168223
String getApiRoute() {
169224
return owner.getApiTailUrl("/branches/" + name);
170225
}

src/main/java/org/kohsuke/github/GitHubResponse.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,14 @@ class GitHubResponse<T> {
7878
@CheckForNull
7979
static <T> T parseBody(ResponseInfo responseInfo, Class<T> type) throws IOException {
8080

81-
if (responseInfo.statusCode() == HttpURLConnection.HTTP_NO_CONTENT && type != null && type.isArray()) {
82-
// no content
83-
return type.cast(Array.newInstance(type.getComponentType(), 0));
81+
if (responseInfo.statusCode() == HttpURLConnection.HTTP_NO_CONTENT) {
82+
if (type != null && type.isArray()) {
83+
// no content for array should be empty array
84+
return type.cast(Array.newInstance(type.getComponentType(), 0));
85+
} else {
86+
// no content for object should be null
87+
return null;
88+
}
8489
}
8590

8691
String data = responseInfo.getBodyAsString();
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package org.kohsuke.github;
2+
3+
import org.junit.Test;
4+
5+
import java.io.IOException;
6+
7+
import static org.hamcrest.Matchers.*;
8+
9+
public class GHBranchTest extends AbstractGitHubWireMockTest {
10+
private static final String BRANCH_1 = "testBranch1";
11+
private static final String BRANCH_2 = "testBranch2";
12+
13+
private GHRepository repository;
14+
15+
@Test
16+
public void testMergeBranch() throws Exception {
17+
repository = getTempRepository();
18+
19+
String masterHead = repository.getRef("heads/master").getObject().getSha();
20+
createRefAndPostContent(BRANCH_1, masterHead);
21+
createRefAndPostContent(BRANCH_2, masterHead);
22+
23+
GHBranch otherBranch = repository.getBranch(BRANCH_2);
24+
String commitMessage = "merging " + BRANCH_2;
25+
GHCommit mergeCommit = repository.getBranch(BRANCH_1).merge(otherBranch, commitMessage);
26+
assertThat(mergeCommit, notNullValue());
27+
assertThat(mergeCommit.getCommitShortInfo().getMessage(), equalTo(commitMessage));
28+
29+
// Merging commit sha should work
30+
commitMessage = "merging from " + mergeCommit.getSHA1();
31+
GHBranch master = repository.getBranch("master");
32+
mergeCommit = master.merge(mergeCommit.getSHA1(), commitMessage);
33+
34+
assertThat(mergeCommit, notNullValue());
35+
assertThat(mergeCommit.getCommitShortInfo().getMessage(), equalTo(commitMessage));
36+
37+
mergeCommit = master.merge(mergeCommit.getSHA1(), commitMessage);
38+
// Should be null since all changes already merged
39+
assertThat(mergeCommit, nullValue());
40+
}
41+
42+
private void createRefAndPostContent(String branchName, String sha) throws IOException {
43+
String refName = "refs/heads/" + branchName;
44+
repository.createRef(refName, sha);
45+
repository.createContent().content(branchName).message(branchName).path(branchName).branch(branchName).commit();
46+
}
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
{
2+
"id": 283313593,
3+
"node_id": "MDEwOlJlcG9zaXRvcnkyODMzMTM1OTM=",
4+
"name": "temp-testMergeBranch",
5+
"full_name": "hub4j-test-org/temp-testMergeBranch",
6+
"private": false,
7+
"owner": {
8+
"login": "hub4j-test-org",
9+
"id": 7544739,
10+
"node_id": "MDEyOk9yZ2FuaXphdGlvbjc1NDQ3Mzk=",
11+
"avatar_url": "https://avatars3.githubusercontent.com/u/7544739?v=4",
12+
"gravatar_id": "",
13+
"url": "https://api.github.com/users/hub4j-test-org",
14+
"html_url": "https://github.com/hub4j-test-org",
15+
"followers_url": "https://api.github.com/users/hub4j-test-org/followers",
16+
"following_url": "https://api.github.com/users/hub4j-test-org/following{/other_user}",
17+
"gists_url": "https://api.github.com/users/hub4j-test-org/gists{/gist_id}",
18+
"starred_url": "https://api.github.com/users/hub4j-test-org/starred{/owner}{/repo}",
19+
"subscriptions_url": "https://api.github.com/users/hub4j-test-org/subscriptions",
20+
"organizations_url": "https://api.github.com/users/hub4j-test-org/orgs",
21+
"repos_url": "https://api.github.com/users/hub4j-test-org/repos",
22+
"events_url": "https://api.github.com/users/hub4j-test-org/events{/privacy}",
23+
"received_events_url": "https://api.github.com/users/hub4j-test-org/received_events",
24+
"type": "Organization",
25+
"site_admin": false
26+
},
27+
"html_url": "https://github.com/hub4j-test-org/temp-testMergeBranch",
28+
"description": "A test repository for testing the github-api project: temp-testMergeBranch",
29+
"fork": false,
30+
"url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch",
31+
"forks_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/forks",
32+
"keys_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/keys{/key_id}",
33+
"collaborators_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/collaborators{/collaborator}",
34+
"teams_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/teams",
35+
"hooks_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/hooks",
36+
"issue_events_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/issues/events{/number}",
37+
"events_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/events",
38+
"assignees_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/assignees{/user}",
39+
"branches_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/branches{/branch}",
40+
"tags_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/tags",
41+
"blobs_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/git/blobs{/sha}",
42+
"git_tags_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/git/tags{/sha}",
43+
"git_refs_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/git/refs{/sha}",
44+
"trees_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/git/trees{/sha}",
45+
"statuses_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/statuses/{sha}",
46+
"languages_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/languages",
47+
"stargazers_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/stargazers",
48+
"contributors_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/contributors",
49+
"subscribers_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/subscribers",
50+
"subscription_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/subscription",
51+
"commits_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/commits{/sha}",
52+
"git_commits_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/git/commits{/sha}",
53+
"comments_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/comments{/number}",
54+
"issue_comment_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/issues/comments{/number}",
55+
"contents_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/contents/{+path}",
56+
"compare_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/compare/{base}...{head}",
57+
"merges_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/merges",
58+
"archive_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/{archive_format}{/ref}",
59+
"downloads_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/downloads",
60+
"issues_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/issues{/number}",
61+
"pulls_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/pulls{/number}",
62+
"milestones_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/milestones{/number}",
63+
"notifications_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/notifications{?since,all,participating}",
64+
"labels_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/labels{/name}",
65+
"releases_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/releases{/id}",
66+
"deployments_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/deployments",
67+
"created_at": "2020-07-28T19:54:49Z",
68+
"updated_at": "2020-07-28T19:54:53Z",
69+
"pushed_at": "2020-07-28T19:54:51Z",
70+
"git_url": "git://github.com/hub4j-test-org/temp-testMergeBranch.git",
71+
"ssh_url": "[email protected]:hub4j-test-org/temp-testMergeBranch.git",
72+
"clone_url": "https://github.com/hub4j-test-org/temp-testMergeBranch.git",
73+
"svn_url": "https://github.com/hub4j-test-org/temp-testMergeBranch",
74+
"homepage": "http://github-api.kohsuke.org/",
75+
"size": 0,
76+
"stargazers_count": 0,
77+
"watchers_count": 0,
78+
"language": null,
79+
"has_issues": true,
80+
"has_projects": true,
81+
"has_downloads": true,
82+
"has_wiki": true,
83+
"has_pages": false,
84+
"forks_count": 0,
85+
"mirror_url": null,
86+
"archived": false,
87+
"disabled": false,
88+
"open_issues_count": 0,
89+
"license": null,
90+
"forks": 0,
91+
"open_issues": 0,
92+
"watchers": 0,
93+
"default_branch": "master",
94+
"permissions": {
95+
"admin": true,
96+
"push": true,
97+
"pull": true
98+
},
99+
"temp_clone_token": "",
100+
"allow_squash_merge": true,
101+
"allow_merge_commit": true,
102+
"allow_rebase_merge": true,
103+
"delete_branch_on_merge": false,
104+
"organization": {
105+
"login": "hub4j-test-org",
106+
"id": 7544739,
107+
"node_id": "MDEyOk9yZ2FuaXphdGlvbjc1NDQ3Mzk=",
108+
"avatar_url": "https://avatars3.githubusercontent.com/u/7544739?v=4",
109+
"gravatar_id": "",
110+
"url": "https://api.github.com/users/hub4j-test-org",
111+
"html_url": "https://github.com/hub4j-test-org",
112+
"followers_url": "https://api.github.com/users/hub4j-test-org/followers",
113+
"following_url": "https://api.github.com/users/hub4j-test-org/following{/other_user}",
114+
"gists_url": "https://api.github.com/users/hub4j-test-org/gists{/gist_id}",
115+
"starred_url": "https://api.github.com/users/hub4j-test-org/starred{/owner}{/repo}",
116+
"subscriptions_url": "https://api.github.com/users/hub4j-test-org/subscriptions",
117+
"organizations_url": "https://api.github.com/users/hub4j-test-org/orgs",
118+
"repos_url": "https://api.github.com/users/hub4j-test-org/repos",
119+
"events_url": "https://api.github.com/users/hub4j-test-org/events{/privacy}",
120+
"received_events_url": "https://api.github.com/users/hub4j-test-org/received_events",
121+
"type": "Organization",
122+
"site_admin": false
123+
},
124+
"network_count": 0,
125+
"subscribers_count": 8
126+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
{
2+
"name": "master",
3+
"commit": {
4+
"sha": "688a1c3f8bc67deb767f68560ed8c7350d70d50f",
5+
"node_id": "MDY6Q29tbWl0MjgzMzEzNTkzOjY4OGExYzNmOGJjNjdkZWI3NjdmNjg1NjBlZDhjNzM1MGQ3MGQ1MGY=",
6+
"commit": {
7+
"author": {
8+
"name": "Liam Newman",
9+
"email": "[email protected]",
10+
"date": "2020-07-28T19:54:50Z"
11+
},
12+
"committer": {
13+
"name": "GitHub",
14+
"email": "[email protected]",
15+
"date": "2020-07-28T19:54:50Z"
16+
},
17+
"message": "Initial commit",
18+
"tree": {
19+
"sha": "c6da5eb430eb876e5a03323a62ea01365a753d3b",
20+
"url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/git/trees/c6da5eb430eb876e5a03323a62ea01365a753d3b"
21+
},
22+
"url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/git/commits/688a1c3f8bc67deb767f68560ed8c7350d70d50f",
23+
"comment_count": 0,
24+
"verification": {
25+
"verified": true,
26+
"reason": "valid",
27+
"signature": "-----BEGIN PGP SIGNATURE-----\n\nwsBcBAABCAAQBQJfIIKKCRBK7hj4Ov3rIwAAdHIIAH1NiSMFO1907U4QdfRgdqCP\nQHgc6KsflfLPxeP/Tt02Q+BJQeN8e1/IMXziwOp5dt5+CSuGFLN9U41fsBOxIRk6\nagqX5q4KleZaUEjOik2PfmXkwO+mQU64CX2QdpfHuqZNvCl6H1G4dnEAWdM1E3oA\nvLMIOHdjy6Fc1VJzmcc8+RVzUvKv/9Fq0uU6WR9Jl6TfMnfGqtVwQ1USuerhnIs0\nPSI9Yo12Wy8b+8psD3bwBcjbX8r6ItM//DjzU3XcKRZxv54u7tgQuLELqxh9dQSu\nCDSV9/LyHpMC3WMRAupusGsb4rMKeN+C7NYC4ZMmoRmwvWAhRKjTtCHBP8Ksapo=\n=OLMl\n-----END PGP SIGNATURE-----\n",
28+
"payload": "tree c6da5eb430eb876e5a03323a62ea01365a753d3b\nauthor Liam Newman <[email protected]> 1595966090 -0700\ncommitter GitHub <[email protected]> 1595966090 -0700\n\nInitial commit"
29+
}
30+
},
31+
"url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/commits/688a1c3f8bc67deb767f68560ed8c7350d70d50f",
32+
"html_url": "https://github.com/hub4j-test-org/temp-testMergeBranch/commit/688a1c3f8bc67deb767f68560ed8c7350d70d50f",
33+
"comments_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/commits/688a1c3f8bc67deb767f68560ed8c7350d70d50f/comments",
34+
"author": {
35+
"login": "bitwiseman",
36+
"id": 1958953,
37+
"node_id": "MDQ6VXNlcjE5NTg5NTM=",
38+
"avatar_url": "https://avatars3.githubusercontent.com/u/1958953?v=4",
39+
"gravatar_id": "",
40+
"url": "https://api.github.com/users/bitwiseman",
41+
"html_url": "https://github.com/bitwiseman",
42+
"followers_url": "https://api.github.com/users/bitwiseman/followers",
43+
"following_url": "https://api.github.com/users/bitwiseman/following{/other_user}",
44+
"gists_url": "https://api.github.com/users/bitwiseman/gists{/gist_id}",
45+
"starred_url": "https://api.github.com/users/bitwiseman/starred{/owner}{/repo}",
46+
"subscriptions_url": "https://api.github.com/users/bitwiseman/subscriptions",
47+
"organizations_url": "https://api.github.com/users/bitwiseman/orgs",
48+
"repos_url": "https://api.github.com/users/bitwiseman/repos",
49+
"events_url": "https://api.github.com/users/bitwiseman/events{/privacy}",
50+
"received_events_url": "https://api.github.com/users/bitwiseman/received_events",
51+
"type": "User",
52+
"site_admin": false
53+
},
54+
"committer": {
55+
"login": "web-flow",
56+
"id": 19864447,
57+
"node_id": "MDQ6VXNlcjE5ODY0NDQ3",
58+
"avatar_url": "https://avatars3.githubusercontent.com/u/19864447?v=4",
59+
"gravatar_id": "",
60+
"url": "https://api.github.com/users/web-flow",
61+
"html_url": "https://github.com/web-flow",
62+
"followers_url": "https://api.github.com/users/web-flow/followers",
63+
"following_url": "https://api.github.com/users/web-flow/following{/other_user}",
64+
"gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}",
65+
"starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}",
66+
"subscriptions_url": "https://api.github.com/users/web-flow/subscriptions",
67+
"organizations_url": "https://api.github.com/users/web-flow/orgs",
68+
"repos_url": "https://api.github.com/users/web-flow/repos",
69+
"events_url": "https://api.github.com/users/web-flow/events{/privacy}",
70+
"received_events_url": "https://api.github.com/users/web-flow/received_events",
71+
"type": "User",
72+
"site_admin": false
73+
},
74+
"parents": []
75+
},
76+
"_links": {
77+
"self": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/branches/master",
78+
"html": "https://github.com/hub4j-test-org/temp-testMergeBranch/tree/master"
79+
},
80+
"protected": false,
81+
"protection": {
82+
"enabled": false,
83+
"required_status_checks": {
84+
"enforcement_level": "off",
85+
"contexts": []
86+
}
87+
},
88+
"protection_url": "https://api.github.com/repos/hub4j-test-org/temp-testMergeBranch/branches/master/protection"
89+
}

0 commit comments

Comments
 (0)