Skip to content

Commit 46a2670

Browse files
committed
[FIXED JENKINS-8365] There's no need to keep the whole output from "git whatchanged" in memory. Stream it for better efficiency.
1 parent 64e14c5 commit 46a2670

1 file changed

Lines changed: 121 additions & 140 deletions

File tree

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

Lines changed: 121 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
import hudson.scm.SCM;
4040
import hudson.util.FormValidation;
4141

42-
import java.io.ByteArrayOutputStream;
4342
import java.io.File;
4443
import java.io.FileOutputStream;
4544
import java.io.IOException;
@@ -60,6 +59,7 @@
6059

6160
import javax.servlet.ServletException;
6261

62+
import hudson.util.IOUtils;
6363
import net.sf.json.JSONObject;
6464

6565
import org.eclipse.jgit.lib.Config;
@@ -858,9 +858,10 @@ public boolean getAuthorOrCommitter() {
858858

859859
@Override
860860
public boolean checkout(final AbstractBuild build, Launcher launcher,
861-
final FilePath workspace, final BuildListener listener, File changelogFile)
861+
final FilePath workspace, final BuildListener listener, File _changelogFile)
862862
throws IOException, InterruptedException {
863-
Object[] returnData; // Changelog, BuildData
863+
864+
final FilePath changelogFile = new FilePath(_changelogFile);
864865

865866
listener.getLogger().println("Checkout:" + workspace.getName() + " / " + workspace.getRemote() + " - " + workspace.getChannel());
866867
listener.getLogger().println("Using strategy: " + buildChooser.getDisplayName());
@@ -1056,157 +1057,144 @@ public Revision invoke(File localWorkspace, VirtualChannel channel)
10561057
listener.getLogger().println("Commencing build of " + revToBuild);
10571058
environment.put(GIT_COMMIT, revToBuild.getSha1String());
10581059

1059-
if (mergeOptions.doMerge()) {
1060-
if (!revToBuild.containsBranchName(mergeOptions.getRemoteBranchName())) {
1061-
returnData = workingDirectory.act(new FileCallable<Object[]>() {
1062-
1063-
private static final long serialVersionUID = 1L;
1060+
if (mergeOptions.doMerge() && !revToBuild.containsBranchName(mergeOptions.getRemoteBranchName())) {
1061+
build.addAction(workingDirectory.act(new FileCallable<BuildData>() {
10641062

1065-
public Object[] invoke(File localWorkspace, VirtualChannel channel)
1066-
throws IOException {
1067-
IGitAPI git = new GitAPI(gitExe, new FilePath(localWorkspace), listener, environment);
1063+
private static final long serialVersionUID = 1L;
10681064

1069-
// Do we need to merge this revision onto MergeTarget
1065+
public BuildData invoke(File localWorkspace, VirtualChannel channel)
1066+
throws IOException, InterruptedException {
1067+
IGitAPI git = new GitAPI(gitExe, new FilePath(localWorkspace), listener, environment);
10701068

1071-
// Only merge if there's a branch to merge that isn't
1072-
// us..
1073-
listener.getLogger().println(
1074-
"Merging " + revToBuild + " onto "
1075-
+ mergeOptions.getMergeTarget());
1069+
// Do we need to merge this revision onto MergeTarget
10761070

1077-
// checkout origin/blah
1078-
ObjectId target = git.revParse(mergeOptions.getRemoteBranchName());
1071+
// Only merge if there's a branch to merge that isn't
1072+
// us..
1073+
listener.getLogger().println(
1074+
"Merging " + revToBuild + " onto "
1075+
+ mergeOptions.getMergeTarget());
10791076

1080-
git.checkoutBranch(paramLocalBranch, target.name());
1077+
// checkout origin/blah
1078+
ObjectId target = git.revParse(mergeOptions.getRemoteBranchName());
10811079

1082-
try {
1083-
git.merge(revToBuild.getSha1().name());
1084-
} catch (Exception ex) {
1085-
listener.getLogger().println(
1086-
"Branch not suitable for integration as it does not merge cleanly");
1087-
1088-
// We still need to tag something to prevent
1089-
// repetitive builds from happening - tag the
1090-
// candidate
1091-
// branch.
1092-
git.checkoutBranch(paramLocalBranch, revToBuild.getSha1().name());
1093-
1094-
if (!getSkipTag()) {
1095-
git.tag(buildnumber, "Jenkins Build #"
1096-
+ buildNumber);
1097-
}
1098-
1099-
buildData.saveBuild(new Build(revToBuild, buildNumber, Result.FAILURE));
1100-
return new Object[]{null, buildData};
1101-
}
1080+
git.checkoutBranch(paramLocalBranch, target.name());
11021081

1103-
if (git.hasGitModules()) {
1104-
// This ensures we don't miss changes to submodule paths and allows
1105-
// seamless use of bare and non-bare superproject repositories.
1106-
git.setupSubmoduleUrls(revToBuild, listener);
1107-
git.submoduleUpdate(recursiveSubmodules);
1108-
}
1082+
try {
1083+
git.merge(revToBuild.getSha1().name());
1084+
} catch (Exception ex) {
1085+
// We still need to tag something to prevent
1086+
// repetitive builds from happening - tag the
1087+
// candidate
1088+
// branch.
1089+
git.checkoutBranch(paramLocalBranch, revToBuild.getSha1().name());
11091090

11101091
if (!getSkipTag()) {
1111-
// Tag the successful merge
1112-
git.tag(buildnumber, "Jenkins Build #" + buildNumber);
1113-
}
1114-
1115-
String changeLog = computeChangeLog(git, revToBuild, listener, buildData);
1116-
1117-
Build build = new Build(revToBuild, buildNumber, null);
1118-
buildData.saveBuild(build);
1119-
GitUtils gu = new GitUtils(listener, git);
1120-
build.mergeRevision = gu.getRevisionForSHA1(target);
1121-
if (getClean()) {
1122-
listener.getLogger().println("Cleaning workspace");
1123-
git.clean();
1124-
if (git.hasGitModules()) {
1125-
git.submoduleClean(recursiveSubmodules);
1126-
}
1092+
git.tag(buildnumber, "Jenkins Build #"
1093+
+ buildNumber);
11271094
}
11281095

1129-
// Fetch the diffs into the changelog file
1130-
return new Object[]{changeLog, buildData};
1096+
buildData.saveBuild(new Build(revToBuild, buildNumber, Result.FAILURE));
1097+
throw new AbortException("Branch not suitable for integration as it does not merge cleanly");
11311098
}
1132-
});
1133-
BuildData returningBuildData = (BuildData) returnData[1];
1134-
build.addAction(returningBuildData);
1135-
return changeLogResult((String) returnData[0], changelogFile);
1136-
}
1137-
}
11381099

1139-
// No merge
1100+
if (git.hasGitModules()) {
1101+
// This ensures we don't miss changes to submodule paths and allows
1102+
// seamless use of bare and non-bare superproject repositories.
1103+
git.setupSubmoduleUrls(revToBuild, listener);
1104+
git.submoduleUpdate(recursiveSubmodules);
1105+
}
11401106

1141-
returnData = workingDirectory.act(new FileCallable<Object[]>() {
1107+
if (!getSkipTag()) {
1108+
// Tag the successful merge
1109+
git.tag(buildnumber, "Jenkins Build #" + buildNumber);
1110+
}
11421111

1143-
private static final long serialVersionUID = 1L;
1112+
computeChangeLog(git, revToBuild, listener, buildData, changelogFile);
11441113

1145-
public Object[] invoke(File localWorkspace, VirtualChannel channel)
1146-
throws IOException {
1147-
IGitAPI git = new GitAPI(gitExe, new FilePath(localWorkspace), listener, environment);
1148-
1149-
// Straight compile-the-branch
1150-
listener.getLogger().println("Checking out " + revToBuild);
1114+
Build build = new Build(revToBuild, buildNumber, null);
1115+
buildData.saveBuild(build);
1116+
GitUtils gu = new GitUtils(listener, git);
1117+
build.mergeRevision = gu.getRevisionForSHA1(target);
1118+
if (getClean()) {
1119+
listener.getLogger().println("Cleaning workspace");
1120+
git.clean();
1121+
if (git.hasGitModules()) {
1122+
git.submoduleClean(recursiveSubmodules);
1123+
}
1124+
}
11511125

1152-
if (getClean()) {
1153-
listener.getLogger().println("Cleaning workspace");
1154-
git.clean();
1126+
// Fetch the diffs into the changelog file
1127+
return buildData;
11551128
}
1129+
}));
1130+
} else {
1131+
// No merge
1132+
build.addAction(workingDirectory.act(new FileCallable<BuildData>() {
11561133

1157-
git.checkoutBranch(paramLocalBranch, revToBuild.getSha1().name());
1158-
1159-
if (git.hasGitModules()) {
1160-
// Git submodule update will only 'fetch' from where it
1161-
// regards as 'origin'. However,
1162-
// it is possible that we are building from a
1163-
// RemoteRepository with changes
1164-
// that are not in 'origin' AND it may be a new module that
1165-
// we've only just discovered.
1166-
// So - try updating from all RRs, then use the submodule
1167-
// Update to do the checkout
1168-
//
1169-
// Also, only do this if we're not doing recursive submodules, since that'll
1170-
// theoretically be dealt with there anyway.
1171-
if (!recursiveSubmodules) {
1172-
for (RemoteConfig remoteRepository : paramRepos) {
1173-
fetchSubmodulesFrom(git, localWorkspace, listener, remoteRepository);
1174-
}
1175-
}
1134+
private static final long serialVersionUID = 1L;
11761135

1177-
// This ensures we don't miss changes to submodule paths and allows
1178-
// seamless use of bare and non-bare superproject repositories.
1179-
git.setupSubmoduleUrls(revToBuild, listener);
1180-
git.submoduleUpdate(recursiveSubmodules);
1136+
public BuildData invoke(File localWorkspace, VirtualChannel channel)
1137+
throws IOException, InterruptedException {
1138+
IGitAPI git = new GitAPI(gitExe, new FilePath(localWorkspace), listener, environment);
11811139

1182-
}
1140+
// Straight compile-the-branch
1141+
listener.getLogger().println("Checking out " + revToBuild);
11831142

1184-
// if(compileSubmoduleCompares)
1185-
if (doGenerateSubmoduleConfigurations) {
1186-
SubmoduleCombinator combinator = new SubmoduleCombinator(
1187-
git, listener, localWorkspace, submoduleCfg);
1188-
combinator.createSubmoduleCombinations();
1189-
}
1143+
if (getClean()) {
1144+
listener.getLogger().println("Cleaning workspace");
1145+
git.clean();
1146+
}
11901147

1191-
if (!getSkipTag()) {
1192-
// Tag the successful merge
1193-
git.tag(buildnumber, "Jenkins Build #" + buildNumber);
1194-
}
1148+
git.checkoutBranch(paramLocalBranch, revToBuild.getSha1().name());
1149+
1150+
if (git.hasGitModules()) {
1151+
// Git submodule update will only 'fetch' from where it
1152+
// regards as 'origin'. However,
1153+
// it is possible that we are building from a
1154+
// RemoteRepository with changes
1155+
// that are not in 'origin' AND it may be a new module that
1156+
// we've only just discovered.
1157+
// So - try updating from all RRs, then use the submodule
1158+
// Update to do the checkout
1159+
//
1160+
// Also, only do this if we're not doing recursive submodules, since that'll
1161+
// theoretically be dealt with there anyway.
1162+
if (!recursiveSubmodules) {
1163+
for (RemoteConfig remoteRepository : paramRepos) {
1164+
fetchSubmodulesFrom(git, localWorkspace, listener, remoteRepository);
1165+
}
1166+
}
11951167

1196-
String changeLog = computeChangeLog(git, revToBuild, listener, buildData);
1168+
// This ensures we don't miss changes to submodule paths and allows
1169+
// seamless use of bare and non-bare superproject repositories.
1170+
git.setupSubmoduleUrls(revToBuild, listener);
1171+
git.submoduleUpdate(recursiveSubmodules);
11971172

1198-
buildData.saveBuild(new Build(revToBuild, buildNumber, null));
1173+
}
11991174

1200-
// Fetch the diffs into the changelog file
1201-
return new Object[]{changeLog, buildData};
1202-
}
1203-
});
1175+
// if(compileSubmoduleCompares)
1176+
if (doGenerateSubmoduleConfigurations) {
1177+
SubmoduleCombinator combinator = new SubmoduleCombinator(
1178+
git, listener, localWorkspace, submoduleCfg);
1179+
combinator.createSubmoduleCombinations();
1180+
}
12041181

1182+
if (!getSkipTag()) {
1183+
// Tag the successful merge
1184+
git.tag(buildnumber, "Jenkins Build #" + buildNumber);
1185+
}
1186+
1187+
computeChangeLog(git, revToBuild, listener, buildData,changelogFile);
12051188

1206-
build.addAction((Action) returnData[1]);
1189+
buildData.saveBuild(new Build(revToBuild, buildNumber, null));
12071190

1208-
return changeLogResult((String) returnData[0], changelogFile);
1191+
// Fetch the diffs into the changelog file
1192+
return buildData;
1193+
}
1194+
}));
1195+
}
12091196

1197+
return true;
12101198
}
12111199

12121200
/**
@@ -1222,16 +1210,16 @@ public Object[] invoke(File localWorkspace, VirtualChannel channel)
12221210
* Information that captures what we did during the last build. We need this for changelog,
12231211
* or else we won't know where to stop.
12241212
*/
1225-
private String computeChangeLog(IGitAPI git, Revision revToBuild, BuildListener listener, BuildData buildData) throws IOException {
1213+
private void computeChangeLog(IGitAPI git, Revision revToBuild, BuildListener listener, BuildData buildData, FilePath changelogFile) throws IOException, InterruptedException {
12261214
int histories = 0;
12271215

1228-
StringBuilder changeLog = new StringBuilder();
1216+
PrintStream out = new PrintStream(changelogFile.write());
12291217
try {
12301218
for (Branch b : revToBuild.getBranches()) {
12311219
Build lastRevWas = buildChooser.prevBuildForChangelog(b.getName(), buildData, git);
12321220
if (lastRevWas != null) {
12331221
if (git.isCommitInRepo(lastRevWas.getSHA1().name())) {
1234-
changeLog.append(putChangelogDiffsIntoFile(git, b.name, lastRevWas.getSHA1().name(), revToBuild.getSha1().name()));
1222+
putChangelogDiffs(git, b.name, lastRevWas.getSHA1().name(), revToBuild.getSha1().name(), out);
12351223
histories++;
12361224
} else {
12371225
listener.getLogger().println("Could not record history. Previous build's commit, " + lastRevWas.getSHA1().name()
@@ -1242,14 +1230,14 @@ private String computeChangeLog(IGitAPI git, Revision revToBuild, BuildListener
12421230
}
12431231
}
12441232
} catch (GitException ge) {
1245-
changeLog.append("Unable to retrieve changeset");
1233+
out.println("Unable to retrieve changeset");
1234+
} finally {
1235+
IOUtils.closeQuietly(out);
12461236
}
12471237

12481238
if (histories > 1) {
12491239
listener.getLogger().println("Warning : There are multiple branch changesets here");
12501240
}
1251-
1252-
return changeLog.toString();
12531241
}
12541242

12551243
public void buildEnvVars(AbstractBuild<?, ?> build, java.util.Map<String, String> env) {
@@ -1268,17 +1256,10 @@ public void buildEnvVars(AbstractBuild<?, ?> build, java.util.Map<String, String
12681256

12691257
}
12701258

1271-
private String putChangelogDiffsIntoFile(IGitAPI git, String branchName, String revFrom,
1272-
String revTo) throws IOException {
1273-
ByteArrayOutputStream fos = new ByteArrayOutputStream();
1274-
// fos.write("<data><![CDATA[".getBytes());
1275-
String changeset = "Changes in branch " + branchName + ", between " + revFrom + " and " + revTo + "\n";
1276-
fos.write(changeset.getBytes());
1277-
1259+
private void putChangelogDiffs(IGitAPI git, String branchName, String revFrom,
1260+
String revTo, PrintStream fos) throws IOException {
1261+
fos.println("Changes in branch " + branchName + ", between " + revFrom + " and " + revTo);
12781262
git.changelog(revFrom, revTo, fos);
1279-
// fos.write("]]></data>".getBytes());
1280-
fos.close();
1281-
return fos.toString("UTF-8");
12821263
}
12831264

12841265
@Override

0 commit comments

Comments
 (0)