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



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


import java.io.File; import java.io.File;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.transport.RefSpec;
assertEquals("expected 1 branch", 1, branches.size()); 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 { private RevCommit createInitialCommit() throws Exception {
JGitTestUtil.writeTrashFile(db, "hello.txt", "world"); JGitTestUtil.writeTrashFile(db, "hello.txt", "world");
git.add().addFilepattern("hello.txt").call(); git.add().addFilepattern("hello.txt").call();

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

import org.eclipse.jgit.api.CloneCommand; import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.InvalidRemoteException; 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.AnyObjectId;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.TextProgressMonitor; import org.eclipse.jgit.lib.TextProgressMonitor;
db = command.call().getRepository(); db = command.call().getRepository();
if (msgs && db.resolve(Constants.HEAD) == null) if (msgs && db.resolve(Constants.HEAD) == null)
outw.println(CLIText.get().clonedEmptyRepository); outw.println(CLIText.get().clonedEmptyRepository);
} catch (TransportException e) {
throw die(e.getMessage(), e);
} catch (InvalidRemoteException e) { } catch (InvalidRemoteException e) {
throw die(MessageFormat.format(CLIText.get().doesNotExist, throw die(MessageFormat.format(CLIText.get().doesNotExist,
sourceUri), e); sourceUri), e);

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

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

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

command.setTagOpt( command.setTagOpt(
fetchAll ? TagOpt.FETCH_TAGS : TagOpt.AUTO_FOLLOW); fetchAll ? TagOpt.FETCH_TAGS : TagOpt.AUTO_FOLLOW);
} }
command.setInitialBranch(branch);
configure(command); configure(command);


return command.call(); return command.call();

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



private boolean isForceUpdate; private boolean isForceUpdate;


private String initialBranch;

/** /**
* Callback for status of fetch operation. * Callback for status of fetch operation.
* *
transport.setFetchThin(thin); transport.setFetchThin(thin);
configure(transport); configure(transport);
FetchResult result = transport.fetch(monitor, FetchResult result = transport.fetch(monitor,
applyOptions(refSpecs));
applyOptions(refSpecs), initialBranch);
if (!repo.isBare()) { if (!repo.isBare()) {
fetchSubmodules(result); fetchSubmodules(result);
} }
return this; 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. * Register a progress callback.
* *

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

/***/ public String reftableDirExists; /***/ public String reftableDirExists;
/***/ public String reftableRecordsMustIncrease; /***/ public String reftableRecordsMustIncrease;
/***/ public String refUpdateReturnCodeWas; /***/ public String refUpdateReturnCodeWas;
/***/ public String remoteBranchNotFound;
/***/ public String remoteConfigHasNoURIAssociated; /***/ public String remoteConfigHasNoURIAssociated;
/***/ public String remoteDoesNotHaveSpec; /***/ public String remoteDoesNotHaveSpec;
/***/ public String remoteDoesNotSupportSmartHTTPPush; /***/ public String remoteDoesNotSupportSmartHTTPPush;

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

import org.eclipse.jgit.lib.RefDatabase; import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.revwalk.ObjectWalk; import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.util.StringUtils;


class FetchProcess { class FetchProcess {
/** Transport we will fetch over. */ /** Transport we will fetch over. */
toFetch = f; toFetch = f;
} }


void execute(ProgressMonitor monitor, FetchResult result)
void execute(ProgressMonitor monitor, FetchResult result,
String initialBranch)
throws NotSupportedException, TransportException { throws NotSupportedException, TransportException {
askFor.clear(); askFor.clear();
localUpdates.clear(); localUpdates.clear();


Throwable e1 = null; Throwable e1 = null;
try { try {
executeImp(monitor, result);
executeImp(monitor, result, initialBranch);
} catch (NotSupportedException | TransportException err) { } catch (NotSupportedException | TransportException err) {
e1 = err; e1 = err;
throw err; throw err;
} }
} }


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, 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(); final TagOpt tagopt = transport.getTagOpt();
String getTags = (tagopt == TagOpt.NO_TAGS) ? null : Constants.R_TAGS; String getTags = (tagopt == TagOpt.NO_TAGS) ? null : Constants.R_TAGS;
String getHead = null; String getHead = null;
} }
conn = transport.openFetch(toFetch, getTags, getHead); conn = transport.openFetch(toFetch, getTags, getHead);
try { 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(); result.peerUserAgent = conn.getPeerUserAgent();
final Set<Ref> matched = new HashSet<>(); final Set<Ref> matched = new HashSet<>();
for (RefSpec spec : toFetch) { for (RefSpec spec : toFetch) {

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

* the remote connection could not be established or object * the remote connection could not be established or object
* copying (if necessary) failed or update specification was * copying (if necessary) failed or update specification was
* incorrect. * 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, public FetchResult fetch(final ProgressMonitor monitor,
Collection<RefSpec> toFetch) throws NotSupportedException,
Collection<RefSpec> toFetch, String branch)
throws NotSupportedException,
TransportException { TransportException {
if (toFetch == null || toFetch.isEmpty()) { if (toFetch == null || toFetch.isEmpty()) {
// If the caller did not ask for anything use the defaults. // If the caller did not ask for anything use the defaults.
} }


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


local.autoGC(monitor); local.autoGC(monitor);



Loading…
Cancel
Save