When select a valid branch then a GitDbBranch is returned

This commit is contained in:
Paul Campbell 2018-06-07 19:37:11 +01:00
parent 897f9e1bc4
commit a0fc7c28c5
7 changed files with 137 additions and 249 deletions

View file

@ -22,9 +22,7 @@
package net.kemitix.gitdb; package net.kemitix.gitdb;
import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryBuilder;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
@ -57,22 +55,15 @@ public interface GitDB {
* @return a GitDB instance for the local gitdb * @return a GitDB instance for the local gitdb
*/ */
static GitDBLocal openLocal(final Path dbDir) { static GitDBLocal openLocal(final Path dbDir) {
if (dbDir.toFile().isFile()) {
throw new GitDBRepoNotFoundException("Not a directory", dbDir);
}
if (!dbDir.toFile().exists()) {
throw new GitDBRepoNotFoundException("Directory not found", dbDir);
}
Repository repository = null;
try { try {
repository = Git.open(dbDir.toFile()).getRepository(); return Optional.of(Git.open(dbDir.toFile()))
} catch (Exception e) { .map(Git::getRepository)
e.printStackTrace(); .filter(Repository::isBare)
.map(GitDBLocal::new)
.orElseThrow(() -> new InvalidRepositoryException("Not a bare repo", dbDir));
} catch (IOException e) {
throw new InvalidRepositoryException("Error opening repository", dbDir, e);
} }
if (repository != null && repository.isBare()) {
return new GitDBLocal(repository);
}
throw new InvalidRepositoryException("Not a bare repo", dbDir);
} }
/** /**
@ -80,6 +71,7 @@ public interface GitDB {
* *
* @param name the branch to select * @param name the branch to select
* @return an Optional containing the branch if it exists * @return an Optional containing the branch if it exists
* @throws IOException if there is an error accessing the branch name
*/ */
Optional<GitDBBranch> branch(String name); Optional<GitDBBranch> branch(String name) throws IOException;
} }

View file

@ -21,6 +21,8 @@
package net.kemitix.gitdb; package net.kemitix.gitdb;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
/** /**
@ -28,8 +30,18 @@ import org.eclipse.jgit.lib.Ref;
* *
* @author Paul Campbell (pcampbell@kemitix.net) * @author Paul Campbell (pcampbell@kemitix.net)
*/ */
public interface GitDBBranch { @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
static GitDBBranch withRef(final Ref ref) { public class GitDBBranch {
return null;
private final Ref ref;
/**
* Create a new instance of GitDBBranch for the Ref.
*
* @param ref the Ref
* @return a GitDBBranch
*/
public static GitDBBranch withRef(final Ref ref) {
return new GitDBBranch(ref);
} }
} }

View file

