From 2fba1e65e1ce5070b56fe9ef3573a6bf7b5dcf3b Mon Sep 17 00:00:00 2001 From: Matthias Sohn Date: Tue, 1 Mar 2011 00:21:14 +0100 Subject: [PATCH] Fix NPE on checkout of remote tracking branch Checkout of remote tracking branch failed when no local branch existed. Also enhance RepositoryTestCase to enable checking index state of another test repository. Bug: 337695 Change-Id: Idf4c05bdf23b5161688818342b2bf9a45b49f479 Signed-off-by: Matthias Sohn --- .../eclipse/jgit/api/CheckoutCommandTest.java | 34 +++++++++++ .../eclipse/jgit/lib/RepositoryTestCase.java | 56 +++++++++++++++++-- .../org/eclipse/jgit/api/CheckoutCommand.java | 18 +++--- 3 files changed, 95 insertions(+), 13 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java index 743733586..21fbe0a13 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CheckoutCommandTest.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2010, Chris Aniszczyk + * Copyright (C) 2011, Matthias Sohn * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -61,8 +62,13 @@ import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.transport.RefSpec; +import org.eclipse.jgit.transport.RemoteConfig; +import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.util.FileUtils; import org.junit.Before; import org.junit.Test; @@ -205,4 +211,32 @@ public void testCheckoutCommit() { fail(e.getMessage()); } } + + @Test + public void testCheckoutRemoteTrackingWithoutLocalBranch() { + try { + // create second repository + Repository db2 = createWorkRepository(); + Git git2 = new Git(db2); + + // setup the second repository to fetch from the first repository + final StoredConfig config = db2.getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "origin"); + URIish uri = new URIish(db.getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.update(config); + config.save(); + + // fetch from first repository + RefSpec spec = new RefSpec("+refs/heads/*:refs/remotes/origin/*"); + git2.fetch().setRemote("origin").setRefSpecs(spec).call(); + // checkout remote tracking branch in second repository + // (no local branches exist yet in second repository) + git2.checkout().setName("remotes/origin/test").call(); + assertEquals("[Test.txt, mode:100644, content:Some change]", + indexState(db2, CONTENT)); + } catch (Exception e) { + fail(e.getMessage()); + } + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryTestCase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryTestCase.java index 3a50bca5f..ee4165dfb 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryTestCase.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/RepositoryTestCase.java @@ -167,20 +167,24 @@ public void setUp() throws Exception { * 'stage' is only presented when the stage is different from 0. All * reported time stamps are mapped to strings like "t0", "t1", ... "tn". The * smallest reported time-stamp will be called "t0". This allows to write - * assertions against the string although the concrete value of the - * time stamps is unknown. + * assertions against the string although the concrete value of the time + * stamps is unknown. + * + * @param repo + * the repository the index state should be determined for * * @param includedOptions * a bitmask constructed out of the constants {@link #MOD_TIME}, - * {@link #SMUDGE}, {@link #LENGTH}, {@link #CONTENT_ID} and {@link #CONTENT} - * controlling which info is present in the resulting string. + * {@link #SMUDGE}, {@link #LENGTH}, {@link #CONTENT_ID} and + * {@link #CONTENT} controlling which info is present in the + * resulting string. * @return a string encoding the index state * @throws IllegalStateException * @throws IOException */ - public String indexState(int includedOptions) + public String indexState(Repository repo, int includedOptions) throws IllegalStateException, IOException { - DirCache dc = db.readDirCache(); + DirCache dc = repo.readDirCache(); StringBuilder sb = new StringBuilder(); TreeSet timeStamps = null; @@ -223,6 +227,46 @@ public String indexState(int includedOptions) return sb.toString(); } + /** + * Represent the state of the index in one String. This representation is + * useful when writing tests which do assertions on the state of the index. + * By default information about path, mode, stage (if different from 0) is + * included. A bitmask controls which additional info about + * modificationTimes, smudge state and length is included. + *

+ * The format of the returned string is described with this BNF: + * + *

+	 * result = ( "[" path mode stage? time? smudge? length? sha1? content? "]" )* .
+	 * mode = ", mode:" number .
+	 * stage = ", stage:" number .
+	 * time = ", time:t" timestamp-index .
+	 * smudge = "" | ", smudged" .
+	 * length = ", length:" number .
+	 * sha1 = ", sha1:" hex-sha1 .
+	 * content = ", content:" blob-data .
+	 * 
+ * + * 'stage' is only presented when the stage is different from 0. All + * reported time stamps are mapped to strings like "t0", "t1", ... "tn". The + * smallest reported time-stamp will be called "t0". This allows to write + * assertions against the string although the concrete value of the time + * stamps is unknown. + * + * @param includedOptions + * a bitmask constructed out of the constants {@link #MOD_TIME}, + * {@link #SMUDGE}, {@link #LENGTH}, {@link #CONTENT_ID} and + * {@link #CONTENT} controlling which info is present in the + * resulting string. + * @return a string encoding the index state + * @throws IllegalStateException + * @throws IOException + */ + public String indexState(int includedOptions) + throws IllegalStateException, IOException { + return indexState(db, includedOptions); + } + /** * Resets the index to represent exactly some filesystem content. E.g. the * following call will replace the index with the working tree content: diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java index 73ca26167..16c1fcfa3 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CheckoutCommand.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2010, Chris Aniszczyk + * Copyright (C) 2011, Matthias Sohn * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -54,13 +55,15 @@ import org.eclipse.jgit.dircache.DirCacheCheckout; import org.eclipse.jgit.errors.AmbiguousObjectException; import org.eclipse.jgit.errors.CheckoutConflictException; +import org.eclipse.jgit.lib.AnyObjectId; 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.RefUpdate.Result; +import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevWalk; /** @@ -119,21 +122,22 @@ public Ref call() throws JGitInternalException, RefAlreadyExistsException, command.call(); } - RevWalk revWalk = new RevWalk(repo); Ref headRef = repo.getRef(Constants.HEAD); - RevCommit headCommit = revWalk.parseCommit(headRef.getObjectId()); String refLogMessage = "checkout: moving from " + headRef.getTarget().getName(); ObjectId branch = repo.resolve(name); - if (branch == null) throw new RefNotFoundException(MessageFormat.format(JGitText .get().refNotResolved, name)); + RevWalk revWalk = new RevWalk(repo); + AnyObjectId headId = headRef.getObjectId(); + RevCommit headCommit = headId == null ? null : revWalk + .parseCommit(headId); RevCommit newCommit = revWalk.parseCommit(branch); - - DirCacheCheckout dco = new DirCacheCheckout(repo, headCommit - .getTree(), repo.lockDirCache(), newCommit.getTree()); + RevTree headTree = headCommit == null ? null : headCommit.getTree(); + DirCacheCheckout dco = new DirCacheCheckout(repo, headTree, + repo.lockDirCache(), newCommit.getTree()); dco.setFailOnConflict(true); try { dco.checkout();