diff options
Diffstat (limited to 'org.eclipse.jgit')
22 files changed, 474 insertions, 158 deletions
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index b354690e4f..258e6781b5 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF @@ -2,27 +2,27 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin_name Bundle-SymbolicName: org.eclipse.jgit -Bundle-Version: 0.8.5.qualifier +Bundle-Version: 0.9.0.qualifier Bundle-Localization: plugin Bundle-Vendor: %provider_name -Export-Package: org.eclipse.jgit;version="0.8.5", - org.eclipse.jgit.api;version="0.8.5", - org.eclipse.jgit.diff;version="0.8.5", - org.eclipse.jgit.dircache;version="0.8.5", - org.eclipse.jgit.errors;version="0.8.5", - org.eclipse.jgit.fnmatch;version="0.8.5", - org.eclipse.jgit.lib;version="0.8.5", - org.eclipse.jgit.merge;version="0.8.5", - org.eclipse.jgit.nls;version="0.8.5", - org.eclipse.jgit.patch;version="0.8.5", - org.eclipse.jgit.revplot;version="0.8.5", - org.eclipse.jgit.revwalk;version="0.8.5", - org.eclipse.jgit.revwalk.filter;version="0.8.5", - org.eclipse.jgit.transport;version="0.8.5", - org.eclipse.jgit.treewalk;version="0.8.5", - org.eclipse.jgit.treewalk.filter;version="0.8.5", - org.eclipse.jgit.util;version="0.8.5", - org.eclipse.jgit.util.io;version="0.8.5" +Export-Package: org.eclipse.jgit;version="0.9.0", + org.eclipse.jgit.api;version="0.9.0", + org.eclipse.jgit.diff;version="0.9.0", + org.eclipse.jgit.dircache;version="0.9.0", + org.eclipse.jgit.errors;version="0.9.0", + org.eclipse.jgit.fnmatch;version="0.9.0", + org.eclipse.jgit.lib;version="0.9.0", + org.eclipse.jgit.merge;version="0.9.0", + org.eclipse.jgit.nls;version="0.9.0", + org.eclipse.jgit.patch;version="0.9.0", + org.eclipse.jgit.revplot;version="0.9.0", + org.eclipse.jgit.revwalk;version="0.9.0", + org.eclipse.jgit.revwalk.filter;version="0.9.0", + org.eclipse.jgit.transport;version="0.9.0", + org.eclipse.jgit.treewalk;version="0.9.0", + org.eclipse.jgit.treewalk.filter;version="0.9.0", + org.eclipse.jgit.util;version="0.9.0", + org.eclipse.jgit.util.io;version="0.9.0" Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: J2SE-1.5 Require-Bundle: com.jcraft.jsch;bundle-version="[0.1.37,0.2.0)" diff --git a/org.eclipse.jgit/pom.xml b/org.eclipse.jgit/pom.xml index 0b0d83ec3a..fc8e72e8e4 100644 --- a/org.eclipse.jgit/pom.xml +++ b/org.eclipse.jgit/pom.xml @@ -53,7 +53,7 @@ <parent> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit-parent</artifactId> - <version>0.8.5-SNAPSHOT</version> + <version>0.9.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.jgit</artifactId> @@ -82,7 +82,6 @@ <directory>.</directory> <includes> <include>plugin.properties</include> - <include>about.html</include> </includes> </resource> <resource> diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java index 0203d5d44a..d5cab0e4c6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java @@ -71,7 +71,6 @@ import org.eclipse.jgit.JGitText; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.NotSupportedException; -import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.RawParseUtils; /** @@ -328,16 +327,16 @@ public class GitIndex { } } - static boolean File_canExecute( File f){ - return FS.INSTANCE.canExecute(f); + private boolean File_canExecute(File f){ + return db.getFS().canExecute(f); } - static boolean File_setExecute(File f, boolean value) { - return FS.INSTANCE.setExecute(f, value); + private boolean File_setExecute(File f, boolean value) { + return db.getFS().setExecute(f, value); } - static boolean File_hasExecute() { - return FS.INSTANCE.supportsExecute(); + private boolean File_hasExecute() { + return db.getFS().supportsExecute(); } static byte[] makeKey(File wd, File f) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java index 06700ebe1d..9a5bcdb68c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java @@ -88,6 +88,8 @@ public class ObjectDirectory extends ObjectDatabase { private final File[] alternateObjectDir; + private final FS fs; + /** * Initialize a reference to an on-disk object directory. * @@ -95,14 +97,18 @@ public class ObjectDirectory extends ObjectDatabase { * the location of the <code>objects</code> directory. * @param alternateObjectDir * a list of alternate object directories + * @param fs + * the file system abstraction which will be necessary to + * perform certain file system operations. */ - public ObjectDirectory(final File dir, File[] alternateObjectDir) { + public ObjectDirectory(final File dir, File[] alternateObjectDir, FS fs) { objects = dir; this.alternateObjectDir = alternateObjectDir; infoDirectory = new File(objects, "info"); packDirectory = new File(objects, "pack"); alternatesFile = new File(infoDirectory, "alternates"); packList = new AtomicReference<PackList>(NO_PACKS); + this.fs = fs; } /** @@ -485,17 +491,17 @@ public class ObjectDirectory extends ObjectDatabase { private ObjectDatabase openAlternate(final String location) throws IOException { - final File objdir = FS.resolve(objects, location); + final File objdir = fs.resolve(objects, location); return openAlternate(objdir); } private ObjectDatabase openAlternate(File objdir) throws IOException { final File parent = objdir.getParentFile(); - if (FileKey.isGitRepository(parent)) { - final Repository db = RepositoryCache.open(FileKey.exact(parent)); + if (FileKey.isGitRepository(parent, fs)) { + final Repository db = RepositoryCache.open(FileKey.exact(parent, fs)); return new AlternateRepositoryDatabase(db); } - return new ObjectDirectory(objdir, null); + return new ObjectDirectory(objdir, null, fs); } private static final class PackList { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectWriter.java index ea57a0215e..20147ed6ce 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectWriter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectWriter.java @@ -300,6 +300,24 @@ public class ObjectWriter { return writeObject(Constants.OBJ_BLOB, len, is, false); } + /** + * Compute the SHA-1 of an object without actually creating an object in the + * database + * + * @param type + * kind of object + * @param len + * number of bytes to consume + * @param is + * stream for read data from + * @return SHA-1 of data combined with type information + * @throws IOException + */ + public ObjectId computeObjectSha1(final int type, final long len, final InputStream is) + throws IOException { + return writeObject(type, len, is, false); + } + ObjectId writeObject(final int type, long len, final InputStream is, boolean store) throws IOException { final File t; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java index 5ba186955f..302b63b486 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDirectory.java @@ -151,12 +151,13 @@ public class RefDirectory extends RefDatabase { private final AtomicInteger lastNotifiedModCnt = new AtomicInteger(); RefDirectory(final Repository db) { + final FS fs = db.getFS(); parent = db; gitDir = db.getDirectory(); - refsDir = FS.resolve(gitDir, R_REFS); - logsDir = FS.resolve(gitDir, LOGS); - logsRefsDir = FS.resolve(gitDir, LOGS + '/' + R_REFS); - packedRefsFile = FS.resolve(gitDir, PACKED_REFS); + refsDir = fs.resolve(gitDir, R_REFS); + logsDir = fs.resolve(gitDir, LOGS); + logsRefsDir = fs.resolve(gitDir, LOGS + '/' + R_REFS); + packedRefsFile = fs.resolve(gitDir, PACKED_REFS); looseRefs.set(RefList.<LooseRef> emptyList()); packedRefs.set(PackedRefList.NO_PACKED_REFS); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java index 233cecf310..98362af987 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java @@ -102,6 +102,8 @@ public class Repository { private final File gitDir; + private final FS fs; + private final FileBasedConfig userConfig; private final RepositoryConfig config; @@ -185,6 +187,41 @@ public class Repository { */ public Repository(final File d, final File workTree, final File objectDir, final File[] alternateObjectDir, final File indexFile) throws IOException { + this(d, workTree, objectDir, alternateObjectDir, indexFile, FS.DETECTED); + } + + /** + * Construct a representation of a Git repository using the given parameters + * possibly overriding default conventions. + * + * @param d + * GIT_DIR (the location of the repository metadata). May be null + * for default value in which case it depends on GIT_WORK_TREE. + * @param workTree + * GIT_WORK_TREE (the root of the checkout). May be null for + * default value if GIT_DIR is + * @param objectDir + * GIT_OBJECT_DIRECTORY (where objects and are stored). May be + * null for default value. Relative names ares resolved against + * GIT_WORK_TREE + * @param alternateObjectDir + * GIT_ALTERNATE_OBJECT_DIRECTORIES (where more objects are read + * from). May be null for default value. Relative names ares + * resolved against GIT_WORK_TREE + * @param indexFile + * GIT_INDEX_FILE (the location of the index file). May be null + * for default value. Relative names ares resolved against + * GIT_WORK_TREE. + * @param fs + * the file system abstraction which will be necessary to + * perform certain file system operations. + * @throws IOException + * the repository appears to already exist but cannot be + * accessed. + */ + public Repository(final File d, final File workTree, final File objectDir, + final File[] alternateObjectDir, final File indexFile, + FS fs) throws IOException { if (workTree != null) { workDir = workTree; @@ -199,8 +236,10 @@ public class Repository { throw new IllegalArgumentException(JGitText.get().eitherGIT_DIRorGIT_WORK_TREEmustBePassed); } - userConfig = SystemReader.getInstance().openUserConfig(); - config = new RepositoryConfig(userConfig, FS.resolve(gitDir, "config")); + this.fs = fs; + + userConfig = SystemReader.getInstance().openUserConfig(fs); + config = new RepositoryConfig(userConfig, fs.resolve(gitDir, "config")); loadUserConfig(); loadConfig(); @@ -208,7 +247,7 @@ public class Repository { if (workDir == null) { String workTreeConfig = getConfig().getString("core", null, "worktree"); if (workTreeConfig != null) { - workDir = FS.resolve(d, workTreeConfig); + workDir = fs.resolve(d, workTreeConfig); } else { workDir = gitDir.getParentFile(); } @@ -216,11 +255,11 @@ public class Repository { refs = new RefDirectory(this); if (objectDir != null) - objectDatabase = new ObjectDirectory(FS.resolve(objectDir, ""), - alternateObjectDir); + objectDatabase = new ObjectDirectory(fs.resolve(objectDir, ""), + alternateObjectDir, fs); else - objectDatabase = new ObjectDirectory(FS.resolve(gitDir, "objects"), - alternateObjectDir); + objectDatabase = new ObjectDirectory(fs.resolve(gitDir, "objects"), + alternateObjectDir, fs); if (indexFile != null) this.indexFile = indexFile; @@ -352,6 +391,13 @@ public class Repository { } /** + * @return the used file system abstraction + */ + public FS getFS() { + return fs; + } + + /** * Construct a filename where the loose object having a specified SHA-1 * should be stored. If the object is stored in a shared repository the path * to the alternative repo will be returned. If the object is not yet store diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java index b086968c6c..0b0260a4cc 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RepositoryCache.java @@ -120,7 +120,7 @@ public class RepositoryCache { * repository to register. */ public static void register(final Repository db) { - cache.registerRepository(FileKey.exact(db.getDirectory()), db); + cache.registerRepository(FileKey.exact(db.getDirectory(), db.getFS()), db); } /** @@ -133,7 +133,7 @@ public class RepositoryCache { * repository to unregister. */ public static void close(final Repository db) { - cache.unregisterRepository(FileKey.exact(db.getDirectory())); + cache.unregisterRepository(FileKey.exact(db.getDirectory(), db.getFS())); } /** Unregister all repositories from the cache. */ @@ -248,11 +248,14 @@ public class RepositoryCache { * * @param directory * location where the repository database is. + * @param fs + * the file system abstraction which will be necessary to + * perform certain file system operations. * @return a key for the given directory. - * @see #lenient(File) + * @see #lenient(File, FS) */ - public static FileKey exact(final File directory) { - return new FileKey(directory); + public static FileKey exact(final File directory, FS fs) { + return new FileKey(directory, fs); } /** @@ -268,22 +271,30 @@ public class RepositoryCache { * * @param directory * location where the repository database might be. + * @param fs + * the file system abstraction which will be necessary to + * perform certain file system operations. * @return a key for the given directory. - * @see #exact(File) + * @see #exact(File, FS) */ - public static FileKey lenient(final File directory) { - final File gitdir = resolve(directory); - return new FileKey(gitdir != null ? gitdir : directory); + public static FileKey lenient(final File directory, FS fs) { + final File gitdir = resolve(directory, fs); + return new FileKey(gitdir != null ? gitdir : directory, fs); } private final File path; + private final FS fs; /** * @param directory * exact location of the repository. + * @param fs + * the file system abstraction which will be necessary to + * perform certain file system operations. */ - protected FileKey(final File directory) { + protected FileKey(final File directory, FS fs) { path = canonical(directory); + this.fs = fs; } private static File canonical(final File path) { @@ -300,7 +311,7 @@ public class RepositoryCache { } public Repository open(final boolean mustExist) throws IOException { - if (mustExist && !isGitRepository(path)) + if (mustExist && !isGitRepository(path, fs)) throw new RepositoryNotFoundException(path); return new Repository(path); } @@ -328,13 +339,16 @@ public class RepositoryCache { * * @param dir * the location of the directory to examine. + * @param fs + * the file system abstraction which will be necessary to + * perform certain file system operations. * @return true if the directory "looks like" a Git repository; false if * it doesn't look enough like a Git directory to really be a * Git directory. */ - public static boolean isGitRepository(final File dir) { - return FS.resolve(dir, "objects").exists() - && FS.resolve(dir, "refs").exists() + public static boolean isGitRepository(final File dir, FS fs) { + return fs.resolve(dir, "objects").exists() + && fs.resolve(dir, "refs").exists() && isValidHead(new File(dir, Constants.HEAD)); } @@ -371,18 +385,21 @@ public class RepositoryCache { * * @param directory * location to guess from. Several permutations are tried. + * @param fs + * the file system abstraction which will be necessary to + * perform certain file system operations. * @return the actual directory location if a better match is found; * null if there is no suitable match. */ - public static File resolve(final File directory) { - if (isGitRepository(directory)) + public static File resolve(final File directory, FS fs) { + if (isGitRepository(directory, fs)) return directory; - if (isGitRepository(new File(directory, Constants.DOT_GIT))) + if (isGitRepository(new File(directory, Constants.DOT_GIT), fs)) return new File(directory, Constants.DOT_GIT); final String name = directory.getName(); final File parent = directory.getParentFile(); - if (isGitRepository(new File(parent, name + Constants.DOT_GIT_EXT))) + if (isGitRepository(new File(parent, name + Constants.DOT_GIT_EXT), fs)) return new File(parent, name + Constants.DOT_GIT_EXT); return null; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java index 39c7ae8f01..aa2e2521c2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java @@ -63,6 +63,7 @@ import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryCache; import org.eclipse.jgit.lib.RepositoryCache.FileKey; +import org.eclipse.jgit.util.FS; /** Basic daemon for the anonymous <code>git://</code> transport protocol. */ public class Daemon { @@ -368,7 +369,7 @@ public class Daemon { } for (final File baseDir : exportBase) { - final File gitdir = FileKey.resolve(new File(baseDir, name)); + final File gitdir = FileKey.resolve(new File(baseDir, name), FS.DETECTED); if (gitdir != null && canExport(gitdir)) return openRepository(gitdir); } @@ -377,7 +378,7 @@ public class Daemon { private static Repository openRepository(final File gitdir) { try { - return RepositoryCache.open(FileKey.exact(gitdir)); + return RepositoryCache.open(FileKey.exact(gitdir, FS.DETECTED)); } catch (IOException err) { // null signals it "wasn't found", which is all that is suitable // for the remote client to know. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java index e7a307f809..20f3174b2e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/OpenSshConfig.java @@ -82,10 +82,13 @@ public class OpenSshConfig { * requests are cached and are automatically updated if the user modifies * the configuration file since the last time it was cached. * + * @param fs + * the file system abstraction which will be necessary to + * perform certain file system operations. * @return a caching reader of the user's configuration file. */ - public static OpenSshConfig get() { - File home = FS.userHome(); + public static OpenSshConfig get(FS fs) { + File home = fs.userHome(); if (home == null) home = new File(".").getAbsoluteFile(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigSessionFactory.java index c30d32d9f9..daa6f4ca2f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigSessionFactory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigSessionFactory.java @@ -83,15 +83,18 @@ public abstract class SshConfigSessionFactory extends SshSessionFactory { @Override public synchronized Session getSession(String user, String pass, - String host, int port) throws JSchException { - final OpenSshConfig.Host hc = getConfig().lookup(host); + String host, int port, FS fs) throws JSchException { + if (config == null) + config = OpenSshConfig.get(fs); + + final OpenSshConfig.Host hc = config.lookup(host); host = hc.getHostName(); if (port <= 0) port = hc.getPort(); if (user == null) user = hc.getUser(); - final Session session = createSession(hc, user, host, port); + final Session session = createSession(hc, user, host, port, fs); if (pass != null) session.setPassword(pass); final String strictHostKeyCheckingPolicy = hc @@ -117,14 +120,17 @@ public abstract class SshConfigSessionFactory extends SshSessionFactory { * server name to connect to. * @param port * port number of the SSH daemon (typically 22). + * @param fs + * the file system abstraction which will be necessary to + * perform certain file system operations. * @return new session instance, but otherwise unconfigured. * @throws JSchException * the session could not be created. */ protected Session createSession(final OpenSshConfig.Host hc, - final String user, final String host, final int port) + final String user, final String host, final int port, FS fs) throws JSchException { - return getJSch(hc).getSession(user, host, port); + return getJSch(hc, fs).getSession(user, host, port); } /** @@ -143,58 +149,54 @@ public abstract class SshConfigSessionFactory extends SshSessionFactory { * * @param hc * host configuration + * @param fs + * the file system abstraction which will be necessary to + * perform certain file system operations. * @return the JSch instance to use. * @throws JSchException * the user configuration could not be created. */ - protected JSch getJSch(final OpenSshConfig.Host hc) throws JSchException { - final JSch def = getDefaultJSch(); + protected JSch getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException { + if (defaultJSch == null) { + defaultJSch = createDefaultJSch(fs); + for (Object name : defaultJSch.getIdentityNames()) { + byIdentityFile.put((String) name, defaultJSch); + } + } + final File identityFile = hc.getIdentityFile(); if (identityFile == null) { - return def; + return defaultJSch; } final String identityKey = identityFile.getAbsolutePath(); JSch jsch = byIdentityFile.get(identityKey); if (jsch == null) { jsch = new JSch(); - jsch.setHostKeyRepository(def.getHostKeyRepository()); + jsch.setHostKeyRepository(defaultJSch.getHostKeyRepository()); jsch.addIdentity(identityKey); byIdentityFile.put(identityKey, jsch); } return jsch; } - private JSch getDefaultJSch() throws JSchException { - if (defaultJSch == null) { - defaultJSch = createDefaultJSch(); - for (Object name : defaultJSch.getIdentityNames()) { - byIdentityFile.put((String) name, defaultJSch); - } - } - return defaultJSch; - } - /** + * @param fs + * the file system abstraction which will be necessary to + * perform certain file system operations. * @return the new default JSch implementation. * @throws JSchException * known host keys cannot be loaded. */ - protected JSch createDefaultJSch() throws JSchException { + protected JSch createDefaultJSch(FS fs) throws JSchException { final JSch jsch = new JSch(); - knownHosts(jsch); - identities(jsch); + knownHosts(jsch, fs); + identities(jsch, fs); return jsch; } - private OpenSshConfig getConfig() { - if (config == null) - config = OpenSshConfig.get(); - return config; - } - - private static void knownHosts(final JSch sch) throws JSchException { - final File home = FS.userHome(); + private static void knownHosts(final JSch sch, FS fs) throws JSchException { + final File home = fs.userHome(); if (home == null) return; final File known_hosts = new File(new File(home, ".ssh"), "known_hosts"); @@ -212,8 +214,8 @@ public abstract class SshConfigSessionFactory extends SshSessionFactory { } } - private static void identities(final JSch sch) { - final File home = FS.userHome(); + private static void identities(final JSch sch, FS fs) { + final File home = fs.userHome(); if (home == null) return; final File sshdir = new File(home, ".ssh"); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java index 810b04ce40..d10010fcf6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java @@ -44,6 +44,8 @@ package org.eclipse.jgit.transport; +import org.eclipse.jgit.util.FS; + import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; @@ -109,19 +111,22 @@ public abstract class SshSessionFactory { * @param port * port number the server is listening for connections on. May be <= * 0 to indicate the IANA registered port of 22 should be used. + * @param fs + * the file system abstraction which will be necessary to + * perform certain file system operations. * @return a session that can contact the remote host. * @throws JSchException * the session could not be created. */ public abstract Session getSession(String user, String pass, String host, - int port) throws JSchException; + int port, FS fs) throws JSchException; /** * Close (or recycle) a session to a host. * * @param session * a session previously obtained from this factory's - * {@link #getSession(String,String, String, int)} method.s + * {@link #getSession(String,String, String, int, FS)} method.s */ public void releaseSession(final Session session) { if (session.isConnected()) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshTransport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshTransport.java index d25a7b6180..f642ac1ea8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshTransport.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshTransport.java @@ -128,7 +128,7 @@ public abstract class SshTransport extends TcpTransport { final String host = uri.getHost(); final int port = uri.getPort(); try { - sock = sch.getSession(user, pass, host, port); + sock = sch.getSession(user, pass, host, port, local.getFS()); if (!sock.isConnected()) sock.connect(tms); } catch (JSchException je) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java index c0b2eedc00..e1988a6c85 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java @@ -66,6 +66,7 @@ import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.TransferConfig; +import org.eclipse.jgit.util.FS; /** * Connects two Git repositories together and copies objects between them. @@ -319,6 +320,40 @@ public abstract class Transport { } /** + * Determines whether the transport can handle the given URIish. + * + * @param remote + * location of the remote repository. + * @param fs + * type of filesystem the local repository is stored on. + * @return true if the protocol is supported. + */ + public static boolean canHandleProtocol(final URIish remote, final FS fs) { + if (TransportGitSsh.canHandle(remote)) + return true; + + else if (TransportHttp.canHandle(remote)) + return true; + + else if (TransportSftp.canHandle(remote)) + return true; + + else if (TransportGitAnon.canHandle(remote)) + return true; + + else if (TransportAmazonS3.canHandle(remote)) + return true; + + else if (TransportBundleFile.canHandle(remote, fs)) + return true; + + else if (TransportLocal.canHandle(remote, fs)) + return true; + + return false; + } + + /** * Open a new transport instance to connect two repositories. * * @param local @@ -346,10 +381,10 @@ public abstract class Transport { else if (TransportAmazonS3.canHandle(remote)) return new TransportAmazonS3(local, remote); - else if (TransportBundleFile.canHandle(remote)) + else if (TransportBundleFile.canHandle(remote, local.getFS())) return new TransportBundleFile(local, remote); - else if (TransportLocal.canHandle(remote)) + else if (TransportLocal.canHandle(remote, local.getFS())) return new TransportLocal(local, remote); throw new NotSupportedException(MessageFormat.format(JGitText.get().URINotSupported, remote)); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java index bcf6e873fe..56a5c9796d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportAmazonS3.java @@ -69,7 +69,6 @@ import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.SymbolicRef; import org.eclipse.jgit.lib.Ref.Storage; -import org.eclipse.jgit.util.FS; /** * Transport over the non-Git aware Amazon S3 protocol. @@ -130,7 +129,7 @@ public class TransportAmazonS3 extends HttpTransport implements WalkTransport { Properties props = null; File propsFile = new File(local.getDirectory(), uri.getUser()); if (!propsFile.isFile()) - propsFile = new File(FS.userHome(), uri.getUser()); + propsFile = new File(local.getFS().userHome(), uri.getUser()); if (propsFile.isFile()) { try { props = AmazonS3.properties(propsFile); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundleFile.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundleFile.java index 0245818fe3..c47833f21c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundleFile.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportBundleFile.java @@ -58,13 +58,13 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.util.FS; class TransportBundleFile extends Transport implements TransportBundle { - static boolean canHandle(final URIish uri) { + static boolean canHandle(final URIish uri, FS fs) { if (uri.getHost() != null || uri.getPort() > 0 || uri.getUser() != null || uri.getPass() != null || uri.getPath() == null) return false; if ("file".equals(uri.getScheme()) || uri.getScheme() == null) { - final File f = FS.resolve(new File("."), uri.getPath()); + final File f = fs.resolve(new File("."), uri.getPath()); return f.isFile() || f.getName().endsWith(".bundle"); } @@ -75,7 +75,7 @@ class TransportBundleFile extends Transport implements TransportBundle { TransportBundleFile(final Repository local, final URIish uri) { super(local, uri); - bundle = FS.resolve(new File("."), uri.getPath()).getAbsoluteFile(); + bundle = local.getFS().resolve(new File("."), uri.getPath()).getAbsoluteFile(); } @Override diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java index cf4dbd5392..08fd8901d8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportLocal.java @@ -91,13 +91,13 @@ import org.eclipse.jgit.util.io.StreamCopyThread; class TransportLocal extends Transport implements PackTransport { private static final String PWD = "."; - static boolean canHandle(final URIish uri) { + static boolean canHandle(final URIish uri, FS fs) { if (uri.getHost() != null || uri.getPort() > 0 || uri.getUser() != null || uri.getPass() != null || uri.getPath() == null) return false; if ("file".equals(uri.getScheme()) || uri.getScheme() == null) - return FS.resolve(new File(PWD), uri.getPath()).isDirectory(); + return fs.resolve(new File(PWD), uri.getPath()).isDirectory(); return false; } @@ -106,7 +106,7 @@ class TransportLocal extends Transport implements PackTransport { TransportLocal(final Repository local, final URIish uri) { super(local, uri); - File d = FS.resolve(new File(PWD), uri.getPath()).getAbsoluteFile(); + File d = local.getFS().resolve(new File(PWD), uri.getPath()).getAbsoluteFile(); if (new File(d, Constants.DOT_GIT).isDirectory()) d = new File(d, Constants.DOT_GIT); remoteGitDir = d; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java index 19db39a8ff..8dfab8aa57 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java @@ -65,6 +65,7 @@ import org.eclipse.jgit.util.FS; */ public class FileTreeIterator extends WorkingTreeIterator { private final File directory; + private final FS fs; /** * Create a new iterator to traverse the given directory and its children. @@ -72,9 +73,13 @@ public class FileTreeIterator extends WorkingTreeIterator { * @param root * the starting directory. This directory should correspond to * the root of the repository. + * @param fs + * the file system abstraction which will be necessary to + * perform certain file system operations. */ - public FileTreeIterator(final File root) { + public FileTreeIterator(final File root, FS fs) { directory = root; + this.fs = fs; init(entries()); } @@ -83,20 +88,24 @@ public class FileTreeIterator extends WorkingTreeIterator { * * @param p * the parent iterator we were created from. + * @param fs + * the file system abstraction which will be necessary to + * perform certain file system operations. * @param root * the subdirectory. This should be a directory contained within * the parent directory. */ - protected FileTreeIterator(final FileTreeIterator p, final File root) { + protected FileTreeIterator(final FileTreeIterator p, final File root, FS fs) { super(p); directory = root; + this.fs = fs; init(entries()); } @Override public AbstractTreeIterator createSubtreeIterator(final Repository repo) throws IncorrectObjectTypeException, IOException { - return new FileTreeIterator(this, ((FileEntry) current()).file); + return new FileTreeIterator(this, ((FileEntry) current()).file, fs); } private Entry[] entries() { @@ -105,7 +114,7 @@ public class FileTreeIterator extends WorkingTreeIterator { return EOF; final Entry[] r = new Entry[all.length]; for (int i = 0; i < r.length; i++) - r[i] = new FileEntry(all[i]); + r[i] = new FileEntry(all[i], fs); return r; } @@ -121,7 +130,7 @@ public class FileTreeIterator extends WorkingTreeIterator { private long lastModified; - FileEntry(final File f) { + FileEntry(final File f, FS fs) { file = f; if (f.isDirectory()) { @@ -129,7 +138,7 @@ public class FileTreeIterator extends WorkingTreeIterator { mode = FileMode.GITLINK; else mode = FileMode.TREE; - } else if (FS.INSTANCE.canExecute(file)) + } else if (fs.canExecute(file)) mode = FileMode.EXECUTABLE_FILE; else mode = FileMode.REGULAR_FILE; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java new file mode 100644 index 0000000000..6c146f79f0 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/ChangeIdUtil.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2010, Robin Rosenberg + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.util; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.regex.Pattern; + +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectWriter; +import org.eclipse.jgit.lib.PersonIdent; + +/** + * Utilities for creating and working with Change-Id's, like the one used by + * Gerrit Code Review. + * <p> + * A Change-Id is a SHA-1 computed from the content of a commit, in a similar + * fashion to how the commit id is computed. Unlike the commit id a Change-Id is + * retained in the commit and subsequent revised commits in the footer of the + * commit text. + */ +public class ChangeIdUtil { + + // package-private so the unit test can test this part only + static String clean(String msg) { + return msg.// + replaceAll("(?i)(?m)^Signed-off-by:.*$\n?", "").// + replaceAll("(?m)^#.*$\n?", "").// + replaceAll("(?m)\n\n\n+", "\\\n").// + replaceAll("\\n*$", "").// + replaceAll("(?s)\ndiff --git.*", "").// + trim(); + } + + /** + * Compute a Change-Id. + * + * @param treeId + * The id of the tree that would be committed + * @param firstParentId + * parent id of previous commit or null + * @param author + * the {@link PersonIdent} for the presumed author and time + * @param committer + * the {@link PersonIdent} for the presumed committer and time + * @param message + * The commit message + * @return the change id SHA1 string (without the 'I') or null if the + * message is not complete enough + * @throws IOException + */ + public static ObjectId computeChangeId(final ObjectId treeId, + final ObjectId firstParentId, final PersonIdent author, + final PersonIdent committer, final String message) + throws IOException { + String cleanMessage = clean(message); + if (cleanMessage.length() == 0) + return null; + StringBuilder b = new StringBuilder(); + b.append("tree "); + b.append(ObjectId.toString(treeId)); + b.append("\n"); + if (firstParentId != null) { + b.append("parent "); + b.append(ObjectId.toString(firstParentId)); + b.append("\n"); + } + b.append("author "); + b.append(author.toExternalString()); + b.append("\n"); + b.append("committer "); + b.append(committer.toExternalString()); + b.append("\n\n"); + b.append(cleanMessage); + ObjectWriter w = new ObjectWriter(null); + byte[] bytes = b.toString().getBytes(Constants.CHARACTER_ENCODING); + ByteArrayInputStream is = new ByteArrayInputStream(bytes); + ObjectId sha1 = w.computeObjectSha1(Constants.OBJ_COMMIT, bytes.length, + is); + return sha1; + } + + private static final Pattern issuePattern = Pattern + .compile("^(Bug|Issue)[a-zA-Z0-9-]*:.*$"); + + private static final Pattern footerPattern = Pattern + .compile("(^[a-zA-Z0-9-]+:(?!//).*$)"); + + private static final Pattern includeInFooterPattern = Pattern + .compile("^[ \\[].*$"); + + /** + * Find the right place to insert a Change-Id and return it. + * <p> + * The Change-Id is inserted before the first footer line but after a Bug + * line. + * + * @param message + * @param changeId + * @return a commit message with an inserted Change-Id line + */ + public static String insertId(String message, ObjectId changeId) { + if (message.indexOf("\nChange-Id:") > 0) + return message; + + String[] lines = message.split("\n"); + int footerFirstLine = lines.length; + for (int i = lines.length - 1; i > 1; --i) { + if (footerPattern.matcher(lines[i]).matches()) { + footerFirstLine = i; + continue; + } + if (footerFirstLine != lines.length && lines[i].length() == 0) { + break; + } + if (footerFirstLine != lines.length + && includeInFooterPattern.matcher(lines[i]).matches()) { + footerFirstLine = i + 1; + continue; + } + footerFirstLine = lines.length; + break; + } + int insertAfter = footerFirstLine; + for (int i = footerFirstLine; i < lines.length; ++i) { + if (issuePattern.matcher(lines[i]).matches()) { + insertAfter = i + 1; + continue; + } + break; + } + StringBuilder ret = new StringBuilder(); + int i = 0; + for (; i < insertAfter; ++i) { + ret.append(lines[i]); + ret.append("\n"); + } + if (insertAfter == lines.length && insertAfter == footerFirstLine) + ret.append("\n"); + ret.append("Change-Id: I"); + ret.append(ObjectId.toString(changeId)); + ret.append("\n"); + for (; i < lines.length; ++i) { + ret.append(lines[i]); + ret.append("\n"); + } + return ret.toString(); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java index c4f4242f90..b8d433762e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java @@ -49,19 +49,28 @@ import java.security.PrivilegedAction; /** Abstraction to support various file system operations not in Java. */ public abstract class FS { - /** The implementation selected for this operating system and JRE. */ - public static final FS INSTANCE; + /** The auto-detected implementation selected for this operating system and JRE. */ + public static final FS DETECTED; static { if (FS_Win32.detect()) { if (FS_Win32_Cygwin.detect()) - INSTANCE = new FS_Win32_Cygwin(); + DETECTED = new FS_Win32_Cygwin(); else - INSTANCE = new FS_Win32(); + DETECTED = new FS_Win32(); } else if (FS_POSIX_Java6.detect()) - INSTANCE = new FS_POSIX_Java6(); + DETECTED = new FS_POSIX_Java6(); else - INSTANCE = new FS_POSIX_Java5(); + DETECTED = new FS_POSIX_Java5(); + } + + private final File userHome; + + /** + * Constructs a file system abstraction. + */ + protected FS() { + this.userHome = userHomeImpl(); } /** @@ -117,29 +126,7 @@ public abstract class FS { * @return the translated path. <code>new File(dir,name)</code> if this * platform does not require path name translation. */ - public static File resolve(final File dir, final String name) { - return INSTANCE.resolveImpl(dir, name); - } - - /** - * Resolve this file to its actual path name that the JRE can use. - * <p> - * This method can be relatively expensive. Computing a translation may - * require forking an external process per path name translated. Callers - * should try to minimize the number of translations necessary by caching - * the results. - * <p> - * Not all platforms and JREs require path name translation. Currently only - * Cygwin on Win32 require translation for Cygwin based paths. - * - * @param dir - * directory relative to which the path name is. - * @param name - * path name to translate. - * @return the translated path. <code>new File(dir,name)</code> if this - * platform does not require path name translation. - */ - protected File resolveImpl(final File dir, final String name) { + public File resolve(final File dir, final String name) { final File abspn = new File(name); if (abspn.isAbsolute()) return abspn; @@ -157,12 +144,8 @@ public abstract class FS { * * @return the user's home directory; null if the user does not have one. */ - public static File userHome() { - return USER_HOME.home; - } - - private static class USER_HOME { - static final File home = INSTANCE.userHomeImpl(); + public File userHome() { + return userHome; } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java index f727084860..39f2c03a06 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java @@ -72,7 +72,7 @@ class FS_Win32_Cygwin extends FS_Win32 { return false; } - protected File resolveImpl(final File dir, final String pn) { + public File resolve(final File dir, final String pn) { try { final Process p; @@ -103,7 +103,7 @@ class FS_Win32_Cygwin extends FS_Win32 { // Fall through and use the default return. // } - return super.resolveImpl(dir, pn); + return super.resolve(dir, pn); } @Override @@ -116,6 +116,6 @@ class FS_Win32_Cygwin extends FS_Win32 { }); if (home == null || home.length() == 0) return super.userHomeImpl(); - return resolveImpl(new File("."), home); + return resolve(new File("."), home); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java index 771e77058a..9d7feb08f2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java @@ -72,8 +72,8 @@ public abstract class SystemReader { return System.getProperty(key); } - public FileBasedConfig openUserConfig() { - final File home = FS.userHome(); + public FileBasedConfig openUserConfig(FS fs) { + final File home = fs.userHome(); return new FileBasedConfig(new File(home, ".gitconfig")); } @@ -136,9 +136,12 @@ public abstract class SystemReader { public abstract String getProperty(String key); /** + * @param fs + * the file system abstraction which will be necessary to + * perform certain file system operations. * @return the git configuration found in the user home */ - public abstract FileBasedConfig openUserConfig(); + public abstract FileBasedConfig openUserConfig(FS fs); /** * @return the current system time |