Skip to content

Commit de4d516

Browse files
feat: option to allow fetch + rebase before push
1 parent d2c3704 commit de4d516

File tree

3 files changed

+95
-14
lines changed

3 files changed

+95
-14
lines changed

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,10 +301,16 @@ else if (!tagExists) {
301301

302302
// expand environment variables in remote repository
303303
remote = gitSCM.getParamExpandedRepo(environment, remote);
304+
remoteURI = remote.getURIs().get(0);
305+
306+
if (b.isRebaseBeforePush()) {
307+
listener.getLogger().println("Fetch and rebase with " + branchName + " of " + targetRepo);
308+
git.fetch_().from(remoteURI, remote.getFetchRefSpecs()).execute();
309+
git.rebase().setUpstream(targetRepo + "/" + branchName).execute();
310+
}
304311

305312
listener.getLogger().println("Pushing HEAD to branch " + branchName + " at repo "
306313
+ targetRepo);
307-
remoteURI = remote.getURIs().get(0);
308314
PushCommand push = git.push().to(remoteURI).ref("HEAD:" + branchName);
309315
if (forcePush) {
310316
push.force();
@@ -490,15 +496,20 @@ public void setEmptyTargetRepoToOrigin(){
490496

491497
public static final class BranchToPush extends PushConfig {
492498
private String branchName;
499+
private boolean rebaseBeforePush;
493500

494501
public String getBranchName() {
495502
return branchName;
496503
}
504+
public boolean isRebaseBeforePush() {
505+
return rebaseBeforePush;
506+
}
497507

498508
@DataBoundConstructor
499-
public BranchToPush(String targetRepoName, String branchName) {
509+
public BranchToPush(String targetRepoName, String branchName, boolean rebaseBeforePush) {
500510
super(targetRepoName);
501511
this.branchName = Util.fixEmptyAndTrim(branchName);
512+
this.rebaseBeforePush = rebaseBeforePush;
502513
}
503514

504515
@Extension

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: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,19 @@
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;
5052
import java.util.List;
51-
import java.util.Map;
5253
import java.util.Set;
53-
import java.util.regex.Matcher;
54-
import java.util.regex.Pattern;
5554
import java.util.concurrent.atomic.AtomicInteger;
5655
import jenkins.plugins.git.CliGitCommand;
5756
import org.eclipse.jgit.lib.PersonIdent;
@@ -141,7 +140,7 @@ public void testMergeAndPush() throws Exception {
141140

142141
project.getPublishersList().add(new GitPublisher(
143142
Collections.<TagToPush>emptyList(),
144-
Collections.singletonList(new BranchToPush("origin", "integration")),
143+
Collections.singletonList(new BranchToPush("origin", "integration", false)),
145144
Collections.<NoteToPush>emptyList(),
146145
true, true, false));
147146

@@ -178,7 +177,7 @@ public void testMergeAndPushFF() throws Exception {
178177

179178
project.getPublishersList().add(new GitPublisher(
180179
Collections.<TagToPush>emptyList(),
181-
Collections.singletonList(new BranchToPush("origin", "integration")),
180+
Collections.singletonList(new BranchToPush("origin", "integration", false)),
182181
Collections.<NoteToPush>emptyList(),
183182
true, true, false));
184183

@@ -263,7 +262,7 @@ public void testMergeAndPushNoFF() throws Exception {
263262

264263
project.getPublishersList().add(new GitPublisher(
265264
Collections.<TagToPush>emptyList(),
266-
Collections.singletonList(new BranchToPush("origin", "integration")),
265+
Collections.singletonList(new BranchToPush("origin", "integration", false)),
267266
Collections.<NoteToPush>emptyList(),
268267
true, true, false));
269268

@@ -352,7 +351,7 @@ public void testMergeAndPushFFOnly() throws Exception {
352351

353352
project.getPublishersList().add(new GitPublisher(
354353
Collections.<TagToPush>emptyList(),
355-
Collections.singletonList(new BranchToPush("origin", "integration")),
354+
Collections.singletonList(new BranchToPush("origin", "integration", false)),
356355
Collections.<NoteToPush>emptyList(),
357356
true, true, false));
358357

@@ -455,7 +454,7 @@ public void testPushEnvVarsInRemoteConfig() throws Exception{
455454

456455
project.getPublishersList().add(new GitPublisher(
457456
Collections.singletonList(new TagToPush("$TARGET_NAME", tag_name, "", false, false)),
458-
Collections.singletonList(new BranchToPush("$TARGET_NAME", "$TARGET_BRANCH")),
457+
Collections.singletonList(new BranchToPush("$TARGET_NAME", "$TARGET_BRANCH", false)),
459458
Collections.singletonList(new NoteToPush("$TARGET_NAME", note_content, Constants.R_NOTES_COMMITS, false)),
460459
true, false, true));
461460

@@ -486,7 +485,7 @@ public void testForcePush() throws Exception {
486485

487486
GitPublisher forcedPublisher = new GitPublisher(
488487
Collections.<TagToPush>emptyList(),
489-
Collections.singletonList(new BranchToPush("origin", "otherbranch")),
488+
Collections.singletonList(new BranchToPush("origin", "otherbranch", false)),
490489
Collections.<NoteToPush>emptyList(),
491490
true, true, true);
492491
project.getPublishersList().add(forcedPublisher);
@@ -533,7 +532,7 @@ public void testForcePush() throws Exception {
533532
project.getPublishersList().remove(forcedPublisher);
534533
GitPublisher unforcedPublisher = new GitPublisher(
535534
Collections.<TagToPush>emptyList(),
536-
Collections.singletonList(new BranchToPush("origin", "otherbranch")),
535+
Collections.singletonList(new BranchToPush("origin", "otherbranch", false)),
537536
Collections.<NoteToPush>emptyList(),
538537
true, true, false);
539538
project.getPublishersList().add(unforcedPublisher);
@@ -583,7 +582,7 @@ public void testMergeAndPushWithSkipTagEnabled() throws Exception {
583582

584583
project.getPublishersList().add(new GitPublisher(
585584
Collections.<TagToPush>emptyList(),
586-
Collections.singletonList(new BranchToPush("origin", "integration")),
585+
Collections.singletonList(new BranchToPush("origin", "integration", false)),
587586
Collections.<NoteToPush>emptyList(),
588587
true, true, false));
589588

@@ -602,6 +601,47 @@ public void testMergeAndPushWithSkipTagEnabled() throws Exception {
602601
assertEquals(sha1, testGitClient.revParse(Constants.HEAD).name());
603602
}
604603

604+
@Test
605+
public void testRebaseBeforePush() throws Exception {
606+
FreeStyleProject project = setupSimpleProject("master");
607+
608+
GitSCM scm = new GitSCM(
609+
remoteConfigs(),
610+
Collections.singletonList(new BranchSpec("master")),
611+
false, Collections.<SubmoduleConfig>emptyList(),
612+
null, null,
613+
Collections.<GitSCMExtension>emptyList());
614+
project.setScm(scm);
615+
616+
GitPublisher rebasedPublisher = new GitPublisher(
617+
Collections.<TagToPush>emptyList(),
618+
Collections.singletonList(new BranchToPush("origin", "master", true)),
619+
Collections.<NoteToPush>emptyList(),
620+
true, true, true);
621+
project.getPublishersList().add(rebasedPublisher);
622+
623+
project.getBuildersList().add(new LongRunningCommit(testGitDir));
624+
project.save();
625+
626+
// Assume during our build someone else pushed changes (commitFile1) to the remote repo.
627+
// So our own changes (commitFile2) cannot be pushed back to the remote origin.
628+
//
629+
// * 0eb2599 (HEAD) Added a file named commitFile2
630+
// | * 64e71e7 (origin/master) Added a file named commitFile1
631+
// |/
632+
// * b2578eb init
633+
//
634+
// What we can do is to fetch the remote changes and rebase our own changes:
635+
//
636+
// * 0e7674c (HEAD) Added a file named commitFile2
637+
// * 64e71e7 (origin/master) Added a file named commitFile1
638+
// * b2578eb init
639+
640+
641+
// as we have set "rebaseBeforePush" to true we expect all files to be present after the build.
642+
FreeStyleBuild build = build(project, Result.SUCCESS, "commitFile1", "commitFile2");
643+
}
644+
605645
@Issue("JENKINS-24786")
606646
@Test
607647
public void testMergeAndPushWithCharacteristicEnvVar() throws Exception {
@@ -658,7 +698,7 @@ private void checkEnvVar(FreeStyleProject project, String envName, String envVal
658698
String noteValue = "note for " + envValue;
659699
GitPublisher publisher = new GitPublisher(
660700
Collections.singletonList(new TagToPush("origin", tagNameReference, tagMessageReference, false, true)),
661-
Collections.singletonList(new BranchToPush("origin", envReference)),
701+
Collections.singletonList(new BranchToPush("origin", envReference, false)),
662702
Collections.singletonList(new NoteToPush("origin", noteReference, Constants.R_NOTES_COMMITS, false)),
663703
true, true, true);
664704
assertTrue(publisher.isForcePush());
@@ -739,3 +779,29 @@ private boolean isWindows() {
739779
return java.io.File.pathSeparatorChar==';';
740780
}
741781
}
782+
783+
class LongRunningCommit extends Builder {
784+
785+
private File remoteGitDir;
786+
787+
LongRunningCommit(File remoteGitDir) {
788+
this.remoteGitDir = remoteGitDir;
789+
}
790+
791+
@Override
792+
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
793+
794+
TestGitRepo workspaceGit = new TestGitRepo("workspace", new File(build.getWorkspace().getRemote()), listener);
795+
TestGitRepo remoteGit = new TestGitRepo("remote", this.remoteGitDir, listener);
796+
797+
// simulate an external commit and push to the remote during the build of our project.
798+
ObjectId headRev = remoteGit.git.revParse("HEAD");
799+
remoteGit.commit("commitFile1", remoteGit.johnDoe, "Added a file commitFile1");
800+
remoteGit.git.checkout(headRev.getName()); // allow to push to this repo later
801+
802+
// checkout initial commit and create another head with our changes.
803+
workspaceGit.commit("commitFile2", remoteGit.johnDoe, "Added a file commitFile2");
804+
805+
return true;
806+
}
807+
}

0 commit comments

Comments
 (0)