Skip to content

Commit

Permalink
Merge branch 'master' of git://github.com/mlgiroux/git-plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkEWaite committed Mar 18, 2016
2 parents 61ae4e8 + 0c4ad4f commit b904217
Show file tree
Hide file tree
Showing 3 changed files with 258 additions and 12 deletions.
77 changes: 68 additions & 9 deletions src/main/java/hudson/plugins/git/GitSCM.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import hudson.plugins.git.extensions.impl.BuildChooserSetting;
import hudson.plugins.git.extensions.impl.ChangelogToBranch;
import hudson.plugins.git.extensions.impl.PathRestriction;
import hudson.plugins.git.extensions.impl.LocalBranch;
import hudson.plugins.git.extensions.impl.PreBuildMerge;
import hudson.plugins.git.opt.PreBuildMergeOptions;
import hudson.plugins.git.util.Build;
Expand Down Expand Up @@ -117,6 +118,7 @@ public class GitSCM extends GitSCMBackwardCompatibility {
private GitRepositoryBrowser browser;
private Collection<SubmoduleConfig> submoduleCfg;
public static final String GIT_BRANCH = "GIT_BRANCH";
public static final String GIT_LOCAL_BRANCH = "GIT_LOCAL_BRANCH";
public static final String GIT_COMMIT = "GIT_COMMIT";
public static final String GIT_PREVIOUS_COMMIT = "GIT_PREVIOUS_COMMIT";
public static final String GIT_PREVIOUS_SUCCESSFUL_COMMIT = "GIT_PREVIOUS_SUCCESSFUL_COMMIT";
Expand Down Expand Up @@ -317,7 +319,7 @@ public Object readResolve() throws IOException {
public GitRepositoryBrowser getBrowser() {
return browser;
}

@Override public RepositoryBrowser<?> guessBrowser() {
if (remoteRepositories != null && remoteRepositories.size() == 1) {
List<URIish> uris = remoteRepositories.get(0).getURIs();
Expand Down Expand Up @@ -437,6 +439,41 @@ public List<RemoteConfig> getRepositories() {
return remoteRepositories;
}

/**
* Derives a local branch name from the remote branch name by removing the
* name of the remote from the remote branch name.
* <p>
* Ex. origin/master becomes master
* <p>
* Cycles through the list of user remotes looking for a match allowing user
* to configure an alternalte (not origin) name for the remote.
*
* @param remoteBranchName
* @return a local branch name derived by stripping the remote repository
* name from the {@code remoteBranchName} parameter. If a matching
* remote is not found, the original {@code remoteBranchName} will
* be returned.
*/
public String deriveLocalBranchName(String remoteBranchName) {
// default remoteName is 'origin' used if list of user remote configs is empty.
String remoteName = "origin";

for (final UserRemoteConfig remote : getUserRemoteConfigs()) {
remoteName = remote.getName();
if (remoteName == null || remoteName.isEmpty()) {
remoteName = "origin";
}
if (remoteBranchName.startsWith(remoteName + "/")) {
// found the remote config associated with remoteBranchName
break;
}
}

// now strip the remote name and return the resulting local branch name.
String localBranchName = remoteBranchName.replaceFirst("^" + remoteName + "/", "");
return localBranchName;
}

public String getGitTool() {
return gitTool;
}
Expand Down Expand Up @@ -1035,7 +1072,7 @@ public void checkout(Run<?, ?> build, Launcher launcher, FilePath workspace, Tas
throws IOException, InterruptedException {

if (VERBOSE)
listener.getLogger().println("Using strategy: " + getBuildChooser().getDisplayName());
listener.getLogger().println("Using checkout strategy: " + getBuildChooser().getDisplayName());

BuildData previousBuildData = getBuildData(build.getPreviousBuild()); // read only
BuildData buildData = copyBuildData(build.getPreviousBuild());
Expand Down Expand Up @@ -1067,13 +1104,23 @@ public void checkout(Run<?, ?> build, Launcher launcher, FilePath workspace, Tas

environment.put(GIT_COMMIT, revToBuild.revision.getSha1String());
Branch branch = Iterables.getFirst(revToBuild.revision.getBranches(),null);
String localBranchName = getParamLocalBranch(build, listener);
if (branch != null && branch.getName() != null) { // null for a detached HEAD
environment.put(GIT_BRANCH, getBranchName(branch));
String remoteBranchName = getBranchName(branch);
environment.put(GIT_BRANCH, remoteBranchName);

LocalBranch lb = getExtensions().get(LocalBranch.class);
if (lb != null) {
if (lb.getLocalBranch() == null || lb.getLocalBranch().equals("**")) {
// local branch is configured with empty value or "**" so use remote branch name for checkout
localBranchName = deriveLocalBranchName(remoteBranchName);
}
environment.put(GIT_LOCAL_BRANCH, localBranchName);
}
}

listener.getLogger().println("Checking out " + revToBuild.revision);

CheckoutCommand checkoutCommand = git.checkout().branch(getParamLocalBranch(build, listener)).ref(revToBuild.revision.getSha1String()).deleteBranchIfExist(true);
CheckoutCommand checkoutCommand = git.checkout().branch(localBranchName).ref(revToBuild.revision.getSha1String()).deleteBranchIfExist(true);
for (GitSCMExtension ext : this.getExtensions()) {
ext.decorateCheckoutCommand(this, build, git, listener, checkoutCommand);
}
Expand Down Expand Up @@ -1187,7 +1234,19 @@ public void buildEnvVars(AbstractBuild<?, ?> build, java.util.Map<String, String
if (rev!=null) {
Branch branch = Iterables.getFirst(rev.getBranches(), null);
if (branch!=null && branch.getName()!=null) {
env.put(GIT_BRANCH, getBranchName(branch));
String remoteBranchName = getBranchName(branch);
env.put(GIT_BRANCH, remoteBranchName);

LocalBranch lb = getExtensions().get(LocalBranch.class);
if (lb != null) {
// Set GIT_LOCAL_BRANCH variable from the LocalBranch extension
String localBranchName = lb.getLocalBranch();
if (localBranchName == null || localBranchName.equals("**")) {
// local branch is configured with empty value or "**" so use remote branch name for checkout
localBranchName = deriveLocalBranchName(remoteBranchName);
}
env.put(GIT_LOCAL_BRANCH, localBranchName);
}

String prevCommit = getLastBuiltCommitOfBranch(build, branch);
if (prevCommit != null) {
Expand All @@ -1206,23 +1265,23 @@ public void buildEnvVars(AbstractBuild<?, ?> build, java.util.Map<String, String
}
}


if (userRemoteConfigs.size()==1){
env.put("GIT_URL", userRemoteConfigs.get(0).getUrl());
} else {
int count=1;
for(UserRemoteConfig config:userRemoteConfigs) {
env.put("GIT_URL_"+count, config.getUrl());
count++;
}
}
}

getDescriptor().populateEnvironmentVariables(env);
for (GitSCMExtension ext : extensions) {
ext.populateEnvironmentVariables(this, env);
}
}

private String getBranchName(Branch branch)
{
String name = branch.getName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@
import org.kohsuke.stapler.DataBoundConstructor;

/**
*
*
* The Git plugin checks code out to a detached head. Configure
* LocalBranch to force checkout to a specific local branch.
* Configure this extension as null or as "**" to signify that
* the local branch name should be the same as the remote branch
* name sans the remote repository prefix (origin for example).
*
* @author Kohsuke Kawaguchi
*/
public class LocalBranch extends FakeGitSCMExtension {
Expand All @@ -30,4 +34,4 @@ public String getDisplayName() {
return "Check out to specific local branch";
}
}
}
}
183 changes: 183 additions & 0 deletions src/test/java/hudson/plugins/git/GitSCMTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1268,6 +1268,70 @@ public Void invoke(Repository repo, VirtualChannel channel) throws IOException,
}
});
}

/**
* Verifies that if project specifies LocalBranch with value of "**"
* that the checkout to a local branch using remote branch name sans 'origin'.
* This feature is necessary to support Maven release builds that push updated
* pom.xml to remote branch as
* <br/>
* <pre>
* git push origin localbranch:localbranch
* </pre>
* @throws Exception
*/
public void testCheckoutToDefaultLocalBranch_StarStar() throws Exception {
FreeStyleProject project = setupSimpleProject("master");

final String commitFile1 = "commitFile1";
commit(commitFile1, johnDoe, "Commit number 1");
GitSCM git = (GitSCM)project.getScm();
git.getExtensions().add(new LocalBranch("**"));
FreeStyleBuild build1 = build(project, Result.SUCCESS, commitFile1);

assertEquals("GIT_BRANCH", "origin/master", getEnvVars(project).get(GitSCM.GIT_BRANCH));
assertEquals("GIT_LOCAL_BRANCH", "master", getEnvVars(project).get(GitSCM.GIT_LOCAL_BRANCH));
}

/**
* Verifies that if project specifies LocalBranch with null value (empty string)
* that the checkout to a local branch using remote branch name sans 'origin'.
* This feature is necessary to support Maven release builds that push updated
* pom.xml to remote branch as
* <br/>
* <pre>
* git push origin localbranch:localbranch
* </pre>
* @throws Exception
*/
public void testCheckoutToDefaultLocalBranch_NULL() throws Exception {
FreeStyleProject project = setupSimpleProject("master");

final String commitFile1 = "commitFile1";
commit(commitFile1, johnDoe, "Commit number 1");
GitSCM git = (GitSCM)project.getScm();
git.getExtensions().add(new LocalBranch(""));
FreeStyleBuild build1 = build(project, Result.SUCCESS, commitFile1);

assertEquals("GIT_BRANCH", "origin/master", getEnvVars(project).get(GitSCM.GIT_BRANCH));
assertEquals("GIT_LOCAL_BRANCH", "master", getEnvVars(project).get(GitSCM.GIT_LOCAL_BRANCH));
}

/**
* Verifies that GIT_LOCAL_BRANCH is not set if LocalBranch extension
* is not configured.
* @throws Exception
*/
public void testCheckoutSansLocalBranchExtension() throws Exception {
FreeStyleProject project = setupSimpleProject("master");

final String commitFile1 = "commitFile1";
commit(commitFile1, johnDoe, "Commit number 1");
FreeStyleBuild build1 = build(project, Result.SUCCESS, commitFile1);

assertEquals("GIT_BRANCH", "origin/master", getEnvVars(project).get(GitSCM.GIT_BRANCH));
assertEquals("GIT_LOCAL_BRANCH", null, getEnvVars(project).get(GitSCM.GIT_LOCAL_BRANCH));
}

public void testCheckoutFailureIsRetryable() throws Exception {
FreeStyleProject project = setupSimpleProject("master");
Expand Down Expand Up @@ -1789,7 +1853,126 @@ public void testNoNullPointerExceptionWithNullBranch() throws Exception {
verify(buildData, times(1)).hasBeenReferenced(anyString());
verify(build, times(1)).getActions(BuildData.class);
}

public void testBuildEnvVarsLocalBranchStarStar() throws Exception {
ObjectId sha1 = ObjectId.fromString("2cec153f34767f7638378735dc2b907ed251a67d");

/* This is the null that causes NPE */
Branch branch = new Branch("origin/master", sha1);

List<Branch> branchList = new ArrayList<Branch>();
branchList.add(branch);

Revision revision = new Revision(sha1, branchList);

/* BuildData mock that will use the Revision with null branch name */
BuildData buildData = Mockito.mock(BuildData.class);
Mockito.when(buildData.getLastBuiltRevision()).thenReturn(revision);
Mockito.when(buildData.hasBeenReferenced(anyString())).thenReturn(true);

/* List of build data that will be returned by the mocked BuildData */
List<BuildData> buildDataList = new ArrayList<BuildData>();
buildDataList.add(buildData);

/* AbstractBuild mock which returns the buildDataList that contains a null branch name */
AbstractBuild build = Mockito.mock(AbstractBuild.class);
Mockito.when(build.getActions(BuildData.class)).thenReturn(buildDataList);

final FreeStyleProject project = setupProject("*/*", false);
GitSCM scm = (GitSCM) project.getScm();
scm.getExtensions().add(new LocalBranch("**"));

EnvVars env = new EnvVars();
scm.buildEnvVars(build, env); // NPE here before fix applied

assertEquals("GIT_BRANCH", "origin/master", env.get("GIT_BRANCH"));
assertEquals("GIT_LOCAL_BRANCH", "master", env.get("GIT_LOCAL_BRANCH"));

/* Verify mocks were called as expected */
verify(buildData, times(1)).getLastBuiltRevision();
verify(buildData, times(1)).hasBeenReferenced(anyString());
verify(build, times(1)).getActions(BuildData.class);
}

public void testBuildEnvVarsLocalBranchNull() throws Exception {
ObjectId sha1 = ObjectId.fromString("2cec153f34767f7638378735dc2b907ed251a67d");

/* This is the null that causes NPE */
Branch branch = new Branch("origin/master", sha1);

List<Branch> branchList = new ArrayList<Branch>();
branchList.add(branch);

Revision revision = new Revision(sha1, branchList);

/* BuildData mock that will use the Revision with null branch name */
BuildData buildData = Mockito.mock(BuildData.class);
Mockito.when(buildData.getLastBuiltRevision()).thenReturn(revision);
Mockito.when(buildData.hasBeenReferenced(anyString())).thenReturn(true);

/* List of build data that will be returned by the mocked BuildData */
List<BuildData> buildDataList = new ArrayList<BuildData>();
buildDataList.add(buildData);

/* AbstractBuild mock which returns the buildDataList that contains a null branch name */
AbstractBuild build = Mockito.mock(AbstractBuild.class);
Mockito.when(build.getActions(BuildData.class)).thenReturn(buildDataList);

final FreeStyleProject project = setupProject("*/*", false);
GitSCM scm = (GitSCM) project.getScm();
scm.getExtensions().add(new LocalBranch(""));

EnvVars env = new EnvVars();
scm.buildEnvVars(build, env); // NPE here before fix applied

assertEquals("GIT_BRANCH", "origin/master", env.get("GIT_BRANCH"));
assertEquals("GIT_LOCAL_BRANCH", "master", env.get("GIT_LOCAL_BRANCH"));

/* Verify mocks were called as expected */
verify(buildData, times(1)).getLastBuiltRevision();
verify(buildData, times(1)).hasBeenReferenced(anyString());
verify(build, times(1)).getActions(BuildData.class);
}

public void testBuildEnvVarsLocalBranchNotSet() throws Exception {
ObjectId sha1 = ObjectId.fromString("2cec153f34767f7638378735dc2b907ed251a67d");

/* This is the null that causes NPE */
Branch branch = new Branch("origin/master", sha1);

List<Branch> branchList = new ArrayList<Branch>();
branchList.add(branch);

Revision revision = new Revision(sha1, branchList);

/* BuildData mock that will use the Revision with null branch name */
BuildData buildData = Mockito.mock(BuildData.class);
Mockito.when(buildData.getLastBuiltRevision()).thenReturn(revision);
Mockito.when(buildData.hasBeenReferenced(anyString())).thenReturn(true);

/* List of build data that will be returned by the mocked BuildData */
List<BuildData> buildDataList = new ArrayList<BuildData>();
buildDataList.add(buildData);

/* AbstractBuild mock which returns the buildDataList that contains a null branch name */
AbstractBuild build = Mockito.mock(AbstractBuild.class);
Mockito.when(build.getActions(BuildData.class)).thenReturn(buildDataList);

final FreeStyleProject project = setupProject("*/*", false);
GitSCM scm = (GitSCM) project.getScm();

EnvVars env = new EnvVars();
scm.buildEnvVars(build, env); // NPE here before fix applied

assertEquals("GIT_BRANCH", "origin/master", env.get("GIT_BRANCH"));
assertEquals("GIT_LOCAL_BRANCH", null, env.get("GIT_LOCAL_BRANCH"));

/* Verify mocks were called as expected */
verify(buildData, times(1)).getLastBuiltRevision();
verify(buildData, times(1)).hasBeenReferenced(anyString());
verify(build, times(1)).getActions(BuildData.class);
}

/**
* Method performs HTTP get on "notifyCommit" URL, passing it commit by SHA1
* and tests for build data consistency.
Expand Down

0 comments on commit b904217

Please sign in to comment.