Browse Source

Support gitdir references in working tree .git file

A '.git' file in a repository's working tree root is now parsed
as a ref to a folder located elsewhere.  This supports submodules
having their repository location outside of the parent repository's
working directory such as in the parent repository's '.git/modules'
directory.

This adds support to BaseRepositoryBuilder for repositories created
with the '--separate-git-dir' option specified to 'git init'.

Change-Id: I73c538f6d845bdbc0c4e2bce5a77f900cf36e1a9
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
stable-1.3
Kevin Sawicki 13 years ago committed by Matthias Sohn
parent
commit
2f79cf9900
  1. 103
      org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java
  2. 1
      org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
  3. 1
      org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
  4. 50
      org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java
  5. 53
      org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
  6. 2
      org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java
  7. 14
      org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java

103
org.eclipse.jgit.test/tst/org/eclipse/jgit/submodule/SubmoduleWalkTest.java

@ -44,10 +44,12 @@ package org.eclipse.jgit.submodule;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.io.File; import java.io.File;
import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCache;
@ -58,7 +60,9 @@ import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Constants; 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.Repository;
import org.eclipse.jgit.lib.RepositoryTestCase; import org.eclipse.jgit.lib.RepositoryTestCase;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.treewalk.filter.PathFilter; import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.junit.Test; import org.junit.Test;
@ -97,8 +101,6 @@ public class SubmoduleWalkTest extends RepositoryTestCase {
assertEquals(path, gen.getPath()); assertEquals(path, gen.getPath());
assertEquals(id, gen.getObjectId()); assertEquals(id, gen.getObjectId());
assertEquals(new File(db.getWorkTree(), path), gen.getDirectory()); assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
assertEquals(new File(db.getWorkTree(), path + File.separatorChar
+ Constants.DOT_GIT), gen.getGitDirectory());
assertNull(gen.getConfigUpdate()); assertNull(gen.getConfigUpdate());
assertNull(gen.getConfigUrl()); assertNull(gen.getConfigUrl());
assertNull(gen.getModulesPath()); assertNull(gen.getModulesPath());
@ -108,6 +110,101 @@ public class SubmoduleWalkTest extends RepositoryTestCase {
assertFalse(gen.next()); assertFalse(gen.next());
} }
@Test
public void repositoryWithRootLevelSubmoduleAbsoluteRef()
throws IOException, ConfigInvalidException {
final ObjectId id = ObjectId
.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
final String path = "sub";
File dotGit = new File(db.getWorkTree(), path + File.separatorChar
+ Constants.DOT_GIT);
if (!dotGit.getParentFile().exists())
dotGit.getParentFile().mkdirs();
File modulesGitDir = new File(db.getDirectory(), "modules"
+ File.separatorChar + path);
new FileWriter(dotGit).append(
"gitdir: " + modulesGitDir.getAbsolutePath()).close();
FileRepositoryBuilder builder = new FileRepositoryBuilder();
builder.setWorkTree(new File(db.getWorkTree(), path));
builder.build().create();
DirCache cache = db.lockDirCache();
DirCacheEditor editor = cache.editor();
editor.add(new PathEdit(path) {
public void apply(DirCacheEntry ent) {
ent.setFileMode(FileMode.GITLINK);
ent.setObjectId(id);
}
});
editor.commit();
SubmoduleWalk gen = SubmoduleWalk.forIndex(db);
assertTrue(gen.next());
assertEquals(path, gen.getPath());
assertEquals(id, gen.getObjectId());
assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
assertNull(gen.getConfigUpdate());
assertNull(gen.getConfigUrl());
assertNull(gen.getModulesPath());
assertNull(gen.getModulesUpdate());
assertNull(gen.getModulesUrl());
Repository subRepo = gen.getRepository();
assertNotNull(subRepo);
assertEquals(modulesGitDir, subRepo.getDirectory());
assertEquals(new File(db.getWorkTree(), path), subRepo.getWorkTree());
assertFalse(gen.next());
}
@Test
public void repositoryWithRootLevelSubmoduleRelativeRef()
throws IOException, ConfigInvalidException {
final ObjectId id = ObjectId
.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
final String path = "sub";
File dotGit = new File(db.getWorkTree(), path + File.separatorChar
+ Constants.DOT_GIT);
if (!dotGit.getParentFile().exists())
dotGit.getParentFile().mkdirs();
File modulesGitDir = new File(db.getDirectory(), "modules"
+ File.separatorChar + path);
new FileWriter(dotGit).append(
"gitdir: " + "../" + Constants.DOT_GIT + "/modules/" + path)
.close();
FileRepositoryBuilder builder = new FileRepositoryBuilder();
builder.setWorkTree(new File(db.getWorkTree(), path));
builder.build().create();
DirCache cache = db.lockDirCache();
DirCacheEditor editor = cache.editor();
editor.add(new PathEdit(path) {
public void apply(DirCacheEntry ent) {
ent.setFileMode(FileMode.GITLINK);
ent.setObjectId(id);
}
});
editor.commit();
SubmoduleWalk gen = SubmoduleWalk.forIndex(db);
assertTrue(gen.next());
assertEquals(path, gen.getPath());
assertEquals(id, gen.getObjectId());
assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
assertNull(gen.getConfigUpdate());
assertNull(gen.getConfigUrl());
assertNull(gen.getModulesPath());
assertNull(gen.getModulesUpdate());
assertNull(gen.getModulesUrl());
Repository subRepo = gen.getRepository();
assertNotNull(subRepo);
assertEquals(modulesGitDir, subRepo.getDirectory());
assertEquals(new File(db.getWorkTree(), path), subRepo.getWorkTree());
assertFalse(gen.next());
}
@Test @Test
public void repositoryWithNestedSubmodule() throws IOException, public void repositoryWithNestedSubmodule() throws IOException,
ConfigInvalidException { ConfigInvalidException {
@ -130,8 +227,6 @@ public class SubmoduleWalkTest extends RepositoryTestCase {
assertEquals(path, gen.getPath()); assertEquals(path, gen.getPath());
assertEquals(id, gen.getObjectId()); assertEquals(id, gen.getObjectId());
assertEquals(new File(db.getWorkTree(), path), gen.getDirectory()); assertEquals(new File(db.getWorkTree(), path), gen.getDirectory());
assertEquals(new File(db.getWorkTree(), path + File.separatorChar
+ Constants.DOT_GIT), gen.getGitDirectory());
assertNull(gen.getConfigUpdate()); assertNull(gen.getConfigUpdate());
assertNull(gen.getConfigUrl()); assertNull(gen.getConfigUrl());
assertNull(gen.getModulesPath()); assertNull(gen.getModulesPath());

1
org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties

@ -234,6 +234,7 @@ invalidChannel=Invalid channel {0}
invalidCharacterInBase64Data=Invalid character in Base64 data. invalidCharacterInBase64Data=Invalid character in Base64 data.
invalidCommitParentNumber=Invalid commit parent number invalidCommitParentNumber=Invalid commit parent number
invalidEncryption=Invalid encryption invalidEncryption=Invalid encryption
invalidGitdirRef = Invalid .git reference in file ''{0}''
invalidGitType=invalid git type: {0} invalidGitType=invalid git type: {0}
invalidId=Invalid id {0} invalidId=Invalid id {0}
invalidIdLength=Invalid id length {0}; should be {1} invalidIdLength=Invalid id length {0}; should be {1}

1
org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java

@ -294,6 +294,7 @@ public class JGitText extends TranslationBundle {
/***/ public String invalidCharacterInBase64Data; /***/ public String invalidCharacterInBase64Data;
/***/ public String invalidCommitParentNumber; /***/ public String invalidCommitParentNumber;
/***/ public String invalidEncryption; /***/ public String invalidEncryption;
/***/ public String invalidGitdirRef;
/***/ public String invalidGitType; /***/ public String invalidGitType;
/***/ public String invalidId; /***/ public String invalidId;
/***/ public String invalidIdLength; /***/ public String invalidIdLength;

50
org.eclipse.jgit/src/org/eclipse/jgit/lib/BaseRepositoryBuilder.java

@ -69,6 +69,8 @@ import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.storage.file.FileRepository; import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder; import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.RawParseUtils;
import org.eclipse.jgit.util.SystemReader; import org.eclipse.jgit.util.SystemReader;
/** /**
@ -85,6 +87,19 @@ import org.eclipse.jgit.util.SystemReader;
* @see FileRepositoryBuilder * @see FileRepositoryBuilder
*/ */
public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Repository> { public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Repository> {
private static boolean isSymRef(byte[] ref) {
if (ref.length < 9)
return false;
return /**/ref[0] == 'g' //
&& ref[1] == 'i' //
&& ref[2] == 't' //
&& ref[3] == 'd' //
&& ref[4] == 'i' //
&& ref[5] == 'r' //
&& ref[6] == ':' //
&& ref[7] == ' ';
}
private FS fs; private FS fs;
private File gitDir; private File gitDir;
@ -546,10 +561,37 @@ public class BaseRepositoryBuilder<B extends BaseRepositoryBuilder, R extends Re
* the repository could not be accessed * the repository could not be accessed
*/ */
protected void setupGitDir() throws IOException { protected void setupGitDir() throws IOException {
// No gitDir? Try to assume its under the workTree. // No gitDir? Try to assume its under the workTree or a ref to another
// // location
if (getGitDir() == null && getWorkTree() != null) if (getGitDir() == null && getWorkTree() != null) {
setGitDir(new File(getWorkTree(), DOT_GIT)); File dotGit = new File(getWorkTree(), DOT_GIT);
if (!dotGit.isFile())
setGitDir(dotGit);
else {
byte[] content = IO.readFully(dotGit);
if (!isSymRef(content))
throw new IOException(MessageFormat.format(
JGitText.get().invalidGitdirRef,
dotGit.getAbsolutePath()));
int pathStart = 8;
int lineEnd = RawParseUtils.nextLF(content, pathStart);
if (content[lineEnd - 1] == '\n')
lineEnd--;
if (lineEnd == pathStart)
throw new IOException(MessageFormat.format(
JGitText.get().invalidGitdirRef,
dotGit.getAbsolutePath()));
String gitdirPath = RawParseUtils.decode(content, pathStart,
lineEnd);
File gitdirFile = new File(gitdirPath);
if (gitdirFile.isAbsolute())
setGitDir(gitdirFile);
else
setGitDir(new File(getWorkTree(), gitdirPath)
.getCanonicalFile());
}
}
} }
/** /**

53
org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java

@ -155,27 +155,32 @@ public class SubmoduleWalk {
*/ */
public static Repository getSubmoduleRepository(final Repository parent, public static Repository getSubmoduleRepository(final Repository parent,
final String path) throws IOException { final String path) throws IOException {
File directory = getSubmoduleGitDirectory(parent, path); return getSubmoduleRepository(parent.getWorkTree(), path);
if (!directory.isDirectory())
return null;
try {
return new RepositoryBuilder().setMustExist(true)
.setFS(FS.DETECTED).setGitDir(directory).build();
} catch (RepositoryNotFoundException e) {
return null;
}
} }
/** /**
* Get the .git directory for a repository submodule path * Get submodule repository at path
* *
* @param parent * @param parent
* @param path * @param path
* @return .git for submodule repository * @return repository or null if repository doesn't exist
* @throws IOException
*/ */
public static File getSubmoduleGitDirectory(final Repository parent, public static Repository getSubmoduleRepository(final File parent,
final String path) { final String path) throws IOException {
return new File(getSubmoduleDirectory(parent, path), Constants.DOT_GIT); File subWorkTree = new File(parent, path);
if (!subWorkTree.isDirectory())
return null;
File workTree = new File(parent, path);
try {
return new RepositoryBuilder() //
.setMustExist(true) //
.setFS(FS.DETECTED) //
.setWorkTree(workTree) //
.build();
} catch (RepositoryNotFoundException e) {
return null;
}
} }
/** /**
@ -348,15 +353,6 @@ public class SubmoduleWalk {
return getSubmoduleDirectory(repository, path); return getSubmoduleDirectory(repository, path);
} }
/**
* Get the .git directory for the current submodule entry
*
* @return .git for submodule repository
*/
public File getGitDirectory() {
return getSubmoduleGitDirectory(repository, path);
}
/** /**
* Advance to next submodule in the index tree. * Advance to next submodule in the index tree.
* *
@ -466,20 +462,9 @@ public class SubmoduleWalk {
ConfigConstants.CONFIG_KEY_UPDATE); ConfigConstants.CONFIG_KEY_UPDATE);
} }
/**
* Does the current submodule entry have a .git directory in the parent
* repository's working tree?
*
* @return true if .git directory exists, false otherwise
*/
public boolean hasGitDirectory() {
return getGitDirectory().isDirectory();
}
/** /**
* Get repository for current submodule entry * Get repository for current submodule entry
* *
* @see #hasGitDirectory()
* @return repository or null if non-existent * @return repository or null if non-existent
* @throws IOException * @throws IOException
*/ */

2
org.eclipse.jgit/src/org/eclipse/jgit/treewalk/FileTreeIterator.java

@ -159,7 +159,7 @@ public class FileTreeIterator extends WorkingTreeIterator {
file = f; file = f;
if (f.isDirectory()) { if (f.isDirectory()) {
if (new File(f, Constants.DOT_GIT).isDirectory()) if (new File(f, Constants.DOT_GIT).exists())
mode = FileMode.GITLINK; mode = FileMode.GITLINK;
else else
mode = FileMode.TREE; mode = FileMode.TREE;

14
org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java

@ -76,7 +76,7 @@ import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
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.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryBuilder; import org.eclipse.jgit.submodule.SubmoduleWalk;
import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.io.EolCanonicalizingInputStream; import org.eclipse.jgit.util.io.EolCanonicalizingInputStream;
@ -280,18 +280,16 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
* @return non-null submodule id * @return non-null submodule id
*/ */
protected byte[] idSubmodule(File directory, Entry e) { protected byte[] idSubmodule(File directory, Entry e) {
final String gitDirPath = e.getName() + "/" + Constants.DOT_GIT;
final File submoduleGitDir = new File(directory, gitDirPath);
if (!submoduleGitDir.isDirectory())
return zeroid;
final Repository submoduleRepo; final Repository submoduleRepo;
try { try {
FS fs = repository != null ? repository.getFS() : FS.DETECTED; submoduleRepo = SubmoduleWalk.getSubmoduleRepository(directory,
submoduleRepo = new RepositoryBuilder().setGitDir(submoduleGitDir) e.getName());
.setMustExist(true).setFS(fs).build();
} catch (IOException exception) { } catch (IOException exception) {
return zeroid; return zeroid;
} }
if (submoduleRepo == null)
return zeroid;
final ObjectId head; final ObjectId head;
try { try {
head = submoduleRepo.resolve(Constants.HEAD); head = submoduleRepo.resolve(Constants.HEAD);

Loading…
Cancel
Save