Browse Source

Fail clone if initial branch doesn't exist in remote repository

jgit clone --branch foo <url>

did not fail if the remote branch "foo" didn't exist in the remote
repository being cloned.

Bug: 546580
Change-Id: I55648ad3a39da4a5711dfa8e6d6682bb8190a6d6
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
tags/v5.11.0.202102240950-m3
Matthias Sohn 3 years ago
parent
commit
64cb7148ac

+ 42
- 0
org.eclipse.jgit.pgm.test/tst/org/eclipse/jgit/pgm/CloneTest.java View File

@@ -11,7 +11,9 @@ package org.eclipse.jgit.pgm;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;

import java.io.File;
@@ -25,6 +27,7 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.RefSpec;
@@ -64,6 +67,45 @@ public class CloneTest extends CLIRepositoryTestCase {
assertEquals("expected 1 branch", 1, branches.size());
}

@Test
public void testCloneInitialBranch() throws Exception {
createInitialCommit();

File gitDir = db.getDirectory();
String sourceURI = gitDir.toURI().toString();
File target = createTempDirectory("target");
String cmd = "git clone --branch master " + sourceURI + " "
+ shellQuote(target.getPath());
String[] result = execute(cmd);
assertArrayEquals(new String[] {
"Cloning into '" + target.getPath() + "'...", "", "" }, result);

Git git2 = Git.open(target);
List<Ref> branches = git2.branchList().call();
assertEquals("expected 1 branch", 1, branches.size());

Repository db2 = git2.getRepository();
ObjectId head = db2.resolve("HEAD");
assertNotNull(head);
assertNotEquals(ObjectId.zeroId(), head);
ObjectId master = db2.resolve("master");
assertEquals(head, master);
}

@Test
public void testCloneInitialBranchMissing() throws Exception {
createInitialCommit();

File gitDir = db.getDirectory();
String sourceURI = gitDir.toURI().toString();
File target = createTempDirectory("target");
String cmd = "git clone --branch foo " + sourceURI + " "
+ shellQuote(target.getPath());
Die e = assertThrows(Die.class, () -> execute(cmd));
assertEquals("Remote branch 'foo' not found in upstream origin",
e.getMessage());
}

