Browse Source

Recognize Git repository environment variables

This makes the jgit command line behave like the C Git implementation
in the respect.

These variables are not recognized in the core, though we add support
to do the overrides there. Hence other users of the JGit library, like
the Eclipse plugin and others, will not be affected.

GIT_DIR
	The location of the ".git" directory.

GIT_WORK_TREE
	The location of the work tree.

GIT_INDEX_FILE
	The location of the index file.

GIT_CEILING_DIRECTORIES
	A colon (semicolon on Windows) separated list of paths that
	which JGit will not cross when looking for the .git directory.

GIT_OBJECT_DIRECTORY
	The location of the objects directory under which objects are
	stored.

GIT_ALTERNATE_OBJECT_DIRECTORIES
	A colon (semicolon on Windows) separated list of object directories
	to search for objects.

In addition to these we support the core.worktree config setting when
the git directory is set deliberately instead of being found.

Change-Id: I2b9bceb13c0f66b25e9e3cefd2e01534a286e04c
Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
tags/v0.7.0
Robin Rosenberg 14 years ago
parent
commit
eb63bfc1b8

+ 22
- 0
org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/LocalDiskRepositoryTestCase.java View File

@@ -51,6 +51,7 @@ import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -58,6 +59,7 @@ import java.util.concurrent.TimeUnit;

import junit.framework.TestCase;

