Skip to content

Commit 2773af2

Browse files
committed
Use JGit to find merge base between branches.
This replaces the filtering tip branches logic to use a JGit RevWalk to find the merge base of two branches instead of invoking the 'git merge-base' command.
1 parent 92ff8a0 commit 2773af2

3 files changed

Lines changed: 71 additions & 28 deletions

File tree

src/main/java/hudson/plugins/git/GitAPI.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -980,7 +980,7 @@ public String getAllLogEntries(String branch) {
980980
return launchCommand("log", "--all", "--pretty=format:'%H#%ct'", branch);
981981
}
982982

983-
private Repository getRepository() throws IOException {
983+
public Repository getRepository() throws IOException {
984984
return new FileRepository(new File(workspace.getRemote(), Constants.DOT_GIT));
985985
}
986986

src/main/java/hudson/plugins/git/IGitAPI.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package hudson.plugins.git;
22

33
import hudson.EnvVars;
4+
import hudson.FilePath;
45
import hudson.model.TaskListener;
56

67
import java.io.File;
@@ -11,6 +12,7 @@
1112

1213
import org.eclipse.jgit.lib.ObjectId;
1314
import org.eclipse.jgit.lib.Ref;
15+
import org.eclipse.jgit.lib.Repository;
1416
import org.eclipse.jgit.transport.RemoteConfig;
1517

1618
/**
@@ -19,6 +21,7 @@
1921
public interface IGitAPI {
2022
String getGitExe();
2123
EnvVars getEnvironment();
24+
Repository getRepository() throws IOException;
2225

2326
public void init() throws GitException;
2427

src/main/java/hudson/plugins/git/util/GitUtils.java

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,24 @@
1515
import hudson.plugins.git.Branch;
1616
import hudson.plugins.git.GitException;
1717
import hudson.plugins.git.IGitAPI;
18-
import hudson.plugins.git.IndexEntry;
1918
import hudson.plugins.git.Revision;
2019

21-
2220
import java.io.IOException;
2321
import java.io.OutputStream;
22+
import java.text.MessageFormat;
2423
import java.util.ArrayList;
2524
import java.util.Collection;
2625
import java.util.HashMap;
2726
import java.util.HashSet;
28-
import java.util.Iterator;
2927
import java.util.List;
3028
import java.util.Map;
3129
import java.util.Set;
30+
import java.util.logging.Level;
3231
import java.util.logging.Logger;
3332

3433
import org.eclipse.jgit.lib.ObjectId;
34+
import org.eclipse.jgit.revwalk.RevWalk;
35+
import org.eclipse.jgit.revwalk.filter.RevFilter;
3536

3637
public class GitUtils {
3738
IGitAPI git;
@@ -92,39 +93,78 @@ public Revision getRevisionForSHA1(ObjectId sha1) throws GitException, IOExcepti
9293
/**
9394
* Return a list of 'tip' branches (I.E. branches that aren't included entirely within another branch).
9495
*
95-
* @param git
96-
* @return
96+
* @param revisions
97+
* @return filtered tip branches
9798
*/
9899
public Collection<Revision> filterTipBranches(Collection<Revision> revisions) {
99100
// If we have 3 branches that we might want to build
100101
// ----A--.---.--- B
101102
// \-----C
102103

103104
// we only want (B) and (C), as (A) is an ancestor (old).
104-
105-
List<Revision> l = new ArrayList<Revision>(revisions);
106-
107-
OUTER:
108-
for (int i=0; i<l.size(); i++) {
109-
for (int j=i+1; j<l.size(); j++) {
110-
Revision ri = l.get(i);
111-
Revision rj = l.get(j);
112-
ObjectId commonAncestor = git.mergeBase(ri.getSha1(), rj.getSha1());
113-
if (commonAncestor==null) continue;
114-
115-
if (commonAncestor.equals(ri.getSha1())) {
116-
LOGGER.fine("filterTipBranches: "+rj+" subsumes "+ri);
117-
l.remove(i);
118-
i--;
119-
continue OUTER;
120-
}
121-
if (commonAncestor.equals(rj.getSha1())) {
122-
LOGGER.fine("filterTipBranches: "+ri+" subsumes "+rj);
123-
l.remove(j);
124-
j--;
105+
final List<Revision> l = new ArrayList<Revision>(revisions);
106+
107+
// Bypass any rev walks if only one branch or less
108+
if (l.size() <= 1)
109+
return l;
110+
111+
final boolean log = LOGGER.isLoggable(Level.FINE);
112+
Revision revI;
113+
Revision revJ;
114+
ObjectId shaI;
115+
ObjectId shaJ;
116+
ObjectId commonAncestor;
117+
RevWalk walk = null;
118+
final long start = System.currentTimeMillis();
119+
long calls = 0;
120+
if (log)
121+
LOGGER.fine(MessageFormat.format(
122+
"Computing merge base of {0} branches", l.size()));
123+
try {
124+
walk = new RevWalk(git.getRepository());
125+
walk.setRetainBody(false);
126+
walk.setRevFilter(RevFilter.MERGE_BASE);
127+
for (int i = 0; i < l.size(); i++)
128+
for (int j = i + 1; j < l.size(); j++) {
129+
revI = l.get(i);
130+
revJ = l.get(j);
131+
shaI = revI.getSha1();
132+
shaJ = revJ.getSha1();
133+
134+
walk.reset();
135+
walk.markStart(walk.parseCommit(shaI));
136+
walk.markStart(walk.parseCommit(shaJ));
137+
commonAncestor = walk.next();
138+
calls++;
139+
140+
if (commonAncestor == null)
141+
continue;
142+
if (commonAncestor.equals(shaI)) {
143+
if (log)
144+
LOGGER.fine("filterTipBranches: " + revJ
145+
+ " subsumes " + revI);
146+
l.remove(i);
147+
i--;
148+
break;
149+
}
150+
if (commonAncestor.equals(shaJ)) {
151+
if (log)
152+
LOGGER.fine("filterTipBranches: " + revI
153+
+ " subsumes " + revJ);
154+
l.remove(j);
155+
j--;
156+
}
125157
}
126-
}
158+
} catch (IOException e) {
159+
throw new GitException("Error computing merge base", e);
160+
} finally {
161+
if (walk != null)
162+
walk.release();
127163
}
164+
if (log)
165+
LOGGER.fine(MessageFormat.format(
166+
"Computed {0} merge bases in {1} ms", calls,
167+
(System.currentTimeMillis() - start)));
128168

129169
return l;
130170
}

0 commit comments

Comments
 (0)