@ -21,13 +21,12 @@
package net.kemitix.gitdb; package net.kemitix.gitdb;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.*; import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FS;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryNotEmptyException; import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.NotDirectoryException; import java.nio.file.NotDirectoryException;
@ -61,14 +60,7 @@ class GitDBLocal implements GitDB {
* @param repository the Git repository * @param repository the Git repository
*/ */
GitDBLocal(final Repository repository) { GitDBLocal(final Repository repository) {
this.repository = verifyIsBareRepo(repository); this.repository = repository;
}
private static Repository verifyIsBareRepo(final Repository repository) {
if (repository.isBare()) {
return repository;
}
throw new InvalidRepositoryException("Not a bare repo", repository.getDirectory().toPath());
} }
private void validateDbDir(final File dbDir) throws IOException { private void validateDbDir(final File dbDir) throws IOException {
@ -92,129 +84,65 @@ class GitDBLocal implements GitDB {
private static Repository initRepo(final File dbDir) throws IOException { private static Repository initRepo(final File dbDir) throws IOException {
dbDir.mkdirs(); dbDir.mkdirs();
final RepositoryCache.FileKey fileKey = RepositoryCache.FileKey.exact(dbDir, FS.DETECTED); final Repository repository = RepositoryCache.FileKey.exact(dbDir, FS.DETECTED).open(false);
final Repository repository = fileKey.open(false);
repository.create(true); repository.create(true);
createInitialBranchOnMaster(repository);
return repository; return repository;
} }
private static void createInitialBranchOnMaster(final Repository repository) throws IOException {
// create empty file
final ObjectId objectId = insertAnEmptyBlob(repository);
// create tree
final ObjectId treeId = insertTree(repository, objectId);
// create commit
final ObjectId commitId = insertCommit(repository, treeId);
// create branch
writeBranch(repository, commitId, "master");
}
private static void writeBranch(
final Repository repository,
final ObjectId commitId,
final String branchName
) throws IOException {
final Path branchRefPath =
repository.getDirectory().toPath().resolve("refs/heads/" + branchName).toAbsolutePath();
final byte[] commitIdBytes = commitId.name().getBytes(StandardCharsets.UTF_8);
Files.write(branchRefPath, commitIdBytes);
}
private static ObjectId insertCommit(
final Repository repository,
final ObjectId treeId
) throws IOException {
final CommitBuilder commitBuilder = new CommitBuilder();
commitBuilder.setTreeId(treeId);
commitBuilder.setMessage("Initialise GitDB v1");
final PersonIdent ident = new PersonIdent("GitDB", "pcampbell@kemitix.net");
commitBuilder.setAuthor(ident);
commitBuilder.setCommitter(ident);
commitBuilder.setParentId(ObjectId.zeroId());
return repository.getObjectDatabase().newInserter().insert(commitBuilder);
}
private static ObjectId insertTree(
final Repository repository,
final ObjectId objectId
) throws IOException {
final TreeFormatter treeFormatter = new TreeFormatter();
treeFormatter.append("isGitDB", FileMode.REGULAR_FILE, objectId);
return repository.getObjectDatabase().newInserter().insert(treeFormatter);
}
private static ObjectId insertAnEmptyBlob(final Repository repository) throws IOException {
return repository.getObjectDatabase().newInserter().insert(Constants.OBJ_BLOB, new byte[0]);
}
@Override @Override
public Optional<GitDBBranch> branch(final String name) { public Optional<GitDBBranch> branch(final String name) throws IOException {
try {
return Optional.ofNullable(repository.findRef(name)) return Optional.ofNullable(repository.findRef(name))
.map(GitDBBranch::withRef); .map(GitDBBranch::withRef);
} catch (IOException e) {
return Optional.empty();
} }
}
// @Override
// @SneakyThrows
// public <T> T get(Branch branch, Key key, Class<T> type) {
// //branch
// final RefDatabase refDatabase = repository.getRefDatabase();
// final String branchValue = branch.getValue();
// final Ref refDatabaseRef = refDatabase.getRef(branchValue);
// final ObjectId commitId = refDatabaseRef.getObjectId();
//
// final RevCommit revCommit = repository.parseCommit(commitId);
// final RevTree tree = revCommit.getTree();
// tree.copyTo(System.out);
//
// final ObjectLoader open = repository.getObjectDatabase().open(objectId, Constants.OBJ_TREE);
// final byte[] bytes = open.getBytes();
// final String s = new String(bytes);
// System.out.println("s = " + s);
// //key
// return null;
// }
// @Override
// @SneakyThrows
// public String put(Branch branch, Message message, Document<String> document, Author author) {
//// return document.getValue();
//
// final ObjectInserter objectInserter = repository.newObjectInserter();
// final ObjectReader objectReader = repository.newObjectReader();
// final RevWalk revWalk = new RevWalk(repository);
//
// //blob
// System.out.println("document = " + document.getKey());
// final ObjectId blobId = objectInserter.insert(Constants.OBJ_BLOB, document.getValue().getBytes(UTF_8));
// //tree
// final TreeFormatter treeFormatter = new TreeFormatter();
// treeFormatter.append(document.getKey().getValue(), FileMode.REGULAR_FILE, blobId);
// final ObjectId treeId = objectInserter.insert(treeFormatter);
// //commit
// final CommitBuilder commitBuilder = new CommitBuilder();
// final PersonIdent ident = new PersonIdent(author.getName(), author.getEmail());
// commitBuilder.setCommitter(ident);
// commitBuilder.setAuthor(ident);
// commitBuilder.setTreeId(treeId);
// commitBuilder.setMessage(message.getValue());
// //TODO: setParentId()
// final ObjectId commitId = objectInserter.insert(commitBuilder);
// //branch
// final RevCommit revCommit = revWalk.parseCommit(commitId);
// revCommit.getShortMessage();
// git.branchCreate()
// .setStartPoint(revCommit)
// .setName(branch.getValue())
// .setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.NOTRACK)
// .call();
//
// //READ
//
// //block
// final String readBlob = new String(objectReader.open(blobId).getBytes());
// System.out.println("readBlob = " + readBlob);
// final RevBlob revBlob = revWalk.lookupBlob(blobId);
// System.out.println("revBlob = " + revBlob);
// final String blobName = revBlob.name();
// System.out.println("blobName = " + blobName);
// //tree
// final RevTree revTree = revWalk.lookupTree(treeId);
// System.out.println("revTree = " + revTree);
// final String treeName = revTree.name();
// System.out.println("treeName = " + treeName);
// //commit
// System.out.println("revCommit= " + revCommit);
// final String commitName = revCommit.getName();
// System.out.println("commitName= " + commitName);
// //branch
// final Ref branchRef = repository.getRefDatabase().getRef(branch.getValue());
// System.out.println("branchRef = " + branchRef.getName());
//
//// final TreeWalk treeWalk = new TreeWalk(repository);
//// treeWalk.addTree(treeId);
//// treeWalk.next();
//// final String nameString = treeWalk.getNameString();
//// System.out.println("name = " + nameString);
//// final ObjectId objectId = treeWalk.getObjectId(0);
//// System.out.println("objectId = " + objectId);
//
//// final ObjectLoader openTree = repository.newObjectReader().open(treeId);
//// final int type = openTree.openStream().getType();
//// final long size = openTree.openStream().getSize();
//// final String readTree = new String(openTree.getBytes());
//
////
//// //commit
//// final CommitBuilder commitBuilder = new CommitBuilder();
//// commitBuilder.setAuthor(new PersonIdent(author.getName(), author.getEmail()));
//// commitBuilder.setCommitter(new PersonIdent(author.getName(), author.getEmail()));
//// commitBuilder.setMessage(message.getValue());
//// findParentCommit(branch)
//// .ifPresent(commitBuilder::setParentId);
//// commitBuilder.setTreeId(treeId);
//// final ObjectId commitId = repository.newObjectInserter().insert(commitBuilder);
////
//// //branch
//// repository.updateRef(branch.getValue()).setNewObjectId(commitId);
////
//// //get
//// return get(branch, document.getKey());
// return document.getValue();
// }
} }

View file

@ -1,42 +0,0 @@
/*
The MIT License (MIT)
Copyright (c) 2018 Paul Campbell
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies
or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package net.kemitix.gitdb;
import java.nio.file.Path;
/**
* Runtime exception thrown when attempting to open to location that is not a GitDB repo.
*
* @author Paul Campbell (pcampbell@kemitix.net)
*/
public class GitDBRepoNotFoundException extends RuntimeException {
/**
* Constructor.
*
* @param message why the GitDB wasn't found
* @param path the location where a GitDB repo was not found
*/
GitDBRepoNotFoundException(final String message, final Path path) {
super(String.format("GitDB repo not found: %s: %s", message, path));
}
}

View file

@ -21,6 +21,7 @@
package net.kemitix.gitdb; package net.kemitix.gitdb;
import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
/** /**
@ -36,7 +37,32 @@ public class InvalidRepositoryException extends RuntimeException {
* @param message the reason the repo is invalid * @param message the reason the repo is invalid
* @param path the location of the repo * @param path the location of the repo
*/ */
public InvalidRepositoryException(final String message, final Path path) { public InvalidRepositoryException(
super(String.format("Invalid GitDB repo: %s [%s]", message, path)); final String message,
final Path path
) {
super(message(message, path));
}
/**
* Constructor.
*
* @param message the reason the repo is invalid
* @param path the location of the repo
* @param cause the cause
*/
public InvalidRepositoryException(
final String message,
final Path path,
final IOException cause
) {
super(message(message, path), cause);
}
private static String message(
final String message,
final Path path
) {
return String.format("Invalid GitDB repo: %s [%s]", message, path);
} }
} }

