import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.InvalidRefNameException;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
-import org.eclipse.jgit.api.errors.RefNotFoundException;
+import org.eclipse.jgit.gitrepo.RepoCommand.RemoteFile;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.BlobBasedConfig;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.FS;
import org.junit.Test;
static class IndexedRepos implements RepoCommand.RemoteReader {
Map<String, Repository> uriRepoMap;
+
IndexedRepos() {
uriRepoMap = new HashMap<>();
}
}
@Override
- public byte[] readFile(String uri, String refName, String path)
- throws GitAPIException, IOException {
+ public RemoteFile readFileWithMode(String uri, String ref, String path)
+ throws GitAPIException, IOException {
Repository repo = uriRepoMap.get(uri);
-
- String idStr = refName + ":" + path;
- ObjectId id = repo.resolve(idStr);
- if (id == null) {
- throw new RefNotFoundException(
- String.format("repo %s does not have %s", repo.toString(), idStr));
- }
- try (ObjectReader reader = repo.newObjectReader()) {
- return reader.open(id).getCachedBytes(Integer.MAX_VALUE);
+ ObjectId refCommitId = sha1(uri, ref);
+ if (refCommitId == null) {
+ throw new InvalidRefNameException(MessageFormat
+ .format(JGitText.get().refNotResolved, ref));
}
+ RevCommit commit = repo.parseCommit(refCommitId);
+ TreeWalk tw = TreeWalk.forPath(repo, path, commit.getTree());
+
+ // TODO(ifrade): Cope better with big files (e.g. using InputStream
+ // instead of byte[])
+ return new RemoteFile(tw.getObjectReader().open(tw.getObjectId(0))
+ .getCachedBytes(Integer.MAX_VALUE), tw.getFileMode(0));
}
}
import java.util.StringJoiner;
import java.util.TreeMap;
+import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.GitCommand;
import org.eclipse.jgit.api.SubmoduleAddCommand;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.InvalidRefNameException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.FileUtils;
/**
* @param uri
* The URI of the remote repository
* @param ref
- * The ref (branch/tag/etc.) to read
+ * Name of the ref to lookup. May be a short-hand form, e.g.
+ * "master" which is is automatically expanded to
+ * "refs/heads/master" if "refs/heads/master" already exists.
* @return the sha1 of the remote repository, or null if the ref does
* not exist.
* @throws GitAPIException
* @throws GitAPIException
* @throws IOException
* @since 3.5
+ *
+ * @deprecated Use {@link #readFileWithMode(String, String, String)}
+ * instead
*/
- public byte[] readFile(String uri, String ref, String path)
+ @Deprecated
+ public default byte[] readFile(String uri, String ref, String path)
+ throws GitAPIException, IOException {
+ return readFileWithMode(uri, ref, path).getContents();
+ }
+
+ /**
+ * Read contents and mode (i.e. permissions) of the file from a remote
+ * repository.
+ *
+ * @param uri
+ * The URI of the remote repository
+ * @param ref
+ * Name of the ref to lookup. May be a short-hand form, e.g.
+ * "master" which is is automatically expanded to
+ * "refs/heads/master" if "refs/heads/master" already exists.
+ * @param path
+ * The relative path (inside the repo) to the file to read
+ * @return The contents and file mode of the file in the given
+ * repository and branch. Never null.
+ * @throws GitAPIException
+ * If the ref have an invalid or ambiguous name, or it does
+ * not exist in the repository,
+ * @throws IOException
+ * If the object does not exist or is too large
+ * @since 5.2
+ */
+ @NonNull
+ public RemoteFile readFileWithMode(String uri, String ref, String path)
throws GitAPIException, IOException;
}
+ /**
+ * Read-only view of contents and file mode (i.e. permissions) for a file in
+ * a remote repository.
+ *
+ * @since 5.2
+ */
+ public static final class RemoteFile {
+ @NonNull
+ private final byte[] contents;
+
+ @NonNull
+ private final FileMode fileMode;
+
+ /**
+ * @param contents
+ * Raw contents of the file.
+ * @param fileMode
+ * Git file mode for this file (e.g. executable or regular)
+ */
+ public RemoteFile(@NonNull byte[] contents,
+ @NonNull FileMode fileMode) {
+ this.contents = Objects.requireNonNull(contents);
+ this.fileMode = Objects.requireNonNull(fileMode);
+ }
+
+ /**
+ * Contents of the file.
+ * <p>
+ * Callers who receive this reference must not modify its contents (as
+ * it can point to internal cached data).
+ *
+ * @return Raw contents of the file. Do not modify it.
+ */
+ @NonNull
+ public byte[] getContents() {
+ return contents;
+ }
+
+ /**
+ * @return Git file mode for this file (e.g. executable or regular)
+ */
+ @NonNull
+ public FileMode getFileMode() {
+ return fileMode;
+ }
+
+ }
+
/** A default implementation of {@link RemoteReader} callback. */
public static class DefaultRemoteReader implements RemoteReader {
+
@Override
public ObjectId sha1(String uri, String ref) throws GitAPIException {
Map<String, Ref> map = Git
}
@Override
- public byte[] readFile(String uri, String ref, String path)
+ public RemoteFile readFileWithMode(String uri, String ref, String path)
throws GitAPIException, IOException {
File dir = FileUtils.createTempDir("jgit_", ".git", null); //$NON-NLS-1$ //$NON-NLS-2$
try (Git git = Git.cloneRepository().setBare(true).setDirectory(dir)
.setURI(uri).call()) {
- return readFileFromRepo(git.getRepository(), ref, path);
+ Repository repo = git.getRepository();
+ ObjectId refCommitId = sha1(uri, ref);
+ if (refCommitId == null) {
+ throw new InvalidRefNameException(MessageFormat
+ .format(JGitText.get().refNotResolved, ref));
+ }
+ RevCommit commit = repo.parseCommit(refCommitId);
+ TreeWalk tw = TreeWalk.forPath(repo, path, commit.getTree());
+
+ // TODO(ifrade): Cope better with big files (e.g. using
+ // InputStream instead of byte[])
+ return new RemoteFile(
+ tw.getObjectReader().open(tw.getObjectId(0))
+ .getCachedBytes(Integer.MAX_VALUE),
+ tw.getFileMode(0));
} finally {
FileUtils.delete(dir, FileUtils.RECURSIVE);
}
}
-
- /**
- * Read a file from the repository
- *
- * @param repo
- * The repository containing the file
- * @param ref
- * The ref (branch/tag/etc.) to read
- * @param path
- * The relative path (inside the repo) to the file to read
- * @return the file's content
- * @throws GitAPIException
- * @throws IOException
- * @since 3.5
- */
- protected byte[] readFileFromRepo(Repository repo,
- String ref, String path) throws GitAPIException, IOException {
- try (ObjectReader reader = repo.newObjectReader()) {
- ObjectId oid = repo.resolve(ref + ":" + path); //$NON-NLS-1$
- return reader.open(oid).getBytes(Integer.MAX_VALUE);
- }
- }
}
@SuppressWarnings("serial")