Browse Source

RepoCommand.RemoteReader: Add method to read contents and mode of file

The RepoCommand.RemoteReader interface doesn't offer access to the mode
of a file. Caller can only default to mark the copied objects as regular
files, losing e.g. the executable bit (if set).

Add a new method readFileWithMode that returns the contents and mode of
the remote file. It supersedes the readFile method, that is marked as
deprecated.

Now callers can set correctly the file mode of the copied file.

Change-Id: I8fce01e4bc5707434c0cbc4aebbae1b6b64756f0
Signed-off-by: Ivan Frade <ifrade@google.com>
stable-5.2
Ivan Frade 6 years ago
parent
commit
f648a3bd81
  1. 31
      org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java
  2. 130
      org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java

31
org.eclipse.jgit.test/tst/org/eclipse/jgit/gitrepo/RepoCommandTest.java

@ -56,13 +56,16 @@ import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException; 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.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.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.BlobBasedConfig; import org.eclipse.jgit.lib.BlobBasedConfig;
@ -74,6 +77,7 @@ import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FS;
import org.junit.Test; import org.junit.Test;
@ -142,6 +146,7 @@ public class RepoCommandTest extends RepositoryTestCase {
static class IndexedRepos implements RepoCommand.RemoteReader { static class IndexedRepos implements RepoCommand.RemoteReader {
Map<String, Repository> uriRepoMap; Map<String, Repository> uriRepoMap;
IndexedRepos() { IndexedRepos() {
uriRepoMap = new HashMap<>(); uriRepoMap = new HashMap<>();
} }
@ -170,19 +175,21 @@ public class RepoCommandTest extends RepositoryTestCase {
} }
@Override @Override
public byte[] readFile(String uri, String refName, String path) public RemoteFile readFileWithMode(String uri, String ref, String path)
throws GitAPIException, IOException { throws GitAPIException, IOException {
Repository repo = uriRepoMap.get(uri); Repository repo = uriRepoMap.get(uri);
ObjectId refCommitId = sha1(uri, ref);
String idStr = refName + ":" + path; if (refCommitId == null) {
ObjectId id = repo.resolve(idStr); throw new InvalidRefNameException(MessageFormat
if (id == null) { .format(JGitText.get().refNotResolved, ref));
throw new RefNotFoundException( }
String.format("repo %s does not have %s", repo.toString(), idStr)); RevCommit commit = repo.parseCommit(refCommitId);
} TreeWalk tw = TreeWalk.forPath(repo, path, commit.getTree());
try (ObjectReader reader = repo.newObjectReader()) {
return reader.open(id).getCachedBytes(Integer.MAX_VALUE); // 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));
} }
} }

130
org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java

@ -59,12 +59,14 @@ import java.util.Objects;
import java.util.StringJoiner; import java.util.StringJoiner;
import java.util.TreeMap; import java.util.TreeMap;
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.GitCommand; import org.eclipse.jgit.api.GitCommand;
import org.eclipse.jgit.api.SubmoduleAddCommand; import org.eclipse.jgit.api.SubmoduleAddCommand;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException; import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.api.errors.GitAPIException; 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.api.errors.JGitInternalException;
import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder; import org.eclipse.jgit.dircache.DirCacheBuilder;
@ -80,7 +82,6 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
@ -90,6 +91,7 @@ import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.FileUtils; import org.eclipse.jgit.util.FileUtils;
/** /**
@ -144,7 +146,9 @@ public class RepoCommand extends GitCommand<RevCommit> {
* @param uri * @param uri
* The URI of the remote repository * The URI of the remote repository
* @param ref * @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 * @return the sha1 of the remote repository, or null if the ref does
* not exist. * not exist.
* @throws GitAPIException * @throws GitAPIException
@ -165,13 +169,93 @@ public class RepoCommand extends GitCommand<RevCommit> {
* @throws GitAPIException * @throws GitAPIException
* @throws IOException * @throws IOException
* @since 3.5 * @since 3.5
*
* @deprecated Use {@link #readFileWithMode(String, String, String)}
* instead
*/
@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
*/ */
public byte[] readFile(String uri, String ref, String path) @NonNull
public RemoteFile readFileWithMode(String uri, String ref, String path)
throws GitAPIException, IOException; 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. */ /** A default implementation of {@link RemoteReader} callback. */
public static class DefaultRemoteReader implements RemoteReader { public static class DefaultRemoteReader implements RemoteReader {
@Override @Override
public ObjectId sha1(String uri, String ref) throws GitAPIException { public ObjectId sha1(String uri, String ref) throws GitAPIException {
Map<String, Ref> map = Git Map<String, Ref> map = Git
@ -183,38 +267,30 @@ public class RepoCommand extends GitCommand<RevCommit> {
} }
@Override @Override
public byte[] readFile(String uri, String ref, String path) public RemoteFile readFileWithMode(String uri, String ref, String path)
throws GitAPIException, IOException { throws GitAPIException, IOException {
File dir = FileUtils.createTempDir("jgit_", ".git", null); //$NON-NLS-1$ //$NON-NLS-2$ File dir = FileUtils.createTempDir("jgit_", ".git", null); //$NON-NLS-1$ //$NON-NLS-2$
try (Git git = Git.cloneRepository().setBare(true).setDirectory(dir) try (Git git = Git.cloneRepository().setBare(true).setDirectory(dir)
.setURI(uri).call()) { .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 { } finally {
FileUtils.delete(dir, FileUtils.RECURSIVE); 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") @SuppressWarnings("serial")

Loading…
Cancel
Save