5

I have a bare repo located at main.git and am trying to fetch a branch (foo, let's say) in another repo, test, which has only just been git init'd:

fetchtest/
  |- main.git/
  |- test/
       |- .git/

Using regular git commands, I can do a git fetch ../main.git foo:foo and this will make a new branch foo in test/ and fetch the objects required for the branch. I then want to do the same thing but programmatically using JGit, ie not using the git CLI but using only Java code. There is no way I can use the git CLI:

Git git = Git.init().setDirectory(new File("fetchtest/test/")).call();

git.fetch().setRemote(new File("../main.git"))
           .setRefSpecs(new RefSpec("foo:foo"))
           .call();

but it just errors with:

org.eclipse.jgit.api.errors.TransportException: Remote does not have foo available for fetch.
    at org.eclipse.jgit.api.FetchCommand.call(FetchCommand.java:137)
    // ......
Caused by: org.eclipse.jgit.errors.TransportException: Remote does not have foo available for fetch.
    at org.eclipse.jgit.transport.FetchProcess.expandSingle(FetchProcess.java:349)
    at org.eclipse.jgit.transport.FetchProcess.executeImp(FetchProcess.java:139)
    at org.eclipse.jgit.transport.FetchProcess.execute(FetchProcess.java:113)
    at org.eclipse.jgit.transport.Transport.fetch(Transport.java:1069)
    at org.eclipse.jgit.api.FetchCommand.call(FetchCommand.java:128)

How do I get this to work?

Callum Rogers
  • 15,630
  • 17
  • 67
  • 90
  • The answer you are *not* looking for: learn **command line git** d= – dcow Jul 03 '12 at 05:28
  • 1
    @David: I should add that I *cannot* use the git command line - I have to use it programmatically. (J)Git is actually part of the application I'm building which will be run on client's hardware and git will not be installed (nor would I want to install it, being a pain to set up on windows). The only thing they will have is Java. – Callum Rogers Jul 03 '12 at 06:07

1 Answers1

5

What should work:

Git git = Git.init().setDirectory(new File("fetchtest/test/")).call();

git.fetch().setRemote(new File("../main.git"))
           .setRefSpecs(new RefSpec("refs/heads/foo:refs/heads/foo"))
           .call();

Note the RefSpec definition.
At least, try in your example:

new RefSpec("refs/heads/foo:refs/heads/foo")

The RefSpec class mentions:

/**
 * Parse a ref specification for use during transport operations.
 * <p>
 * Specifications are typically one of the following forms:
 * <ul>
 * <li><code>refs/head/master</code></li>
 * <li><code>refs/head/master:refs/remotes/origin/master</code></li>
 * <li><code>refs/head/*:refs/remotes/origin/*</code></li>
 * <li><code>+refs/head/master</code></li>
 * <li><code>+refs/head/master:refs/remotes/origin/master</code></li>
 * <li><code>+refs/head/*:refs/remotes/origin/*</code></li>
 * <li><code>:refs/head/master</code></li>
 * </ul>
 *
 * @param spec
 * string describing the specification.
 * @throws IllegalArgumentException
 * the specification is invalid.
*/

So "refs/head/" seems mandatory.


Original answer:

The setRemote() function on api.FetchCommand takes a name or an URI.

And looking at the FetchCommandTest URI definition, I prefer making the remote more visible:
I would rather define a named remote (here below: "test") for your second repo (referring your first repo), and then fetch.

// setup the first repository to fetch from the second repository
final StoredConfig config = db.getConfig();
RemoteConfig remoteConfig = new RemoteConfig(config, "test");
URIish uri = new URIish(db2.getDirectory().toURI().toURL());
remoteConfig.addURI(uri);
remoteConfig.update(config);
config.save();

// create some refs via commits and tag
RevCommit commit = git2.commit().setMessage("initial commit").call();
Ref tagRef = git2.tag().setName("tag").call();

Git git1 = new Git(db);

RefSpec spec = new RefSpec("refs/heads/master:refs/heads/x");
git1.fetch().setRemote("test").setRefSpecs(spec)
.call();
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • Thanks for the answer, I'll try out your suggestions. I think the relative path thing should work (I actually use `new File(relativePath).getAbsolutePath()`) and this also works for push commands. – Callum Rogers Jul 03 '12 at 06:11
  • @CallumRogers I just edited the answer to add a remark on `RefSpec`. – VonC Jul 03 '12 at 06:13
  • 1
    Excellent! It now works - the problem, as you correctly got, was that the full `refs/heads/foo` needed to be used for each branch in the `RefSpec`! Thanks a bunch. – Callum Rogers Jul 03 '12 at 06:34
  • 1
    @CallumRogers that's great. I have reorganized the anwer to make the actual solution (about `RefSpec`) more visible. – VonC Jul 03 '12 at 06:56