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>
This commit is contained in:
Matthias Sohn 2021-01-24 00:17:13 +01:00
parent 704ccdc096
commit 64cb7148ac
8 changed files with 139 additions and 8 deletions

View File

@ -11,7 +11,9 @@
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.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 void testClone() throws Exception {
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();

View File

@ -18,6 +18,7 @@
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 @@ protected void run() throws Exception {
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);

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

View File

@ -297,6 +297,7 @@ private FetchResult fetch(Repository clonedRepo, URIish u)
command.setTagOpt(
fetchAll ? TagOpt.FETCH_TAGS : TagOpt.AUTO_FOLLOW);
}
command.setInitialBranch(branch);
configure(command);
return command.call();

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 FetchResult call() throws GitAPIException, InvalidRemoteException,
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 FetchCommand setTagOpt(TagOpt tagOpt) {
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.
*

View File

@ -596,6 +596,7 @@ public static JGitText get() {
/***/ public String reftableDirExists;
/***/ public String reftableRecordsMustIncrease;
/***/ public String refUpdateReturnCodeWas;
/***/ public String remoteBranchNotFound;
/***/ public String remoteConfigHasNoURIAssociated;
/***/ public String remoteDoesNotHaveSpec;
/***/ public String remoteDoesNotSupportSmartHTTPPush;

View File

@ -48,6 +48,7 @@
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 @@ void execute(ProgressMonitor monitor, FetchResult result)
Throwable e1 = null;
try {
executeImp(monitor, result);
executeImp(monitor, result, initialBranch);
} catch (NotSupportedException | TransportException err) {
e1 = err;
throw err;
@ -107,9 +109,22 @@ void execute(ProgressMonitor monitor, FetchResult result)
}
}
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 @@ private void executeImp(final ProgressMonitor monitor,
}
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) {

View File

@ -1231,9 +1231,52 @@ public void setPushOptions(List<String> pushOptions) {
* 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)
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, 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 FetchResult fetch(final ProgressMonitor monitor,
}
final FetchResult result = new FetchResult();
new FetchProcess(this, toFetch).execute(monitor, result);
new FetchProcess(this, toFetch).execute(monitor, result, branch);
local.autoGC(monitor);