Skip to content

Commit 768eeac

Browse files
authored
Merge pull request #694 from johannespfeiffer/master
[JENKINS-60564] Rebase before push
2 parents 623b281 + 9c4cf41 commit 768eeac

File tree

3 files changed

+98
-5
lines changed

3 files changed

+98
-5
lines changed

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

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@
2525
import org.eclipse.jgit.transport.URIish;
2626
import org.jenkinsci.plugins.gitclient.GitClient;
2727
import org.jenkinsci.plugins.gitclient.PushCommand;
28-
import org.kohsuke.stapler.AncestorInPath;
29-
import org.kohsuke.stapler.DataBoundConstructor;
30-
import org.kohsuke.stapler.QueryParameter;
31-
import org.kohsuke.stapler.StaplerRequest;
28+
import org.kohsuke.stapler.*;
3229

3330
import javax.servlet.ServletException;
3431
import java.io.IOException;
@@ -295,10 +292,20 @@ else if (!tagExists) {
295292

296293
// expand environment variables in remote repository
297294
remote = gitSCM.getParamExpandedRepo(environment, remote);
295+
remoteURI = remote.getURIs().get(0);
296+
297+
if (b.getRebaseBeforePush()) {
298+
listener.getLogger().println("Fetch and rebase with " + branchName + " of " + targetRepo);
299+
git.fetch_().from(remoteURI, remote.getFetchRefSpecs()).execute();
300+
if (!git.revParse("HEAD").equals(git.revParse(targetRepo + "/" + branchName))) {
301+
git.rebase().setUpstream(targetRepo + "/" + branchName).execute();
302+
} else {
303+
listener.getLogger().println("No rebase required. HEAD equals " + targetRepo + "/" + branchName);
304+
}
305+
}
298306

299307
listener.getLogger().println("Pushing HEAD to branch " + branchName + " at repo "
300308
+ targetRepo);
301-
remoteURI = remote.getURIs().get(0);
302309
PushCommand push = git.push().to(remoteURI).ref("HEAD:" + branchName).force(forcePush);
303310
push.execute();
304311
} catch (GitException e) {
@@ -478,6 +485,7 @@ public void setEmptyTargetRepoToOrigin(){
478485

479486
public static final class BranchToPush extends PushConfig {
480487
private String branchName;
488+
private boolean rebaseBeforePush;
481489

482490
public String getBranchName() {
483491
return branchName;
@@ -489,6 +497,15 @@ public BranchToPush(String targetRepoName, String branchName) {
489497
this.branchName = Util.fixEmptyAndTrim(branchName);
490498
}
491499

500+
@DataBoundSetter
501+
public void setRebaseBeforePush(boolean shouldRebase) {
502+
this.rebaseBeforePush = shouldRebase;
503+
}
504+
505+
public boolean getRebaseBeforePush() {
506+
return rebaseBeforePush;
507+
}
508+
492509
@Extension
493510
public static class DescriptorImpl extends Descriptor<PushConfig> {
494511
@Override

src/main/resources/hudson/plugins/git/GitPublisher/config.jelly

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@
6464
<f:textbox
6565
checkUrl="'descriptorByName/GitPublisher/checkRemote?value='+escape(this.value)" />
6666
</f:entry>
67+
<f:entry field="rebaseBeforePush"
68+
title="${%Rebase before push}">
69+
<f:checkbox />
70+
</f:entry>
6771
</table>
6872
<div align="right">
6973
<input type="button" value="${%Delete Branch}" class="repeatable-delete" style="margin-left: 1em;" />

src/test/java/hudson/plugins/git/GitPublisherTest.java

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@
3838
import hudson.plugins.git.extensions.impl.PreBuildMerge;
3939
import hudson.scm.NullSCM;
4040
import hudson.tasks.BuildStepDescriptor;
41+
import hudson.tasks.Builder;
4142
import hudson.util.StreamTaskListener;
4243
import org.eclipse.jgit.lib.Constants;
4344
import org.eclipse.jgit.lib.ObjectId;
4445
import org.jenkinsci.plugins.gitclient.MergeCommand;
4546
import org.jvnet.hudson.test.Issue;
4647

48+
import java.io.File;
4749
import java.io.IOException;
4850
import java.util.ArrayList;
4951
import java.util.Collections;
@@ -601,6 +603,50 @@ public void testMergeAndPushWithSkipTagEnabled() throws Exception {
601603
assertEquals(sha1, testGitClient.revParse(Constants.HEAD).name());
602604
}
603605

606+
@Test
607+
public void testRebaseBeforePush() throws Exception {
608+
FreeStyleProject project = setupSimpleProject("master");
609+
610+
GitSCM scm = new GitSCM(
611+
remoteConfigs(),
612+
Collections.singletonList(new BranchSpec("master")),
613+
false, Collections.<SubmoduleConfig>emptyList(),
614+
null, null,
615+
Collections.<GitSCMExtension>emptyList());
616+
project.setScm(scm);
617+
618+
BranchToPush btp = new BranchToPush("origin", "master");
619+
btp.setRebaseBeforePush(true);
620+
621+
GitPublisher rebasedPublisher = new GitPublisher(
622+
Collections.<TagToPush>emptyList(),
623+
Collections.singletonList(btp),
624+
Collections.<NoteToPush>emptyList(),
625+
true, true, true);
626+
project.getPublishersList().add(rebasedPublisher);
627+
628+
project.getBuildersList().add(new LongRunningCommit(testGitDir));
629+
project.save();
630+
631+
// Assume during our build someone else pushed changes (commitFile1) to the remote repo.
632+
// So our own changes (commitFile2) cannot be pushed back to the remote origin.
633+
//
634+
// * 0eb2599 (HEAD) Added a file named commitFile2
635+
// | * 64e71e7 (origin/master) Added a file named commitFile1
636+
// |/
637+
// * b2578eb init
638+
//
639+
// What we can do is to fetch the remote changes and rebase our own changes:
640+
//
641+
// * 0e7674c (HEAD) Added a file named commitFile2
642+
// * 64e71e7 (origin/master) Added a file named commitFile1
643+
// * b2578eb init
644+
645+
646+
// as we have set "rebaseBeforePush" to true we expect all files to be present after the build.
647+
FreeStyleBuild build = build(project, Result.SUCCESS, "commitFile1", "commitFile2");
648+
}
649+
604650
@Issue("JENKINS-24786")
605651
@Test
606652
public void testMergeAndPushWithCharacteristicEnvVar() throws Exception {
@@ -738,3 +784,29 @@ private boolean isWindows() {
738784
return java.io.File.pathSeparatorChar==';';
739785
}
740786
}
787+
788+
class LongRunningCommit extends Builder {
789+
790+
private File remoteGitDir;
791+
792+
LongRunningCommit(File remoteGitDir) {
793+
this.remoteGitDir = remoteGitDir;
794+
}
795+
796+
@Override
797+
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
798+
799+
TestGitRepo workspaceGit = new TestGitRepo("workspace", new File(build.getWorkspace().getRemote()), listener);
800+
TestGitRepo remoteGit = new TestGitRepo("remote", this.remoteGitDir, listener);
801+
802+
// simulate an external commit and push to the remote during the build of our project.
803+
ObjectId headRev = remoteGit.git.revParse("HEAD");
804+
remoteGit.commit("commitFile1", remoteGit.johnDoe, "Added a file commitFile1");
805+
remoteGit.git.checkout(headRev.getName()); // allow to push to this repo later
806+
807+
// commit onto the initial commit (creates a head with our changes later).
808+
workspaceGit.commit("commitFile2", remoteGit.johnDoe, "Added a file commitFile2");
809+
810+
return true;
811+
}
812+
}

0 commit comments

Comments
 (0)