import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileBasedConfig;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
@@ -124,6 +126,7 @@ public abstract class LocalDiskRepositoryTestCase extends TestCase {
mockSystemReader = new MockSystemReader();
mockSystemReader.userGitConfig = new FileBasedConfig(new File(trash,
"usergitconfig"));
ceilTestDirectories(getCeilings());
SystemReader.setInstance(mockSystemReader);

final long now = mockSystemReader.getCurrentTime();
@@ -142,6 +145,25 @@ public abstract class LocalDiskRepositoryTestCase extends TestCase {
WindowCache.reconfigure(c);
}


protected List<File> getCeilings() {
return Collections.singletonList(trash.getParentFile().getAbsoluteFile());
}

private void ceilTestDirectories(List<File> ceilings) {
mockSystemReader.setProperty(Constants.GIT_CEILING_DIRECTORIES_KEY, makePath(ceilings));
}

private String makePath(List<?> objects) {
final StringBuilder stringBuilder = new StringBuilder();
for (Object object : objects) {
if (stringBuilder.length() > 0)
stringBuilder.append(File.pathSeparatorChar);
stringBuilder.append(object.toString());
}
return stringBuilder.toString();
}

@Override
protected void tearDown() throws Exception {
RepositoryCache.clear();

+ 53
- 2
org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java View File

@@ -49,15 +49,20 @@ import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.jgit.awtui.AwtAuthenticator;
import org.eclipse.jgit.awtui.AwtSshSessionFactory;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.pgm.opt.CmdLineParser;
import org.eclipse.jgit.pgm.opt.SubcommandHandler;
import org.eclipse.jgit.util.CachedAuthenticator;
import org.eclipse.jgit.util.SystemReader;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.ExampleMode;
@@ -158,13 +163,50 @@ public class Main {

final TextBuiltin cmd = subcommand;
if (cmd.requiresRepository()) {
if (gitdir == null) {
String gitDirEnv = SystemReader.getInstance().getenv(Constants.GIT_DIR_KEY);
if (gitDirEnv != null)
gitdir = new File(gitDirEnv);
}
if (gitdir == null)
gitdir = findGitDir();

File gitworktree;
String gitWorkTreeEnv = SystemReader.getInstance().getenv(Constants.GIT_WORK_TREE_KEY);
if (gitWorkTreeEnv != null)
gitworktree = new File(gitWorkTreeEnv);
else
gitworktree = null;

File indexfile;
String indexFileEnv = SystemReader.getInstance().getenv(Constants.GIT_INDEX_KEY);
if (indexFileEnv != null)
indexfile = new File(indexFileEnv);
else
indexfile = null;

File objectdir;
String objectDirEnv = SystemReader.getInstance().getenv(Constants.GIT_OBJECT_DIRECTORY_KEY);
if (objectDirEnv != null)
objectdir = new File(objectDirEnv);
else
objectdir = null;

File[] altobjectdirs;
String altObjectDirEnv = SystemReader.getInstance().getenv(Constants.GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY);
if (altObjectDirEnv != null) {
String[] parserdAltObjectDirEnv = altObjectDirEnv.split(File.pathSeparator);
altobjectdirs = new File[parserdAltObjectDirEnv.length];
for (int i = 0; i < parserdAltObjectDirEnv.length; i++)
altobjectdirs[i] = new File(parserdAltObjectDirEnv[i]);
} else
altobjectdirs = null;

if (gitdir == null || !gitdir.isDirectory()) {
System.err.println("error: can't find git directory");
System.exit(1);
}
cmd.init(new Repository(gitdir), gitdir);
cmd.init(new Repository(gitdir, gitworktree, objectdir, altobjectdirs, indexfile), gitdir);
} else {
cmd.init(null, gitdir);
}
@@ -177,12 +219,21 @@ public class Main {
}

private static File findGitDir() {
File current = new File(".").getAbsoluteFile();
Set<String> ceilingDirectories = new HashSet<String>();
String ceilingDirectoriesVar = SystemReader.getInstance().getenv(
Constants.GIT_CEILING_DIRECTORIES_KEY);
if (ceilingDirectoriesVar != null) {
ceilingDirectories.addAll(Arrays.asList(ceilingDirectoriesVar
.split(File.pathSeparator)));
}
File current = new File("").getAbsoluteFile();
while (current != null) {
final File gitDir = new File(current, ".git");
if (gitDir.isDirectory())
return gitDir;
current = current.getParentFile();
if (ceilingDirectories.contains(current.getPath()))
break;
}
return null;
}

+ 156
- 1
org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0003_Basic.java View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
* Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg@dewire.com>
* Copyright (C) 2007-2009, Robin Rosenberg <robin.rosenberg@dewire.com>
* Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
* and other copyright owners as documented in the project's IP log.
*
@@ -78,6 +78,161 @@ public class T0003_Basic extends SampleDataRepositoryTestCase {
assertEquals(23, HEAD.length());
}

public void test000_openRepoBadArgs() throws IOException {
try {
new Repository(null, null);
fail("Must pass either GIT_DIR or GIT_WORK_TREE");
} catch (IllegalArgumentException e) {
assertEquals(
"Either GIT_DIR or GIT_WORK_TREE must be passed to Repository constructor",
e.getMessage());
}
}

/**
* Check the default rules for looking up directories and files within a
* repo when the gitDir is given.
*
* @throws IOException
*/
public void test000_openrepo_default_gitDirSet() throws IOException {
File repo1Parent = new File(trash.getParentFile(), "r1");
Repository repo1initial = new Repository(new File(repo1Parent, ".git"));
repo1initial.create();
repo1initial.close();

File theDir = new File(repo1Parent, ".git");
Repository r = new Repository(theDir, null);
assertEqualsPath(theDir, r.getDirectory());
assertEqualsPath(repo1Parent, r.getWorkDir());
assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
}

/**
* Check that we can pass both a git directory and a work tree
* repo when the gitDir is given.
*
* @throws IOException
*/
public void test000_openrepo_default_gitDirAndWorkTreeSet() throws IOException {
File repo1Parent = new File(trash.getParentFile(), "r1");
Repository repo1initial = new Repository(new File(repo1Parent, ".git"));
repo1initial.create();
repo1initial.close();

File theDir = new File(repo1Parent, ".git");
Repository r = new Repository(theDir, repo1Parent.getParentFile());
assertEqualsPath(theDir, r.getDirectory());
assertEqualsPath(repo1Parent.getParentFile(), r.getWorkDir());
assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
}

/**
* Check the default rules for looking up directories and files within a
* repo when the workTree is given.
*
* @throws IOException
*/
public void test000_openrepo_default_workDirSet() throws IOException {
File repo1Parent = new File(trash.getParentFile(), "r1");
Repository repo1initial = new Repository(new File(repo1Parent, ".git"));
repo1initial.create();
repo1initial.close();

File theDir = new File(repo1Parent, ".git");
Repository r = new Repository(null, repo1Parent);
assertEqualsPath(theDir, r.getDirectory());
assertEqualsPath(repo1Parent, r.getWorkDir());
assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
}

/**
* Check that worktree config has an effect, given absolute path.
*
* @throws IOException
*/
public void test000_openrepo_default_absolute_workdirconfig()
throws IOException {
File repo1Parent = new File(trash.getParentFile(), "r1");
File workdir = new File(trash.getParentFile(), "rw");
workdir.mkdir();
Repository repo1initial = new Repository(new File(repo1Parent, ".git"));
repo1initial.create();
repo1initial.getConfig().setString("core", null, "worktree",
workdir.getAbsolutePath());
repo1initial.getConfig().save();
repo1initial.close();

File theDir = new File(repo1Parent, ".git");
Repository r = new Repository(theDir, null);
assertEqualsPath(theDir, r.getDirectory());
assertEqualsPath(workdir, r.getWorkDir());
assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
}

/**
* Check that worktree config has an effect, given a relative path.
*
* @throws IOException
*/
public void test000_openrepo_default_relative_workdirconfig()
throws IOException {
File repo1Parent = new File(trash.getParentFile(), "r1");
File workdir = new File(trash.getParentFile(), "rw");
workdir.mkdir();
Repository repo1initial = new Repository(new File(repo1Parent, ".git"));
repo1initial.create();
repo1initial.getConfig()
.setString("core", null, "worktree", "../../rw");
repo1initial.getConfig().save();
repo1initial.close();

File theDir = new File(repo1Parent, ".git");
Repository r = new Repository(theDir, null);
assertEqualsPath(theDir, r.getDirectory());
assertEqualsPath(workdir, r.getWorkDir());
assertEqualsPath(new File(theDir, "index"), r.getIndexFile());
assertEqualsPath(new File(theDir, "objects"), r.getObjectsDirectory());
}

/**
* Check that the given index file is honored and the alternate object
* directories too
*
* @throws IOException
*/
public void test000_openrepo_alternate_index_file_and_objdirs()
throws IOException {
File repo1Parent = new File(trash.getParentFile(), "r1");
File indexFile = new File(trash, "idx");
File objDir = new File(trash, "../obj");
File[] altObjDirs = new File[] { db.getObjectsDirectory() };
Repository repo1initial = new Repository(new File(repo1Parent, ".git"));
repo1initial.create();
repo1initial.close();

File theDir = new File(repo1Parent, ".git");
Repository r = new Repository(theDir, null, objDir, altObjDirs,
indexFile);
assertEqualsPath(theDir, r.getDirectory());
assertEqualsPath(theDir.getParentFile(), r.getWorkDir());
assertEqualsPath(indexFile, r.getIndexFile());
assertEqualsPath(objDir, r.getObjectsDirectory());
assertNotNull(r.mapCommit("6db9c2ebf75590eef973081736730a9ea169a0c4"));
// Must close or the default repo pack files created by this test gets
// locked via the alternate object directories on Windows.
r.close();
}

protected void assertEqualsPath(File expected, File actual)
throws IOException {
assertEquals(expected.getCanonicalPath(), actual.getCanonicalPath());
}

public void test002_WriteEmptyTree() throws IOException {
// One of our test packs contains the empty tree object. If the pack is
// open when we create it we won't write the object file out as a loose

+ 34
- 0
org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java View File

@@ -262,6 +262,40 @@ public final class Constants {
/** The environment variable that contains the commiter's email */
public static final String GIT_COMMITTER_EMAIL_KEY = "GIT_COMMITTER_EMAIL";

/**
* The environment variable that limits how close to the root of the file
* systems JGit will traverse when looking for a repository root.
*/
public static final String GIT_CEILING_DIRECTORIES_KEY = "GIT_CEILING_DIRECTORIES";

/**
* The environment variable that tells us which directory is the ".git"
* directory
*/
public static final String GIT_DIR_KEY = "GIT_DIR";

/**
* The environment variable that tells us which directory is the working
* directory.
*/
public static final String GIT_WORK_TREE_KEY = "GIT_WORK_TREE";

/**
* The environment variable that tells us which file holds the Git index.
*/
public static final String GIT_INDEX_KEY = "GIT_INDEX";

/**
* The environment variable that tells us where objects are stored
*/
public static final String GIT_OBJECT_DIRECTORY_KEY = "GIT_OBJECT_DIRECTORY";

/**
* The environment variable that tells us where to look for objects, besides
* the default objects directory.
*/
public static final String GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY = "GIT_ALTERNATE_OBJECT_DIRECTORIES";

/** Default value for the user name if no other information is available */
public static final String UNKNOWN_USER_DEFAULT = "unknown-user";


+ 1
- 1
org.eclipse.jgit/src/org/eclipse/jgit/lib/GitIndex.java View File

@@ -137,7 +137,7 @@ public class GitIndex {
*/
public GitIndex(Repository db) {
this.db = db;
this.cacheFile = new File(db.getDirectory(), "index");
this.cacheFile = db.getIndexFile();
}

/**

+ 24
- 9
org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectDirectory.java View File

@@ -84,14 +84,19 @@ public class ObjectDirectory extends ObjectDatabase {

private final AtomicReference<PackList> packList;

private final File[] alternateObjectDir;

/**
* Initialize a reference to an on-disk object directory.
*
* @param dir
* the location of the <code>objects</code> directory.
* @param alternateObjectDir
* a list of alternate object directories
*/
public ObjectDirectory(final File dir) {
public ObjectDirectory(final File dir, File[] alternateObjectDir) {
objects = dir;
this.alternateObjectDir = alternateObjectDir;
infoDirectory = new File(objects, "info");
packDirectory = new File(objects, "pack");
alternatesFile = new File(infoDirectory, "alternates");
@@ -422,15 +427,21 @@ public class ObjectDirectory extends ObjectDatabase {

@Override
protected ObjectDatabase[] loadAlternates() throws IOException {
final BufferedReader br = open(alternatesFile);
final List<ObjectDatabase> l = new ArrayList<ObjectDatabase>(4);
try {
String line;
while ((line = br.readLine()) != null) {
l.add(openAlternate(line));
if (alternateObjectDir != null) {
for (File d : alternateObjectDir) {
l.add(openAlternate(d));
}
} else {
final BufferedReader br = open(alternatesFile);
try {
String line;
while ((line = br.readLine()) != null) {
l.add(openAlternate(line));
}
} finally {
br.close();
}
} finally {
br.close();
}

if (l.isEmpty()) {
@@ -447,12 +458,16 @@ public class ObjectDirectory extends ObjectDatabase {
private ObjectDatabase openAlternate(final String location)
throws IOException {
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));
return new AlternateRepositoryDatabase(db);
}
return new ObjectDirectory(objdir);
return new ObjectDirectory(objdir, null);
}

private static final class PackList {

+ 125
- 11
org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java View File

@@ -109,9 +109,17 @@ public class Repository {
private final List<RepositoryListener> listeners = new Vector<RepositoryListener>(); // thread safe
static private final List<RepositoryListener> allListeners = new Vector<RepositoryListener>(); // thread safe

private File workDir;

private File indexFile;

/**
* Construct a representation of a Git repository.
*
* The work tree, object directory, alternate object directories and index
* file locations are deduced from the given git directory and the default
* rules.
*
* @param d
* GIT_DIR (the location of the repository metadata).
* @throws IOException
@@ -119,9 +127,71 @@ public class Repository {
* accessed.
*/
public Repository(final File d) throws IOException {
gitDir = d.getAbsoluteFile();
refs = new RefDatabase(this);
objectDatabase = new ObjectDirectory(FS.resolve(gitDir, "objects"));
this(d, null, null, null, null); // go figure it out
}

/**
* Construct a representation of a Git repository.
*
* The work tree, object directory, alternate object directories and index
* file locations are deduced from the given git directory and the default
* rules.
*
* @param d
* GIT_DIR (the location of the repository metadata). May be
* null work workTree is set
* @param workTree
* GIT_WORK_TREE (the root of the checkout). May be null for
* default value.
* @throws IOException
* the repository appears to already exist but cannot be
* accessed.
*/
public Repository(final File d, final File workTree) throws IOException {
this(d, workTree, null, null, null); // go figure it out
}

/**
* 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.
* @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) throws IOException {

if (workTree != null) {
workDir = workTree;
if (d == null)
gitDir = new File(workTree, ".git");
else
gitDir = d;
} else {
if (d != null)
gitDir = d;
else
throw new IllegalArgumentException("Either GIT_DIR or GIT_WORK_TREE must be passed to Repository constructor");
}

final FileBasedConfig userConfig;
userConfig = SystemReader.getInstance().openUserConfig();
@@ -136,14 +206,41 @@ public class Repository {
}
config = new RepositoryConfig(userConfig, FS.resolve(gitDir, "config"));

if (objectDatabase.exists()) {
try {
getConfig().load();
} catch (ConfigInvalidException e1) {
IOException e2 = new IOException("Unknown repository format");
e2.initCause(e1);
throw e2;
try {
getConfig().load();
} catch (ConfigInvalidException e1) {
IOException e2 = new IOException("Unknown repository format");
e2.initCause(e1);
throw e2;
}

if (workDir == null) {
if (d != null) {
// Only read core.worktree if GIT_DIR is set explicitly. See
// git-config(1).
String workTreeConfig = getConfig().getString("core", null, "worktree");
if (workTreeConfig != null) {
workDir = FS.resolve(d, workTreeConfig);
} else {
workDir = gitDir.getParentFile();
}
}
}

refs = new RefDatabase(this);
if (objectDir != null)
objectDatabase = new ObjectDirectory(FS.resolve(objectDir, ""),
alternateObjectDir);
else
objectDatabase = new ObjectDirectory(FS.resolve(gitDir, "objects"),
alternateObjectDir);

if (indexFile != null)
this.indexFile = indexFile;
else
this.indexFile = new File(gitDir, "index");

if (objectDatabase.exists()) {
final String repositoryFormatVersion = getConfig().getString(
"core", null, "repositoryFormatVersion");
if (!"0".equals(repositoryFormatVersion)) {
@@ -959,6 +1056,13 @@ public class Repository {
return index;
}

/**
* @return the index file location
*/
public File getIndexFile() {
return indexFile;
}

static byte[] gitInternalSlash(byte[] bytes) {
if (File.separatorChar == '/')
return bytes;
@@ -1084,7 +1188,17 @@ public class Repository {
* @return the workdir file, i.e. where the files are checked out
*/
public File getWorkDir() {
return getDirectory().getParentFile();
return workDir;
}

/**
* Override default workdir
*
* @param workTree
* the work tree directory
*/
public void setWorkDir(File workTree) {
this.workDir = workTree;
}

/**

Loading…
Cancel
Save