View file

@ -1,45 +0,0 @@
/*
The MIT License (MIT)
Copyright (c) 2018 Paul Campbell
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies
or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package net.kemitix.gitdb;
/**
* Unchecked exception thrown when JGit throws a an unexpected exception.
*
* @author Paul Campbell (pcampbell@kemitix.net)
*/
public class UnexpectedGitDbException extends RuntimeException {
/**
* Constructs an instance of this class.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link #getMessage()} method.
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A <tt>null</tt> value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
*/
public UnexpectedGitDbException(final String message, final Throwable cause) {
super(message, cause);
}
}

View file

@ -2,7 +2,6 @@ package net.kemitix.gitdb;
import org.assertj.core.api.WithAssertions; import org.assertj.core.api.WithAssertions;
import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.InitCommand;
import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
@ -10,13 +9,10 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import java.io.IOException; import java.io.*;
import java.nio.file.*; import java.nio.file.*;
import java.util.Optional; import java.util.Optional;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class GitDBTest implements WithAssertions { class GitDBTest implements WithAssertions {
@ -115,7 +111,7 @@ class GitDBTest implements WithAssertions {
//given //given
final Path dir = fileExists(); final Path dir = fileExists();
//then //then
assertThatExceptionOfType(GitDBRepoNotFoundException.class) assertThatExceptionOfType(InvalidRepositoryException.class)
.isThrownBy(() -> GitDB.openLocal(dir)) .isThrownBy(() -> GitDB.openLocal(dir))
.withMessageContaining(dir.toString()); .withMessageContaining(dir.toString());
} }
@ -126,7 +122,7 @@ class GitDBTest implements WithAssertions {
//given //given
final Path dir = dirDoesNotExist(); final Path dir = dirDoesNotExist();
//then //then
assertThatExceptionOfType(GitDBRepoNotFoundException.class) assertThatExceptionOfType(InvalidRepositoryException.class)
.isThrownBy(() -> GitDB.openLocal(dir)) .isThrownBy(() -> GitDB.openLocal(dir))
.withMessageContaining(dir.toString()); .withMessageContaining(dir.toString());
} }
@ -170,20 +166,30 @@ class GitDBTest implements WithAssertions {
// Given a valid GitDb handle // Given a valid GitDb handle
// When select a branch that doesn't exist then an empty Optional is returned // When select a branch that doesn't exist then an empty Optional is returned
@Test @Test
void selectBranch_branchNotExist_thenEmptyOptional() throws IOException { void selectBranch_whenBranchNotExist_thenEmptyOptional() throws IOException {
//given //given
final GitDB gitDb = newGitDBRepo(); final GitDB gitDb = newGitDBRepo(dirDoesNotExist());
//when //when
final Optional<GitDBBranch> branch = gitDb.branch("unknown"); final Optional<GitDBBranch> branch = gitDb.branch("unknown");
//then //then
assertThat(branch).isEmpty(); assertThat(branch).isEmpty();
} }
private GitDB newGitDBRepo() throws IOException { private GitDB newGitDBRepo(final Path dbDir) throws IOException {
return GitDB.initLocal(dirDoesNotExist()); return GitDB.initLocal(dbDir);
} }
// When select a valid branch then a GitDbBranch is returned // When select a valid branch then a GitDbBranch is returned
@Test
void selectBranch_branchExists_thenReturnBranch() throws IOException {
//given
final Path dbDir = dirDoesNotExist();
final GitDB gitDb = newGitDBRepo(dbDir);
//when
final Optional<GitDBBranch> branch = gitDb.branch("master");
//then
assertThat(branch).as("Branch master exists").isNotEmpty();
}
// Given a valid GitDbBranch handle // Given a valid GitDbBranch handle
// When getting a key that does not exist then return an empty Optional // When getting a key that does not exist then return an empty Optional
@ -242,4 +248,15 @@ class GitDBTest implements WithAssertions {
// // .map(Path::toFile) // // .map(Path::toFile)
// // .forEach(File::delete); // // .forEach(File::delete);
// } // }
private static void tree(final Path dbDir, final PrintStream out) throws IOException {
final Process treeProcess = new ProcessBuilder("tree", dbDir.toString()).start();
try (final BufferedReader reader = new BufferedReader(new InputStreamReader(treeProcess.getInputStream()))) {
String line;
while (null != (line = reader.readLine())) {
out.println("line = " + line);
}
}
}
} }