private RevCommit createInitialCommit() throws Exception {
JGitTestUtil.writeTrashFile(db, "hello.txt", "world");
git.add().addFilepattern("hello.txt").call();

+ 3
- 0
org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Clone.java View File

@@ -18,6 +18,7 @@ import java.util.Collection;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
import org.eclipse.jgit.api.errors.TransportException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.TextProgressMonitor;
@@ -110,6 +111,8 @@ class Clone extends AbstractFetchCommand implements CloneCommand.Callback {
db = command.call().getRepository();
if (msgs && db.resolve(Constants.HEAD) == null)
outw.println(CLIText.get().clonedEmptyRepository);
} catch (TransportException e) {
throw die(e.getMessage(), e);
} catch (InvalidRemoteException e) {
throw die(MessageFormat.format(CLIText.get().doesNotExist,
sourceUri), e);

+ 1
- 0
org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties View File

@@ -568,6 +568,7 @@ refNotResolved=Ref {0} cannot be resolved
reftableDirExists=reftable dir exists and is nonempty
reftableRecordsMustIncrease=records must be increasing: last {0}, this {1}
refUpdateReturnCodeWas=RefUpdate return code was: {0}
remoteBranchNotFound=Remote branch ''{0}'' not found in upstream origin
remoteConfigHasNoURIAssociated=Remote config "{0}" has no URIs associated
remoteDoesNotHaveSpec=Remote does not have {0} available for fetch.
remoteDoesNotSupportSmartHTTPPush=remote does not support smart HTTP push

+ 1
- 0
org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java View File

@@ -297,6 +297,7 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
command.setTagOpt(
fetchAll ? TagOpt.FETCH_TAGS : TagOpt.AUTO_FOLLOW);
}
command.setInitialBranch(branch);
configure(command);

return command.call();

+ 21
- 1
org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java View File

@@ -74,6 +74,8 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {

private boolean isForceUpdate;

private String initialBranch;

/**
* Callback for status of fetch operation.
*
@@ -209,7 +211,7 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
transport.setFetchThin(thin);
configure(transport);
FetchResult result = transport.fetch(monitor,
applyOptions(refSpecs));
applyOptions(refSpecs), initialBranch);
if (!repo.isBare()) {
fetchSubmodules(result);
}
@@ -487,6 +489,24 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
return this;
}

/**
* Set the initial branch
*
* @param branch
* the initial branch to check out when cloning the repository.
* Can be specified as ref name (<code>refs/heads/master</code>),
* branch name (<code>master</code>) or tag name
* (<code>v1.2.3</code>). The default is to use the branch
* pointed to by the cloned repository's HEAD and can be
* requested by passing {@code null} or <code>HEAD</code>.
* @return {@code this}
* @since 5.11
*/
public FetchCommand setInitialBranch(String branch) {
this.initialBranch = branch;
return this;
}

/**
* Register a progress callback.
*

+ 1
- 0
org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java View File

@@ -596,6 +596,7 @@ public class JGitText extends TranslationBundle {
/***/ public String reftableDirExists;
/***/ public String reftableRecordsMustIncrease;
/***/ public String refUpdateReturnCodeWas;
/***/ public String remoteBranchNotFound;
/***/ public String remoteConfigHasNoURIAssociated;
/***/ public String remoteDoesNotHaveSpec;
/***/ public String remoteDoesNotSupportSmartHTTPPush;

+ 25
- 5
org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchProcess.java View File

@@ -48,6 +48,7 @@ import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.util.StringUtils;

class FetchProcess {
/** Transport we will fetch over. */
@@ -79,7 +80,8 @@ class FetchProcess {
toFetch = f;
}

void execute(ProgressMonitor monitor, FetchResult result)
void execute(ProgressMonitor monitor, FetchResult result,
String initialBranch)
throws NotSupportedException, TransportException {
askFor.clear();
localUpdates.clear();
@@ -89,7 +91,7 @@ class FetchProcess {

Throwable e1 = null;
try {
executeImp(monitor, result);
executeImp(monitor, result, initialBranch);
} catch (NotSupportedException | TransportException err) {
e1 = err;
throw err;
@@ -107,9 +109,22 @@ class FetchProcess {
}
}

private boolean isInitialBranchMissing(Map<String, Ref> refsMap,
String initialBranch) {
if (StringUtils.isEmptyOrNull(initialBranch) || refsMap.isEmpty()) {
return false;
}
if (refsMap.containsKey(initialBranch)
|| refsMap.containsKey(Constants.R_HEADS + initialBranch)
|| refsMap.containsKey(Constants.R_TAGS + initialBranch)) {
return false;
}
return true;
}

private void executeImp(final ProgressMonitor monitor,
final FetchResult result) throws NotSupportedException,
TransportException {
final FetchResult result, String initialBranch)
throws NotSupportedException, TransportException {
final TagOpt tagopt = transport.getTagOpt();
String getTags = (tagopt == TagOpt.NO_TAGS) ? null : Constants.R_TAGS;
String getHead = null;
@@ -126,7 +141,12 @@ class FetchProcess {
}
conn = transport.openFetch(toFetch, getTags, getHead);
try {
result.setAdvertisedRefs(transport.getURI(), conn.getRefsMap());
Map<String, Ref> refsMap = conn.getRefsMap();
if (isInitialBranchMissing(refsMap, initialBranch)) {
throw new TransportException(MessageFormat.format(
JGitText.get().remoteBranchNotFound, initialBranch));
}
result.setAdvertisedRefs(transport.getURI(), refsMap);
result.peerUserAgent = conn.getPeerUserAgent();
final Set<Ref> matched = new HashSet<>();
for (RefSpec spec : toFetch) {

+ 45
- 2
org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java View File

@@ -1231,9 +1231,52 @@ public abstract class Transport implements AutoCloseable {
* the remote connection could not be established or object
* copying (if necessary) failed or update specification was
* incorrect.
* @since 5.11
*/
public FetchResult fetch(final ProgressMonitor monitor,
Collection<RefSpec> toFetch)
throws NotSupportedException, TransportException {
return fetch(monitor, toFetch, null);
}

/**
* Fetch objects and refs from the remote repository to the local one.
* <p>
* This is a utility function providing standard fetch behavior. Local
* tracking refs associated with the remote repository are automatically
* updated if this transport was created from a
* {@link org.eclipse.jgit.transport.RemoteConfig} with fetch RefSpecs
* defined.
*
* @param monitor
* progress monitor to inform the user about our processing
* activity. Must not be null. Use
* {@link org.eclipse.jgit.lib.NullProgressMonitor} if progress
* updates are not interesting or necessary.
* @param toFetch
* specification of refs to fetch locally. May be null or the
* empty collection to use the specifications from the
* RemoteConfig. Source for each RefSpec can't be null.
* @param branch
* the initial branch to check out when cloning the repository.
* Can be specified as ref name (<code>refs/heads/master</code>),
* branch name (<code>master</code>) or tag name
* (<code>v1.2.3</code>). The default is to use the branch
* pointed to by the cloned repository's HEAD and can be
* requested by passing {@code null} or <code>HEAD</code>.
* @return information describing the tracking refs updated.
* @throws org.eclipse.jgit.errors.NotSupportedException
* this transport implementation does not support fetching
* objects.
* @throws org.eclipse.jgit.errors.TransportException
* the remote connection could not be established or object
* copying (if necessary) failed or update specification was
* incorrect.
* @since 5.11
*/
public FetchResult fetch(final ProgressMonitor monitor,
Collection<RefSpec> toFetch) throws NotSupportedException,
Collection<RefSpec> toFetch, String branch)
throws NotSupportedException,
TransportException {
if (toFetch == null || toFetch.isEmpty()) {
// If the caller did not ask for anything use the defaults.
@@ -1263,7 +1306,7 @@ public abstract class Transport implements AutoCloseable {
}

final FetchResult result = new FetchResult();
new FetchProcess(this, toFetch).execute(monitor, result);
new FetchProcess(this, toFetch).execute(monitor, result, branch);

local.autoGC(monitor);


Loading…
Cancel
Save