Merge branch 'mon-070-result-no-exceptions' into 'master'
Use Mon's Result<T> See merge request kemitix/gitdb!1
This commit is contained in:
commit
f302cc9d39
16 changed files with 490 additions and 366 deletions
2
pom.xml
2
pom.xml
|
@ -21,7 +21,7 @@
|
||||||
<jgit.version>4.11.0.201803080745-r</jgit.version>
|
<jgit.version>4.11.0.201803080745-r</jgit.version>
|
||||||
<junit.version>5.2.0</junit.version>
|
<junit.version>5.2.0</junit.version>
|
||||||
<assertj.version>3.10.0</assertj.version>
|
<assertj.version>3.10.0</assertj.version>
|
||||||
<mon.version>0.4.0</mon.version>
|
<mon.version>0.8.0</mon.version>
|
||||||
<mockito.version>2.18.3</mockito.version>
|
<mockito.version>2.18.3</mockito.version>
|
||||||
<java-semver.version>0.9.0</java-semver.version>
|
<java-semver.version>0.9.0</java-semver.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
|
@ -22,10 +22,10 @@
|
||||||
package net.kemitix.gitdb;
|
package net.kemitix.gitdb;
|
||||||
|
|
||||||
import net.kemitix.gitdb.impl.LocalGitDB;
|
import net.kemitix.gitdb.impl.LocalGitDB;
|
||||||
|
import net.kemitix.mon.maybe.Maybe;
|
||||||
|
import net.kemitix.mon.result.Result;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main API for connecting to a Git repo as a database.
|
* Main API for connecting to a Git repo as a database.
|
||||||
|
@ -41,13 +41,12 @@ public interface GitDB {
|
||||||
* @param userName the user name
|
* @param userName the user name
|
||||||
* @param userEmailAddress the user email address
|
* @param userEmailAddress the user email address
|
||||||
* @return a GitDB instance for the created local gitdb
|
* @return a GitDB instance for the created local gitdb
|
||||||
* @throws IOException if there {@code dbDir} is a file or a non-empty directory
|
|
||||||
*/
|
*/
|
||||||
static GitDB initLocal(
|
static Result<GitDB> initLocal(
|
||||||
final Path dbDir,
|
final Path dbDir,
|
||||||
final String userName,
|
final String userName,
|
||||||
final String userEmailAddress
|
final String userEmailAddress
|
||||||
) throws IOException {
|
) {
|
||||||
return LocalGitDB.init(dbDir, userName, userEmailAddress);
|
return LocalGitDB.init(dbDir, userName, userEmailAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +58,7 @@ public interface GitDB {
|
||||||
* @param userEmailAddress the user email address
|
* @param userEmailAddress the user email address
|
||||||
* @return a GitDB instance for the local gitdb
|
* @return a GitDB instance for the local gitdb
|
||||||
*/
|
*/
|
||||||
static GitDB openLocal(final Path dbDir, final String userName, final String userEmailAddress) {
|
static Result<GitDB> openLocal(final Path dbDir, final String userName, final String userEmailAddress) {
|
||||||
return LocalGitDB.open(dbDir, userName, userEmailAddress);
|
return LocalGitDB.open(dbDir, userName, userEmailAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,9 +66,8 @@ public interface GitDB {
|
||||||
* Select the named branch.
|
* Select the named branch.
|
||||||
*
|
*
|
||||||
* @param name the branch to select
|
* @param name the branch to select
|
||||||
* @return an Optional containing the branch if it exists
|
* @return an Result Maybe containing the branch if it exists
|
||||||
* @throws IOException if there is an error accessing the branch name
|
|
||||||
*/
|
*/
|
||||||
Optional<GitDBBranch> branch(String name) throws IOException;
|
Result<Maybe<GitDBBranch>> branch(String name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,9 +22,8 @@
|
||||||
package net.kemitix.gitdb;
|
package net.kemitix.gitdb;
|
||||||
|
|
||||||
import com.github.zafarkhaja.semver.Version;
|
import com.github.zafarkhaja.semver.Version;
|
||||||
|
import net.kemitix.mon.maybe.Maybe;
|
||||||
import java.io.IOException;
|
import net.kemitix.mon.result.Result;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API for interacting with a branch in a GirDB.
|
* API for interacting with a branch in a GirDB.
|
||||||
|
@ -38,9 +37,8 @@ public interface GitDBBranch {
|
||||||
*
|
*
|
||||||
* @param key the key to lookup
|
* @param key the key to lookup
|
||||||
* @return an Optional containing the value, if it exists, or empty if not
|
* @return an Optional containing the value, if it exists, or empty if not
|
||||||
* @throws IOException if there was an error reading the value
|
|
||||||
*/
|
*/
|
||||||
Optional<String> get(String key) throws IOException;
|
Result<Maybe<String>> get(String key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Put a value into the store for the key.
|
* Put a value into the store for the key.
|
||||||
|
@ -48,18 +46,16 @@ public interface GitDBBranch {
|
||||||
* @param key the key to place the value under
|
* @param key the key to place the value under
|
||||||
* @param value the value (must be Serializable)
|
* @param value the value (must be Serializable)
|
||||||
* @return an updated branch containing the new key/value
|
* @return an updated branch containing the new key/value
|
||||||
* @throws IOException if there was an error writing the key/value
|
|
||||||
*/
|
*/
|
||||||
GitDBBranch put(String key, String value) throws IOException;
|
Result<GitDBBranch> put(String key, String value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a key and its value from the store.
|
* Removes a key and its value from the store.
|
||||||
*
|
*
|
||||||
* @param key the key to remove
|
* @param key the key to remove
|
||||||
* @return an updated branch without the key, or the original if the key was not found
|
* @return an updated branch without the key, or the original if the key was not found
|
||||||
* @throws IOException if there was an error removing the key/value
|
|
||||||
*/
|
*/
|
||||||
GitDBBranch remove(String key) throws IOException;
|
Result<GitDBBranch> remove(String key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the GitDB format for the current branch.
|
* Returns the GitDB format for the current branch.
|
||||||
|
@ -67,8 +63,7 @@ public interface GitDBBranch {
|
||||||
* <p>Different branches can have different versions.</p>
|
* <p>Different branches can have different versions.</p>
|
||||||
*
|
*
|
||||||
* @return the format as per semantic versioning, i.e. "x.y.z" within an Optional
|
* @return the format as per semantic versioning, i.e. "x.y.z" within an Optional
|
||||||
* @throws IOException error reading version
|
|
||||||
*/
|
*/
|
||||||
Optional<Version> getFormatVersion() throws IOException;
|
Result<Maybe<Version>> getFormatVersion();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,9 @@
|
||||||
|
|
||||||
package net.kemitix.gitdb.impl;
|
package net.kemitix.gitdb.impl;
|
||||||
|
|
||||||
|
import net.kemitix.mon.result.Result;
|
||||||
import org.eclipse.jgit.lib.*;
|
import org.eclipse.jgit.lib.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Commits Key/Value updates into the Git Repository.
|
* Commits Key/Value updates into the Git Repository.
|
||||||
*
|
*
|
||||||
|
@ -52,15 +51,14 @@ class CommitWriter {
|
||||||
* @param userName the user name
|
* @param userName the user name
|
||||||
* @param userEmailAddress the user email address
|
* @param userEmailAddress the user email address
|
||||||
* @return the id of the commit
|
* @return the id of the commit
|
||||||
* @throws IOException if there is an error writing the value
|
|
||||||
*/
|
*/
|
||||||
ObjectId write(
|
Result<ObjectId> write(
|
||||||
final ObjectId treeId,
|
final ObjectId treeId,
|
||||||
final ObjectId parentId,
|
final ObjectId parentId,
|
||||||
final String message,
|
final String message,
|
||||||
final String userName,
|
final String userName,
|
||||||
final String userEmailAddress
|
final String userEmailAddress
|
||||||
) throws IOException {
|
) {
|
||||||
final CommitBuilder commitBuilder = new CommitBuilder();
|
final CommitBuilder commitBuilder = new CommitBuilder();
|
||||||
commitBuilder.setTreeId(treeId);
|
commitBuilder.setTreeId(treeId);
|
||||||
commitBuilder.setMessage(message);
|
commitBuilder.setMessage(message);
|
||||||
|
@ -68,7 +66,7 @@ class CommitWriter {
|
||||||
commitBuilder.setAuthor(ident);
|
commitBuilder.setAuthor(ident);
|
||||||
commitBuilder.setCommitter(ident);
|
commitBuilder.setCommitter(ident);
|
||||||
commitBuilder.setParentId(parentId);
|
commitBuilder.setParentId(parentId);
|
||||||
return objectInserter.insert(commitBuilder);
|
return Result.of(() -> objectInserter.insert(commitBuilder));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -82,15 +80,14 @@ class CommitWriter {
|
||||||
* @param userName the user name
|
* @param userName the user name
|
||||||
* @param userEmailAddress the user email address
|
* @param userEmailAddress the user email address
|
||||||
* @return the id of the commit
|
* @return the id of the commit
|
||||||
* @throws IOException if there is an error writing the value
|
|
||||||
*/
|
*/
|
||||||
ObjectId write(
|
Result<ObjectId> write(
|
||||||
final ObjectId treeId,
|
final ObjectId treeId,
|
||||||
final Ref branchRef,
|
final Ref branchRef,
|
||||||
final String message,
|
final String message,
|
||||||
final String userName,
|
final String userName,
|
||||||
final String userEmailAddress
|
final String userEmailAddress
|
||||||
) throws IOException {
|
) {
|
||||||
return write(treeId, branchRef.getObjectId(), message, userName, userEmailAddress);
|
return write(treeId, branchRef.getObjectId(), message, userName, userEmailAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,12 +25,12 @@ import com.github.zafarkhaja.semver.Version;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import net.kemitix.gitdb.GitDBBranch;
|
import net.kemitix.gitdb.GitDBBranch;
|
||||||
|
import net.kemitix.mon.maybe.Maybe;
|
||||||
|
import net.kemitix.mon.result.Result;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,15 +48,6 @@ class GitDBBranchImpl implements GitDBBranch {
|
||||||
private final String userEmailAddress;
|
private final String userEmailAddress;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
private static GitDBBranch select(
|
|
||||||
final Ref branchRef,
|
|
||||||
final GitDBRepo gitDBRepo,
|
|
||||||
final String userName,
|
|
||||||
final String userEmailAddress
|
|
||||||
) {
|
|
||||||
return new GitDBBranchImpl(branchRef, gitDBRepo, userName, userEmailAddress, branchRef.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise the creation of new GitDBBranch instances.
|
* Initialise the creation of new GitDBBranch instances.
|
||||||
*
|
*
|
||||||
|
@ -65,7 +56,7 @@ class GitDBBranchImpl implements GitDBBranch {
|
||||||
* @param userEmailAddress the user's email address to record against changes
|
* @param userEmailAddress the user's email address to record against changes
|
||||||
* @return a Function for creating a GitDBBranch when supplied with a Ref for a branch
|
* @return a Function for creating a GitDBBranch when supplied with a Ref for a branch
|
||||||
*/
|
*/
|
||||||
static Function<Ref, GitDBBranch> init(
|
static Function<Ref, Result<GitDBBranch>> init(
|
||||||
final Repository repository,
|
final Repository repository,
|
||||||
final String userName,
|
final String userName,
|
||||||
final String userEmailAddress
|
final String userEmailAddress
|
||||||
|
@ -73,38 +64,52 @@ class GitDBBranchImpl implements GitDBBranch {
|
||||||
return ref -> select(ref, new GitDBRepo(repository), userName, userEmailAddress);
|
return ref -> select(ref, new GitDBRepo(repository), userName, userEmailAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Result<GitDBBranch> select(
|
||||||
|
final Ref branchRef,
|
||||||
|
final GitDBRepo gitDBRepo,
|
||||||
|
final String userName,
|
||||||
|
final String userEmailAddress
|
||||||
|
) {
|
||||||
|
return Result.ok(new GitDBBranchImpl(branchRef, gitDBRepo, userName, userEmailAddress, branchRef.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<String> get(final String key) throws IOException {
|
public Result<Maybe<String>> get(final String key) {
|
||||||
return gitDBRepo.readValue(branchRef, KEY_PREFIX + key);
|
return gitDBRepo.readValue(branchRef, KEY_PREFIX + key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GitDBBranch put(final String key, final String value) throws IOException {
|
public Result<GitDBBranch> put(final String key, final String value) {
|
||||||
final ObjectId newTree = gitDBRepo.writeValue(branchRef, KEY_PREFIX + key, value);
|
|
||||||
final String message = String.format("Add key [%s] = [%s]", key, value);
|
final String message = String.format("Add key [%s] = [%s]", key, value);
|
||||||
final Ref newBranch = gitDBRepo.writeCommit(branchRef, newTree, message, userName, userEmailAddress);
|
return gitDBRepo.writeValue(branchRef, KEY_PREFIX + key, value)
|
||||||
return select(newBranch, gitDBRepo, userName, userEmailAddress);
|
.flatMap(nt -> gitDBRepo.writeCommit(branchRef, nt, message, userName, userEmailAddress))
|
||||||
|
.flatMap(nb -> select(nb, gitDBRepo, userName, userEmailAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GitDBBranch remove(final String key) throws IOException {
|
public Result<GitDBBranch> remove(final String key) {
|
||||||
final Optional<ObjectId> newTree = gitDBRepo.removeKey(branchRef, KEY_PREFIX + key);
|
return gitDBRepo.removeKey(branchRef, KEY_PREFIX + key).flatMap(treeId ->
|
||||||
if (newTree.isPresent()) {
|
writeRemoveKeyCommit(key, treeId)
|
||||||
final Ref newBranch =
|
.map(selectUpdatedBranch())
|
||||||
gitDBRepo.writeCommit(
|
.orElse(Result.ok(this)));
|
||||||
branchRef, newTree.get(),
|
|
||||||
String.format("Remove Key [%s]", key),
|
|
||||||
userName,
|
|
||||||
userEmailAddress);
|
|
||||||
return select(newBranch, gitDBRepo, userName, userEmailAddress);
|
|
||||||
}
|
}
|
||||||
return this;
|
|
||||||
|
private Maybe<Result<Ref>> writeRemoveKeyCommit(final String key, final Maybe<ObjectId> idMaybe) {
|
||||||
|
return idMaybe.map(objectId -> {
|
||||||
|
final String message = String.format("Remove Key [%s]", key);
|
||||||
|
return gitDBRepo.writeCommit(branchRef, objectId, message, userName, userEmailAddress);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function<Result<Ref>, Result<GitDBBranch>> selectUpdatedBranch() {
|
||||||
|
return refResult -> refResult.flatMap(ref ->
|
||||||
|
select(ref, gitDBRepo, userName, userEmailAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Version> getFormatVersion() throws IOException {
|
public Result<Maybe<Version>> getFormatVersion() {
|
||||||
return gitDBRepo.readValue(branchRef, "GitDB.Version")
|
return gitDBRepo.readValue(branchRef, "GitDB.Version")
|
||||||
.map(Version::valueOf);
|
.map(version -> version.map(Version::valueOf));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,14 +21,14 @@
|
||||||
|
|
||||||
package net.kemitix.gitdb.impl;
|
package net.kemitix.gitdb.impl;
|
||||||
|
|
||||||
import lombok.val;
|
import net.kemitix.mon.maybe.Maybe;
|
||||||
|
import net.kemitix.mon.result.Result;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Optional;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for interacting with the GitDB Repository.
|
* Wrapper for interacting with the GitDB Repository.
|
||||||
|
@ -64,56 +64,35 @@ class GitDBRepo {
|
||||||
* @param key the key to insert
|
* @param key the key to insert
|
||||||
* @param valueId id of the value
|
* @param valueId id of the value
|
||||||
* @return the id of the inserted tree
|
* @return the id of the inserted tree
|
||||||
* @throws IOException the tree could not be stored
|
|
||||||
*/
|
*/
|
||||||
ObjectId insertNewTree(
|
Result<ObjectId> insertNewTree(
|
||||||
final String key,
|
final String key,
|
||||||
final ObjectId valueId
|
final ObjectId valueId
|
||||||
) throws IOException {
|
) {
|
||||||
return keyWriter.writeFirst(key, valueId);
|
return keyWriter.writeFirst(key, valueId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert a commit into the store, returning its unique id.
|
|
||||||
*
|
|
||||||
* @param treeId id of the tree
|
|
||||||
* @param branchRef the branch to commit to
|
|
||||||
* @param message the message
|
|
||||||
* @param userName the user name
|
|
||||||
* @param userEmailAddress the user email address
|
|
||||||
* @return the id of the commit
|
|
||||||
* @throws IOException the commit could not be stored
|
|
||||||
*/
|
|
||||||
ObjectId insertCommit(
|
|
||||||
final ObjectId treeId,
|
|
||||||
final String message,
|
|
||||||
final String userName,
|
|
||||||
final String userEmailAddress,
|
|
||||||
final Ref branchRef
|
|
||||||
) throws IOException {
|
|
||||||
return commitWriter.write(treeId, branchRef, message, userName, userEmailAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a value from the branch with the given key.
|
* Reads a value from the branch with the given key.
|
||||||
*
|
*
|
||||||
* @param branchRef the branch to select from
|
* @param branchRef the branch to select from
|
||||||
* @param key the key to get the value for
|
* @param key the key to get the value for
|
||||||
* @return an Optional containing the value if found, or empty
|
* @return an Optional containing the value if found, or empty
|
||||||
* @throws IOException if there was an error reading the value
|
|
||||||
*/
|
*/
|
||||||
Optional<String> readValue(
|
Result<Maybe<String>> readValue(
|
||||||
final Ref branchRef,
|
final Ref branchRef,
|
||||||
final String key
|
final String key
|
||||||
) throws IOException {
|
) {
|
||||||
val blob = new GitTreeReader(repository)
|
final GitTreeReader treeFilter = new GitTreeReader(repository).treeFilter(key);
|
||||||
.treeFilter(key)
|
return streamTree(branchRef, treeFilter).flatMap(s ->
|
||||||
.stream(branchRef)
|
Result.invert(s.findFirst()
|
||||||
.findFirst();
|
.map(NamedRevBlob::blobAsString)
|
||||||
if (blob.isPresent()) {
|
.map(Maybe::just)
|
||||||
return Optional.of(blob.get().blobAsString());
|
.orElseGet(Maybe::nothing)));
|
||||||
}
|
}
|
||||||
return Optional.empty();
|
|
||||||
|
private Result<Stream<NamedRevBlob>> streamTree(final Ref branchRef, final GitTreeReader treeFilter) {
|
||||||
|
return treeFilter.stream(branchRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -125,11 +104,10 @@ class GitDBRepo {
|
||||||
* @param key the key to place the value under
|
* @param key the key to place the value under
|
||||||
* @param value the value
|
* @param value the value
|
||||||
* @return the id of the updated tree containing the update
|
* @return the id of the updated tree containing the update
|
||||||
* @throws IOException if there was an error writing the value
|
|
||||||
*/
|
*/
|
||||||
ObjectId writeValue(final Ref branchRef, final String key, final String value) throws IOException {
|
Result<ObjectId> writeValue(final Ref branchRef, final String key, final String value) {
|
||||||
final ObjectId blob = valueWriter.write(value.getBytes(StandardCharsets.UTF_8));
|
return valueWriter.write(value.getBytes(StandardCharsets.UTF_8))
|
||||||
return keyWriter.write(key, blob, branchRef);
|
.flatMap(b -> keyWriter.write(key, b, branchRef));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -141,17 +119,37 @@ class GitDBRepo {
|
||||||
* @param userName the user name
|
* @param userName the user name
|
||||||
* @param userEmailAddress the user email address
|
* @param userEmailAddress the user email address
|
||||||
* @return the Ref of the updated branch
|
* @return the Ref of the updated branch
|
||||||
* @throws IOException if there was an error writing the branch
|
|
||||||
*/
|
*/
|
||||||
Ref writeCommit(
|
Result<Ref> writeCommit(
|
||||||
final Ref branchRef,
|
final Ref branchRef,
|
||||||
final ObjectId tree,
|
final ObjectId tree,
|
||||||
final String message,
|
final String message,
|
||||||
final String userName,
|
final String userName,
|
||||||
final String userEmailAddress
|
final String userEmailAddress
|
||||||
) throws IOException {
|
) {
|
||||||
final ObjectId commitId = insertCommit(tree, message, userName, userEmailAddress, branchRef);
|
return insertCommit(tree, message, userName, userEmailAddress, branchRef)
|
||||||
return headWriter.write(branchRef.getName(), commitId);
|
.flatMap(cid -> Result.of(() ->
|
||||||
|
headWriter.write(branchRef.getName(), cid)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert a commit into the store, returning its unique id.
|
||||||
|
*
|
||||||
|
* @param treeId id of the tree
|
||||||
|
* @param branchRef the branch to commit to
|
||||||
|
* @param message the message
|
||||||
|
* @param userName the user name
|
||||||
|
* @param userEmailAddress the user email address
|
||||||
|
* @return the id of the commit
|
||||||
|
*/
|
||||||
|
Result<ObjectId> insertCommit(
|
||||||
|
final ObjectId treeId,
|
||||||
|
final String message,
|
||||||
|
final String userName,
|
||||||
|
final String userEmailAddress,
|
||||||
|
final Ref branchRef
|
||||||
|
) {
|
||||||
|
return commitWriter.write(treeId, branchRef, message, userName, userEmailAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -162,14 +160,13 @@ class GitDBRepo {
|
||||||
* @param initUser the user name
|
* @param initUser the user name
|
||||||
* @param initEmail the user email address
|
* @param initEmail the user email address
|
||||||
* @return the id of the commit
|
* @return the id of the commit
|
||||||
* @throws IOException if there was an error writing the commit
|
|
||||||
*/
|
*/
|
||||||
ObjectId initialCommit(
|
Result<ObjectId> initialCommit(
|
||||||
final ObjectId treeId,
|
final ObjectId treeId,
|
||||||
final String initMessage,
|
final String initMessage,
|
||||||
final String initUser,
|
final String initUser,
|
||||||
final String initEmail
|
final String initEmail
|
||||||
) throws IOException {
|
) {
|
||||||
return commitWriter.write(treeId, ObjectId.zeroId(), initMessage, initUser, initEmail);
|
return commitWriter.write(treeId, ObjectId.zeroId(), initMessage, initUser, initEmail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,9 +179,8 @@ class GitDBRepo {
|
||||||
* @param key the key to place the value under
|
* @param key the key to place the value under
|
||||||
* @return an Optional containing the id of the updated tree containing the update, if the key was found, or an
|
* @return an Optional containing the id of the updated tree containing the update, if the key was found, or an
|
||||||
* empty Optional if there key was not found, the there was no changes made
|
* empty Optional if there key was not found, the there was no changes made
|
||||||
* @throws IOException if there was an error writing the value
|
|
||||||
*/
|
*/
|
||||||
Optional<ObjectId> removeKey(final Ref branchRef, final String key) throws IOException {
|
Result<Maybe<ObjectId>> removeKey(final Ref branchRef, final String key) {
|
||||||
return keyRemover.remove(branchRef, key);
|
return keyRemover.remove(branchRef, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,15 +23,20 @@ package net.kemitix.gitdb.impl;
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.kemitix.mon.result.Result;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.eclipse.jgit.revwalk.RevTree;
|
||||||
import org.eclipse.jgit.revwalk.RevWalk;
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||||
import org.eclipse.jgit.treewalk.filter.PathFilter;
|
import org.eclipse.jgit.treewalk.filter.PathFilter;
|
||||||
import org.eclipse.jgit.treewalk.filter.TreeFilter;
|
import org.eclipse.jgit.treewalk.filter.TreeFilter;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,23 +56,55 @@ class GitTreeReader {
|
||||||
*
|
*
|
||||||
* @param branchRef the branch to read
|
* @param branchRef the branch to read
|
||||||
* @return a stream of key/value pairs as NamedRevBlobs
|
* @return a stream of key/value pairs as NamedRevBlobs
|
||||||
* @throws IOException if there is an error reading the commit or walking the tree
|
|
||||||
*/
|
*/
|
||||||
Stream<NamedRevBlob> stream(final Ref branchRef) throws IOException {
|
Result<Stream<NamedRevBlob>> stream(final Ref branchRef) {
|
||||||
final TreeWalk treeWalk = new TreeWalk(repository);
|
final TreeWalk treeWalk = new TreeWalk(repository);
|
||||||
final RevWalk revWalk = new RevWalk(repository);
|
final RevWalk revWalk = new RevWalk(repository);
|
||||||
treeWalk.addTree(revWalk.parseCommit(branchRef.getObjectId()).getTree());
|
return Result.of(parseBranchCommit(branchRef, revWalk))
|
||||||
treeWalk.setRecursive(false);
|
.map(RevCommit::getTree)
|
||||||
Optional.ofNullable(treeFilter)
|
.flatMap(addTreeTo(treeWalk))
|
||||||
.ifPresent(treeWalk::setFilter);
|
.peek(disableRecursion(treeWalk))
|
||||||
|
.peek(setTreeFilter(treeWalk))
|
||||||
|
.flatMap(streamMatching(treeWalk, revWalk));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function<Void, Result<Stream<NamedRevBlob>>> streamMatching(
|
||||||
|
final TreeWalk treeWalk,
|
||||||
|
final RevWalk revWalk
|
||||||
|
) {
|
||||||
|
return x -> Result.of(() -> {
|
||||||
final Stream.Builder<NamedRevBlob> builder = Stream.builder();
|
final Stream.Builder<NamedRevBlob> builder = Stream.builder();
|
||||||
while (treeWalk.next()) {
|
while (treeWalk.next()) {
|
||||||
builder.add(new NamedRevBlob(
|
builder.add(namedRevBlob(treeWalk, revWalk));
|
||||||
treeWalk.getNameString(),
|
|
||||||
revWalk.lookupBlob(treeWalk.getObjectId(0)),
|
|
||||||
repository));
|
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private NamedRevBlob namedRevBlob(TreeWalk treeWalk, RevWalk revWalk) {
|
||||||
|
return new NamedRevBlob(
|
||||||
|
treeWalk.getNameString(),
|
||||||
|
revWalk.lookupBlob(treeWalk.getObjectId(0)),
|
||||||
|
repository);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Consumer<Void> setTreeFilter(TreeWalk treeWalk) {
|
||||||
|
return x -> Optional.ofNullable(treeFilter).ifPresent(treeWalk::setFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Consumer<Void> disableRecursion(TreeWalk treeWalk) {
|
||||||
|
return x -> treeWalk.setRecursive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function<RevTree, Result<Void>> addTreeTo(TreeWalk treeWalk) {
|
||||||
|
return tree -> Result.of(() -> {
|
||||||
|
treeWalk.addTree(tree);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Callable<RevCommit> parseBranchCommit(Ref branchRef, RevWalk revWalk) {
|
||||||
|
return () -> revWalk.parseCommit(branchRef.getObjectId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,7 +114,7 @@ class GitTreeReader {
|
||||||
* @return the GitTreeReader
|
* @return the GitTreeReader
|
||||||
*/
|
*/
|
||||||
GitTreeReader treeFilter(final String path) {
|
GitTreeReader treeFilter(final String path) {
|
||||||
this.treeFilter = PathFilter.create(path);
|
treeFilter = PathFilter.create(path);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,11 @@
|
||||||
package net.kemitix.gitdb.impl;
|
package net.kemitix.gitdb.impl;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.kemitix.mon.result.Result;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
@ -47,16 +47,17 @@ class HeadWriter {
|
||||||
* @param branchName the branch name
|
* @param branchName the branch name
|
||||||
* @param commitId the commit to point the branch at
|
* @param commitId the commit to point the branch at
|
||||||
* @return the Ref of the new branch
|
* @return the Ref of the new branch
|
||||||
* @throws IOException error writing branch head
|
|
||||||
*/
|
*/
|
||||||
Ref write(final String branchName, final ObjectId commitId) throws IOException {
|
Result<Ref> write(final String branchName, final ObjectId commitId) {
|
||||||
final Path branchRefPath = repository
|
final Path branchRefPath = repository
|
||||||
.getDirectory()
|
.getDirectory()
|
||||||
.toPath()
|
.toPath()
|
||||||
.resolve(branchName)
|
.resolve(branchName)
|
||||||
.toAbsolutePath();
|
.toAbsolutePath();
|
||||||
final byte[] commitIdBytes = commitId.name().getBytes(StandardCharsets.UTF_8);
|
final byte[] commitIdBytes = commitId.name().getBytes(StandardCharsets.UTF_8);
|
||||||
|
return Result.of(() -> {
|
||||||
Files.write(branchRefPath, commitIdBytes);
|
Files.write(branchRefPath, commitIdBytes);
|
||||||
return repository.findRef(branchName);
|
return repository.findRef(branchName);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
package net.kemitix.gitdb.impl;
|
package net.kemitix.gitdb.impl;
|
||||||
|
|
||||||
import net.kemitix.gitdb.FormatVersion;
|
import net.kemitix.gitdb.FormatVersion;
|
||||||
|
import net.kemitix.mon.result.Result;
|
||||||
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.RepositoryCache;
|
import org.eclipse.jgit.lib.RepositoryCache;
|
||||||
|
@ -50,35 +51,73 @@ class InitGitDBRepo {
|
||||||
* Initialise a new GitDB repo.
|
* Initialise a new GitDB repo.
|
||||||
*
|
*
|
||||||
* @param dbDir the directory to initialise the repo in
|
* @param dbDir the directory to initialise the repo in
|
||||||
* @throws IOException if there is an error in creating the repo files
|
|
||||||
*/
|
*/
|
||||||
static void create(final Path dbDir) throws IOException {
|
static Result<Void> create(final Path dbDir) {
|
||||||
final InitGitDBRepo initRepo = new InitGitDBRepo();
|
final InitGitDBRepo initRepo = new InitGitDBRepo();
|
||||||
final File validDbDir = initRepo.validDbDir(dbDir.toFile());
|
return initRepo.validDbDir(dbDir.toFile())
|
||||||
validDbDir.mkdirs();
|
.peek(File::mkdirs)
|
||||||
try (Repository repository = RepositoryCache.FileKey.exact(validDbDir, FS.DETECTED).open(false)) {
|
.flatMap(dir -> {
|
||||||
|
try (Repository repository = RepositoryCache.FileKey.exact(dir, FS.DETECTED).open(false)) {
|
||||||
repository.create(true);
|
repository.create(true);
|
||||||
initRepo.createInitialBranchOnMaster(repository);
|
initRepo.createInitialBranchOnMaster(repository);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return Result.error(e);
|
||||||
}
|
}
|
||||||
|
return Result.ok(null);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createInitialBranchOnMaster(final Repository repository) throws IOException {
|
private Result<File> validDbDir(final File dbDir) {
|
||||||
|
return Result.ok(dbDir)
|
||||||
|
.flatMap(this::verifyIsNotAFile)
|
||||||
|
.flatMap(this::isEmptyIfExists);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<Void> createInitialBranchOnMaster(final Repository repository) {
|
||||||
final GitDBRepo repo = new GitDBRepo(repository);
|
final GitDBRepo repo = new GitDBRepo(repository);
|
||||||
final ValueWriter valueWriter = new ValueWriter(repository);
|
return new ValueWriter(repository)
|
||||||
final ObjectId objectId = valueWriter.write(new FormatVersion().toBytes());
|
.write(new FormatVersion().toBytes())
|
||||||
final ObjectId treeId = repo.insertNewTree(GIT_DB_VERSION, objectId);
|
.flatMap(oid -> repo.insertNewTree(GIT_DB_VERSION, oid))
|
||||||
final ObjectId commitId = repo.initialCommit(treeId, INIT_MESSAGE, INIT_USER, INIT_EMAIL);
|
.flatMap(tid -> repo.initialCommit(tid, INIT_MESSAGE, INIT_USER, INIT_EMAIL))
|
||||||
createBranch(repository, commitId, MASTER);
|
.flatMap(cid -> Result.of(() -> {
|
||||||
|
createBranch(repository, cid, MASTER);
|
||||||
|
return null;
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createBranch(
|
private Result<File> verifyIsNotAFile(final File dbDir) {
|
||||||
|
if (dbDir.isFile()) {
|
||||||
|
return Result.error(new NotDirectoryException(dbDir.toString()));
|
||||||
|
}
|
||||||
|
return Result.ok(dbDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<File> isEmptyIfExists(final File dbDir) {
|
||||||
|
if (dbDir.exists()) {
|
||||||
|
return Result.of(() -> {
|
||||||
|
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(dbDir.toPath())) {
|
||||||
|
if (directoryStream.iterator().hasNext()) {
|
||||||
|
throw new DirectoryNotEmptyException(dbDir.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dbDir;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Result.ok(dbDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<Void> createBranch(
|
||||||
final Repository repository,
|
final Repository repository,
|
||||||
final ObjectId commitId,
|
final ObjectId commitId,
|
||||||
final String branchName
|
final String branchName
|
||||||
) throws IOException {
|
) {
|
||||||
final Path branchRefPath = branchRefPath(repository, branchName);
|
final Path branchRefPath = branchRefPath(repository, branchName);
|
||||||
final byte[] commitIdBytes = commitId.name().getBytes(StandardCharsets.UTF_8);
|
final byte[] commitIdBytes = commitId.name().getBytes(StandardCharsets.UTF_8);
|
||||||
|
return Result.of(() -> {
|
||||||
Files.write(branchRefPath, commitIdBytes);
|
Files.write(branchRefPath, commitIdBytes);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path branchRefPath(
|
private Path branchRefPath(
|
||||||
|
@ -90,26 +129,4 @@ class InitGitDBRepo {
|
||||||
.resolve(String.format(REFS_HEADS_FORMAT, branchName))
|
.resolve(String.format(REFS_HEADS_FORMAT, branchName))
|
||||||
.toAbsolutePath();
|
.toAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
private File validDbDir(final File dbDir) throws IOException {
|
|
||||||
verifyIsNotAFile(dbDir);
|
|
||||||
if (dbDir.exists()) {
|
|
||||||
verifyIsEmpty(dbDir);
|
|
||||||
}
|
|
||||||
return dbDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void verifyIsEmpty(final File dbDir) throws IOException {
|
|
||||||
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(dbDir.toPath())) {
|
|
||||||
if (directoryStream.iterator().hasNext()) {
|
|
||||||
throw new DirectoryNotEmptyException(dbDir.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void verifyIsNotAFile(final File dbDir) throws NotDirectoryException {
|
|
||||||
if (dbDir.isFile()) {
|
|
||||||
throw new NotDirectoryException(dbDir.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,11 @@
|
||||||
package net.kemitix.gitdb.impl;
|
package net.kemitix.gitdb.impl;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.kemitix.mon.maybe.Maybe;
|
||||||
|
import net.kemitix.mon.result.Result;
|
||||||
import org.eclipse.jgit.lib.*;
|
import org.eclipse.jgit.lib.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
@ -40,6 +41,24 @@ class KeyRemover {
|
||||||
|
|
||||||
private final Repository repository;
|
private final Repository repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a key from the repository.
|
||||||
|
*
|
||||||
|
* @param branchRef the branch to update
|
||||||
|
* @param key the key to remove
|
||||||
|
* @return the id of the updated tree
|
||||||
|
*/
|
||||||
|
Result<Maybe<ObjectId>> remove(final Ref branchRef, final String key) {
|
||||||
|
final TreeFormatter treeFormatter = new TreeFormatter();
|
||||||
|
final AtomicBoolean removed = new AtomicBoolean(false);
|
||||||
|
new GitTreeReader(repository)
|
||||||
|
.stream(branchRef)
|
||||||
|
.peek(s -> s.peek(flagIfFound(key, removed))
|
||||||
|
.filter(isNotKey(key))
|
||||||
|
.forEach(addToTree(treeFormatter)));
|
||||||
|
return insertTree(treeFormatter).maybe(oi -> removed.get());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the boolean to true if the key matches a NamedRevBlob's name.
|
* Sets the boolean to true if the key matches a NamedRevBlob's name.
|
||||||
*
|
*
|
||||||
|
@ -75,38 +94,17 @@ class KeyRemover {
|
||||||
return item -> treeFormatter.append(item.getName(), item.getRevBlob());
|
return item -> treeFormatter.append(item.getName(), item.getRevBlob());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a key from the repository.
|
|
||||||
*
|
|
||||||
* @param branchRef the branch to update
|
|
||||||
* @param key the key to remove
|
|
||||||
* @return the id of the updated tree
|
|
||||||
* @throws IOException if there is an error writing the value
|
|
||||||
*/
|
|
||||||
Optional<ObjectId> remove(final Ref branchRef, final String key) throws IOException {
|
|
||||||
final TreeFormatter treeFormatter = new TreeFormatter();
|
|
||||||
final AtomicBoolean removed = new AtomicBoolean(false);
|
|
||||||
new GitTreeReader(repository)
|
|
||||||
.stream(branchRef)
|
|
||||||
.peek(flagIfFound(key, removed))
|
|
||||||
.filter(isNotKey(key))
|
|
||||||
.forEach(addToTree(treeFormatter));
|
|
||||||
if (removed.get()) {
|
|
||||||
return Optional.of(insertTree(treeFormatter));
|
|
||||||
}
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert a tree into the repo, returning its id.
|
* Insert a tree into the repo, returning its id.
|
||||||
*
|
*
|
||||||
* @param treeFormatter the formatter containing the proposed tree's data.
|
* @param treeFormatter the formatter containing the proposed tree's data.
|
||||||
* @return the name of the tree object.
|
* @return the name of the tree object.
|
||||||
* @throws IOException the object could not be stored.
|
|
||||||
*/
|
*/
|
||||||
private ObjectId insertTree(final TreeFormatter treeFormatter) throws IOException {
|
private Result<ObjectId> insertTree(final TreeFormatter treeFormatter) {
|
||||||
try (ObjectInserter inserter = repository.getObjectDatabase().newInserter()) {
|
try (ObjectInserter inserter = repository.getObjectDatabase().newInserter()) {
|
||||||
return inserter.insert(treeFormatter);
|
return Result.ok(inserter.insert(treeFormatter));
|
||||||
|
} catch (IOException e) {
|
||||||
|
return Result.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,9 @@
|
||||||
|
|
||||||
package net.kemitix.gitdb.impl;
|
package net.kemitix.gitdb.impl;
|
||||||
|
|
||||||
|
import net.kemitix.mon.result.Result;
|
||||||
import org.eclipse.jgit.lib.*;
|
import org.eclipse.jgit.lib.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes Keys into the Git Repository.
|
* Writes Keys into the Git Repository.
|
||||||
*
|
*
|
||||||
|
@ -51,9 +50,8 @@ class KeyWriter {
|
||||||
* @param key the key
|
* @param key the key
|
||||||
* @param valueId the id of the value
|
* @param valueId the id of the value
|
||||||
* @return the id of the new tree
|
* @return the id of the new tree
|
||||||
* @throws IOException if there is an error writing the key
|
|
||||||
*/
|
*/
|
||||||
ObjectId writeFirst(final String key, final ObjectId valueId) throws IOException {
|
Result<ObjectId> writeFirst(final String key, final ObjectId valueId) {
|
||||||
return writeTree(key, valueId, new TreeFormatter());
|
return writeTree(key, valueId, new TreeFormatter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,26 +62,26 @@ class KeyWriter {
|
||||||
* @param valueId the id of the value
|
* @param valueId the id of the value
|
||||||
* @param branchRef the branch whose tree should be updated
|
* @param branchRef the branch whose tree should be updated
|
||||||
* @return the id of the updated tree
|
* @return the id of the updated tree
|
||||||
* @throws IOException if there is an error writing the key
|
|
||||||
*/
|
*/
|
||||||
ObjectId write(final String key, final ObjectId valueId, final Ref branchRef) throws IOException {
|
Result<ObjectId> write(final String key, final ObjectId valueId, final Ref branchRef) {
|
||||||
return writeTree(key, valueId, getTreeFormatter(branchRef));
|
return getTreeFormatter(branchRef)
|
||||||
|
.flatMap(f -> writeTree(key, valueId, f));
|
||||||
}
|
}
|
||||||
|
|
||||||
private TreeFormatter getTreeFormatter(final Ref branchRef) throws IOException {
|
private Result<TreeFormatter> getTreeFormatter(final Ref branchRef) {
|
||||||
final TreeFormatter treeFormatter = new TreeFormatter();
|
final TreeFormatter treeFormatter = new TreeFormatter();
|
||||||
final GitTreeReader gitTreeReader = new GitTreeReader(repository);
|
final GitTreeReader gitTreeReader = new GitTreeReader(repository);
|
||||||
gitTreeReader.stream(branchRef)
|
return gitTreeReader.stream(branchRef)
|
||||||
.forEach(item -> treeFormatter.append(item.getName(), item.getRevBlob()));
|
.peek(s -> s.forEach(item -> treeFormatter.append(item.getName(), item.getRevBlob())))
|
||||||
return treeFormatter;
|
.map(x -> treeFormatter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObjectId writeTree(
|
private Result<ObjectId> writeTree(
|
||||||
final String key,
|
final String key,
|
||||||
final ObjectId valueId,
|
final ObjectId valueId,
|
||||||
final TreeFormatter treeFormatter
|
final TreeFormatter treeFormatter
|
||||||
) throws IOException {
|
) {
|
||||||
treeFormatter.append(key, FileMode.REGULAR_FILE, valueId);
|
treeFormatter.append(key, FileMode.REGULAR_FILE, valueId);
|
||||||
return objectInserter.insert(treeFormatter);
|
return Result.of(() -> objectInserter.insert(treeFormatter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
package net.kemitix.gitdb.impl;
|
package net.kemitix.gitdb.impl;
|
||||||
|
|
||||||
import net.kemitix.gitdb.GitDB;
|
import net.kemitix.gitdb.GitDB;
|
||||||
|
import net.kemitix.mon.result.Result;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,9 +40,8 @@ public interface LocalGitDB extends GitDB {
|
||||||
* @param userName the user name
|
* @param userName the user name
|
||||||
* @param userEmailAddress the user email address
|
* @param userEmailAddress the user email address
|
||||||
* @return a GitDB instance for the created local gitdb
|
* @return a GitDB instance for the created local gitdb
|
||||||
* @throws IOException if there {@code dbDir} is a file or a non-empty directory
|
|
||||||
*/
|
*/
|
||||||
static GitDB init(final Path dbDir, final String userName, final String userEmailAddress) throws IOException {
|
static Result<GitDB> init(final Path dbDir, final String userName, final String userEmailAddress) {
|
||||||
return LocalGitDBImpl.init(dbDir, userName, userEmailAddress);
|
return LocalGitDBImpl.init(dbDir, userName, userEmailAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +53,7 @@ public interface LocalGitDB extends GitDB {
|
||||||
* @param userEmailAddress the user email address
|
* @param userEmailAddress the user email address
|
||||||
* @return a GitDB instance for the created local gitdb
|
* @return a GitDB instance for the created local gitdb
|
||||||
*/
|
*/
|
||||||
static GitDB open(final Path dbDir, final String userName, final String userEmailAddress) {
|
static Result<GitDB> open(final Path dbDir, final String userName, final String userEmailAddress) {
|
||||||
return LocalGitDBImpl.open(dbDir, userName, userEmailAddress);
|
return LocalGitDBImpl.open(dbDir, userName, userEmailAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,14 @@ package net.kemitix.gitdb.impl;
|
||||||
import net.kemitix.gitdb.GitDB;
|
import net.kemitix.gitdb.GitDB;
|
||||||
import net.kemitix.gitdb.GitDBBranch;
|
import net.kemitix.gitdb.GitDBBranch;
|
||||||
import net.kemitix.gitdb.InvalidRepositoryException;
|
import net.kemitix.gitdb.InvalidRepositoryException;
|
||||||
|
import net.kemitix.mon.maybe.Maybe;
|
||||||
|
import net.kemitix.mon.result.Result;
|
||||||
import org.eclipse.jgit.api.Git;
|
import org.eclipse.jgit.api.Git;
|
||||||
import org.eclipse.jgit.lib.*;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,10 +45,8 @@ final class LocalGitDBImpl implements GitDB, LocalGitDB {
|
||||||
private static final String ERROR_OPENING_REPOSITORY = "Error opening repository";
|
private static final String ERROR_OPENING_REPOSITORY = "Error opening repository";
|
||||||
|
|
||||||
private final Repository repository;
|
private final Repository repository;
|
||||||
private final String userName;
|
|
||||||
private final String userEmailAddress;
|
|
||||||
|
|
||||||
private final Function<Ref, GitDBBranch> branchInit;
|
private final Function<Ref, Result<GitDBBranch>> branchInit;
|
||||||
|
|
||||||
private LocalGitDBImpl(
|
private LocalGitDBImpl(
|
||||||
final Repository repository,
|
final Repository repository,
|
||||||
|
@ -54,9 +54,7 @@ final class LocalGitDBImpl implements GitDB, LocalGitDB {
|
||||||
final String userEmailAddress
|
final String userEmailAddress
|
||||||
) {
|
) {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.userName = userName;
|
branchInit = GitDBBranchImpl.init(this.repository, userName, userEmailAddress);
|
||||||
this.userEmailAddress = userEmailAddress;
|
|
||||||
branchInit = GitDBBranchImpl.init(this.repository, this.userName, this.userEmailAddress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,15 +64,14 @@ final class LocalGitDBImpl implements GitDB, LocalGitDB {
|
||||||
* @param userName the user name
|
* @param userName the user name
|
||||||
* @param userEmailAddress the user email address
|
* @param userEmailAddress the user email address
|
||||||
* @return a GitDB instance for the created local gitdb
|
* @return a GitDB instance for the created local gitdb
|
||||||
* @throws IOException if there {@code dbDir} is a file or a non-empty directory
|
|
||||||
*/
|
*/
|
||||||
static GitDB init(
|
static Result<GitDB> init(
|
||||||
final Path dbDir,
|
final Path dbDir,
|
||||||
final String userName,
|
final String userName,
|
||||||
final String userEmailAddress
|
final String userEmailAddress
|
||||||
) throws IOException {
|
) {
|
||||||
InitGitDBRepo.create(dbDir);
|
return InitGitDBRepo.create(dbDir)
|
||||||
return open(dbDir, userName, userEmailAddress);
|
.flatMap(c -> open(dbDir, userName, userEmailAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,25 +82,43 @@ final class LocalGitDBImpl implements GitDB, LocalGitDB {
|
||||||
* @param userEmailAddress the user email address
|
* @param userEmailAddress the user email address
|
||||||
* @return a GitDB instance for the created local gitdb
|
* @return a GitDB instance for the created local gitdb
|
||||||
*/
|
*/
|
||||||
static GitDB open(
|
static Result<GitDB> open(
|
||||||
final Path dbDir,
|
final Path dbDir,
|
||||||
final String userName,
|
final String userName,
|
||||||
final String userEmailAddress
|
final String userEmailAddress
|
||||||
) {
|
) {
|
||||||
try {
|
return gitOpen(dbDir)
|
||||||
return Optional.of(Git.open(dbDir.toFile()))
|
|
||||||
.map(Git::getRepository)
|
.map(Git::getRepository)
|
||||||
.filter(Repository::isBare)
|
.maybe(Repository::isBare)
|
||||||
.map(repository -> new LocalGitDBImpl(repository, userName, userEmailAddress))
|
.flatMap(asErrorIfNotBare(dbDir))
|
||||||
.orElseThrow(() -> new InvalidRepositoryException(NOT_A_BARE_REPO, dbDir));
|
.map(toLocalGitDB(userName, userEmailAddress));
|
||||||
} catch (IOException e) {
|
|
||||||
throw new InvalidRepositoryException(ERROR_OPENING_REPOSITORY, dbDir, e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Result<Git> gitOpen(Path dbDir) {
|
||||||
|
try {
|
||||||
|
return Result.ok(Git.open(dbDir.toFile()));
|
||||||
|
} catch (IOException e) {
|
||||||
|
return Result.error(new InvalidRepositoryException(ERROR_OPENING_REPOSITORY, dbDir, e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Function<Maybe<Repository>, Result<Repository>> asErrorIfNotBare(final Path dbDir) {
|
||||||
|
return maybe -> Result.fromMaybe(maybe, () -> new InvalidRepositoryException(NOT_A_BARE_REPO, dbDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Function<Repository, GitDB> toLocalGitDB(final String userName, final String userEmailAddress) {
|
||||||
|
return repository -> new LocalGitDBImpl(repository, userName, userEmailAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<GitDBBranch> branch(final String name) throws IOException {
|
public Result<Maybe<GitDBBranch>> branch(final String name) {
|
||||||
return Optional.ofNullable(repository.findRef(name)).map(branchInit);
|
try {
|
||||||
|
return Result.invert(Maybe.maybe(
|
||||||
|
repository.findRef(name))
|
||||||
|
.map(branchInit::apply));
|
||||||
|
} catch (IOException e) {
|
||||||
|
return Result.error(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,9 @@ package net.kemitix.gitdb.impl;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.kemitix.mon.result.Result;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
|
import org.eclipse.jgit.lib.ObjectLoader;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.eclipse.jgit.revwalk.RevBlob;
|
import org.eclipse.jgit.revwalk.RevBlob;
|
||||||
|
|
||||||
|
@ -48,10 +50,15 @@ class NamedRevBlob {
|
||||||
* Converts the blob to a String.
|
* Converts the blob to a String.
|
||||||
*
|
*
|
||||||
* @return a string
|
* @return a string
|
||||||
* @throws IOException of there was an error reading the blob
|
|
||||||
*/
|
*/
|
||||||
String blobAsString() throws IOException {
|
Result<String> blobAsString() {
|
||||||
return new String(repository.open(revBlob.getId(), Constants.OBJ_BLOB).getBytes());
|
try {
|
||||||
|
return Result.ok(repository.open(revBlob.getId(), Constants.OBJ_BLOB))
|
||||||
|
.map(ObjectLoader::getBytes)
|
||||||
|
.map(String::new);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return Result.error(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,13 +21,12 @@
|
||||||
|
|
||||||
package net.kemitix.gitdb.impl;
|
package net.kemitix.gitdb.impl;
|
||||||
|
|
||||||
|
import net.kemitix.mon.result.Result;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.ObjectInserter;
|
import org.eclipse.jgit.lib.ObjectInserter;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes Values into the Git Repository.
|
* Writes Values into the Git Repository.
|
||||||
*
|
*
|
||||||
|
@ -51,9 +50,8 @@ class ValueWriter {
|
||||||
*
|
*
|
||||||
* @param blob the value blob
|
* @param blob the value blob
|
||||||
* @return the id of the value object
|
* @return the id of the value object
|
||||||
* @throws IOException if there is an error writing the value
|
|
||||||
*/
|
*/
|
||||||
ObjectId write(final byte[] blob) throws IOException {
|
Result<ObjectId> write(final byte[] blob) {
|
||||||
return objectInserter.insert(Constants.OBJ_BLOB, blob);
|
return Result.of(() -> objectInserter.insert(Constants.OBJ_BLOB, blob));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,58 +5,53 @@ import net.kemitix.gitdb.FormatVersion;
|
||||||
import net.kemitix.gitdb.GitDB;
|
import net.kemitix.gitdb.GitDB;
|
||||||
import net.kemitix.gitdb.GitDBBranch;
|
import net.kemitix.gitdb.GitDBBranch;
|
||||||
import net.kemitix.gitdb.InvalidRepositoryException;
|
import net.kemitix.gitdb.InvalidRepositoryException;
|
||||||
|
import net.kemitix.mon.maybe.Maybe;
|
||||||
|
import net.kemitix.mon.result.Result;
|
||||||
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.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;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
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;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assumptions.assumeThat;
|
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
|
||||||
class GitDBTest implements WithAssertions {
|
class GitDBTest implements WithAssertions {
|
||||||
|
|
||||||
private final Supplier<String> stringSupplier = UUID.randomUUID()::toString;
|
private final Supplier<String> stringSupplier = UUID.randomUUID()::toString;
|
||||||
private final String userName = stringSupplier.get();
|
private final String userName = stringSupplier.get();
|
||||||
private final String userEmailAddress = stringSupplier.get();
|
private final String userEmailAddress = stringSupplier.get();
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When initialising a repo in a dir that doesn't exist then a bare repo is created
|
// When initialising a repo in a dir that doesn't exist then a bare repo is created
|
||||||
@Test
|
@Test
|
||||||
void initRepo_whenDirNotExist_thenCreateBareRepo() throws IOException {
|
void initRepo_whenDirNotExist_thenCreateBareRepo() throws IOException {
|
||||||
//given
|
//given
|
||||||
final Path dir = dirDoesNotExist();
|
final Path dir = dirDoesNotExist();
|
||||||
//when
|
//when
|
||||||
final GitDB gitDB = GitDB.initLocal(dir, userName, userEmailAddress);
|
final Result<GitDB> gitDB = GitDB.initLocal(dir, userName, userEmailAddress);
|
||||||
//then
|
//then
|
||||||
assertThat(gitDB).isNotNull();
|
assertThatResultIsOkay(gitDB);
|
||||||
assertThatIsBareRepo(dir);
|
assertThatIsBareRepo(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Path dirDoesNotExist() throws IOException {
|
||||||
|
final Path directory = Files.createTempDirectory("gitdb");
|
||||||
|
Files.delete(directory);
|
||||||
|
return directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> void assertThatResultIsOkay(final Result<T> result) {
|
||||||
|
assertThat(result.isOkay()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
private void assertThatIsBareRepo(final Path dbDir) throws IOException {
|
private void assertThatIsBareRepo(final Path dbDir) throws IOException {
|
||||||
final Git git = Git.open(dbDir.toFile());
|
final Git git = Git.open(dbDir.toFile());
|
||||||
final Repository repository = git.getRepository();
|
final Repository repository = git.getRepository();
|
||||||
|
@ -65,56 +60,64 @@ class GitDBTest implements WithAssertions {
|
||||||
assertThat(repository.getDirectory()).isEqualTo(dbDir.toFile());
|
assertThat(repository.getDirectory()).isEqualTo(dbDir.toFile());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path dirDoesNotExist() throws IOException {
|
|
||||||
final Path directory = Files.createTempDirectory("gitdb");
|
|
||||||
Files.delete(directory);
|
|
||||||
return directory;
|
|
||||||
}
|
|
||||||
|
|
||||||
// When initialising a repo in a dir that is a file then an exception is thrown
|
// When initialising a repo in a dir that is a file then an exception is thrown
|
||||||
@Test
|
@Test
|
||||||
void initRepo_whenDirIsFile_thenThrowException() throws IOException {
|
void initRepo_whenDirIsFile_thenThrowException() throws IOException {
|
||||||
//given
|
//given
|
||||||
final Path dir = fileExists();
|
final Path dir = fileExists();
|
||||||
|
//when
|
||||||
|
final Result<GitDB> gitDBResult = GitDB.initLocal(dir, userName, userEmailAddress);
|
||||||
//then
|
//then
|
||||||
assertThatExceptionOfType(NotDirectoryException.class)
|
gitDBResult.match(
|
||||||
.isThrownBy(() -> GitDB.initLocal(dir, userName, userEmailAddress))
|
failOnSuccess("Is a file not a directory"),
|
||||||
.withMessageContaining(dir.toString());
|
error -> assertThat(error)
|
||||||
|
.isInstanceOf(NotDirectoryException.class)
|
||||||
|
.hasMessageContaining(dir.toString())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path fileExists() throws IOException {
|
private Path fileExists() throws IOException {
|
||||||
return Files.createTempFile("gitdb", "file");
|
return Files.createTempFile("gitdb", "file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private <T> Consumer<T> failOnSuccess(String message) {
|
||||||
|
return success -> fail(message);
|
||||||
|
}
|
||||||
|
|
||||||
// When initialising a repo in a non-empty dir then an exception is thrown
|
// When initialising a repo in a non-empty dir then an exception is thrown
|
||||||
@Test
|
@Test
|
||||||
void initRepo_whenNotEmptyDir_thenThrowException() throws IOException {
|
void initRepo_whenNotEmptyDir_thenThrowException() throws IOException {
|
||||||
//given
|
//given
|
||||||
final Path dir = dirExists();
|
final Path dir = dirExists();
|
||||||
filesExistIn(dir);
|
filesExistIn(dir);
|
||||||
|
//when
|
||||||
|
final Result<GitDB> gitDBResult = GitDB.initLocal(dir, userName, userEmailAddress);
|
||||||
//then
|
//then
|
||||||
assertThatExceptionOfType(DirectoryNotEmptyException.class)
|
gitDBResult.match(
|
||||||
.isThrownBy(() -> GitDB.initLocal(dir, userName, userEmailAddress))
|
failOnSuccess("Directory is not empty"),
|
||||||
.withMessageContaining(dir.toString());
|
error -> assertThat(error)
|
||||||
}
|
.isInstanceOf(DirectoryNotEmptyException.class)
|
||||||
|
.hasMessageContaining(dir.toString())
|
||||||
private void filesExistIn(final Path dir) throws IOException {
|
);
|
||||||
Files.createTempFile(dir, "gitdb", "file");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path dirExists() throws IOException {
|
private Path dirExists() throws IOException {
|
||||||
return Files.createTempDirectory("gitdb");
|
return Files.createTempDirectory("gitdb");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void filesExistIn(final Path dir) throws IOException {
|
||||||
|
Files.createTempFile(dir, "gitdb", "file");
|
||||||
|
}
|
||||||
|
|
||||||
// When initialising a repo in a empty dir then a bare repo is created
|
// When initialising a repo in a empty dir then a bare repo is created
|
||||||
@Test
|
@Test
|
||||||
void initRepo_whenEmptyDir_thenCreateBareRepo() throws IOException {
|
void initRepo_whenEmptyDir_thenCreateBareRepo() throws IOException {
|
||||||
//given
|
//given
|
||||||
final Path dir = dirExists();
|
final Path dir = dirExists();
|
||||||
//when
|
//when
|
||||||
final GitDB gitDB = GitDB.initLocal(dir, userName, userEmailAddress);
|
final Result<GitDB> gitDB = GitDB.initLocal(dir, userName, userEmailAddress);
|
||||||
//then
|
//then
|
||||||
assertThat(gitDB).isNotNull();
|
assertThatResultIsOkay(gitDB);
|
||||||
assertThatIsBareRepo(dir);
|
assertThatIsBareRepo(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,10 +126,16 @@ class GitDBTest implements WithAssertions {
|
||||||
void openRepo_NotBareRepo_thenThrowException() throws IOException {
|
void openRepo_NotBareRepo_thenThrowException() throws IOException {
|
||||||
//given
|
//given
|
||||||
final Path dir = dirExists();
|
final Path dir = dirExists();
|
||||||
|
//when
|
||||||
|
final Result<GitDB> gitDBResult = GitDB.openLocal(dir, userName, userEmailAddress);
|
||||||
//then
|
//then
|
||||||
assertThatExceptionOfType(InvalidRepositoryException.class)
|
gitDBResult.match(
|
||||||
.isThrownBy(() -> GitDB.openLocal(dir, userName, userEmailAddress))
|
failOnSuccess("Not a bare repo"),
|
||||||
.withMessageContaining(dir.toString());
|
error -> assertThat(error)
|
||||||
|
.isInstanceOf(InvalidRepositoryException.class)
|
||||||
|
.hasMessageContaining(dir.toString())
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// When opening a repo in a dir that is a file then an exception is thrown
|
// When opening a repo in a dir that is a file then an exception is thrown
|
||||||
|
@ -134,10 +143,15 @@ class GitDBTest implements WithAssertions {
|
||||||
void openRepo_whenDirIsFile_thenThrowException() throws IOException {
|
void openRepo_whenDirIsFile_thenThrowException() throws IOException {
|
||||||
//given
|
//given
|
||||||
final Path dir = fileExists();
|
final Path dir = fileExists();
|
||||||
|
//when
|
||||||
|
final Result<GitDB> gitDBResult = GitDB.openLocal(dir, userName, userEmailAddress);
|
||||||
//then
|
//then
|
||||||
assertThatExceptionOfType(InvalidRepositoryException.class)
|
gitDBResult.match(
|
||||||
.isThrownBy(() -> GitDB.openLocal(dir, userName, userEmailAddress))
|
failOnSuccess("Directory is a file"),
|
||||||
.withMessageContaining(dir.toString());
|
error -> assertThat(error)
|
||||||
|
.isInstanceOf(InvalidRepositoryException.class)
|
||||||
|
.hasMessageContaining(dir.toString())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When opening a repo in a dir that doesn't exist then an exception is thrown
|
// When opening a repo in a dir that doesn't exist then an exception is thrown
|
||||||
|
@ -145,10 +159,15 @@ class GitDBTest implements WithAssertions {
|
||||||
void openRepo_whenDirNotExist_thenThrowException() throws IOException {
|
void openRepo_whenDirNotExist_thenThrowException() throws IOException {
|
||||||
//given
|
//given
|
||||||
final Path dir = dirDoesNotExist();
|
final Path dir = dirDoesNotExist();
|
||||||
|
//when
|
||||||
|
final Result<GitDB> gitDBResult = GitDB.openLocal(dir, userName, userEmailAddress);
|
||||||
//then
|
//then
|
||||||
assertThatExceptionOfType(InvalidRepositoryException.class)
|
gitDBResult.match(
|
||||||
.isThrownBy(() -> GitDB.openLocal(dir, userName, userEmailAddress))
|
failOnSuccess("Directory does not exist"),
|
||||||
.withMessageContaining(dir.toString());
|
error -> assertThat(error)
|
||||||
|
.isInstanceOf(InvalidRepositoryException.class)
|
||||||
|
.hasMessageContaining(dir.toString())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When opening a repo in a dir that is not a bare repo then an exception is thrown
|
// When opening a repo in a dir that is not a bare repo then an exception is thrown
|
||||||
|
@ -156,12 +175,17 @@ class GitDBTest implements WithAssertions {
|
||||||
void openRepo_whenRepoNotBare_thenThrowException() throws IOException, GitAPIException {
|
void openRepo_whenRepoNotBare_thenThrowException() throws IOException, GitAPIException {
|
||||||
//given
|
//given
|
||||||
final Path dir = nonBareRepo();
|
final Path dir = nonBareRepo();
|
||||||
|
//when
|
||||||
|
final Result<GitDB> gitDBResult = GitDB.openLocal(dir, userName, userEmailAddress);
|
||||||
//then
|
//then
|
||||||
assertThatExceptionOfType(InvalidRepositoryException.class)
|
gitDBResult.match(
|
||||||
.isThrownBy(() -> GitDB.openLocal(dir, userName, userEmailAddress))
|
failOnSuccess("Not a bare repo"),
|
||||||
.withMessageContaining("Invalid GitDB repo")
|
error -> assertThat(error)
|
||||||
.withMessageContaining("Not a bare repo")
|
.isInstanceOf(InvalidRepositoryException.class)
|
||||||
.withMessageContaining(dir.toString());
|
.hasMessageContaining("Invalid GitDB repo")
|
||||||
|
.hasMessageContaining("Not a bare repo")
|
||||||
|
.hasMessageContaining(dir.toString())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path nonBareRepo() throws IOException, GitAPIException {
|
private Path nonBareRepo() throws IOException, GitAPIException {
|
||||||
|
@ -176,9 +200,13 @@ class GitDBTest implements WithAssertions {
|
||||||
//given
|
//given
|
||||||
final Path dir = gitDBRepoPath();
|
final Path dir = gitDBRepoPath();
|
||||||
//when
|
//when
|
||||||
final GitDB gitDB = GitDB.openLocal(dir, userName, userEmailAddress);
|
final Result<GitDB> gitDB = GitDB.openLocal(dir, userName, userEmailAddress);
|
||||||
//then
|
//then
|
||||||
assertThat(gitDB).isNotNull();
|
assertThat(gitDB.isOkay()).isTrue();
|
||||||
|
gitDB.match(
|
||||||
|
success -> assertThat(success).isNotNull(),
|
||||||
|
error -> fail("did not open local repo")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path gitDBRepoPath() throws IOException {
|
private Path gitDBRepoPath() throws IOException {
|
||||||
|
@ -189,144 +217,179 @@ class GitDBTest implements WithAssertions {
|
||||||
|
|
||||||
// Given a valid GitDb handle
|
// Given a valid GitDb handle
|
||||||
|
|
||||||
private GitDB gitDB(final Path dbDir) throws IOException {
|
// When select a branch that doesn't exist then an empty Optional is returned
|
||||||
|
@Test
|
||||||
|
void selectBranch_whenBranchNotExist_thenEmptyOptional() throws Throwable {
|
||||||
|
//given
|
||||||
|
final Result<GitDB> gitDb = gitDB(dirDoesNotExist());
|
||||||
|
//when
|
||||||
|
final Result<Maybe<GitDBBranch>> branch = gitDb.flatMap(selectBranch("unknown"));
|
||||||
|
//then
|
||||||
|
assertThat(branch.orElseThrow().toOptional()).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<GitDB> gitDB(final Path dbDir) {
|
||||||
return GitDB.initLocal(dbDir, userName, userEmailAddress);
|
return GitDB.initLocal(dbDir, userName, userEmailAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When select a branch that doesn't exist then an empty Optional is returned
|
private Function<GitDB, Result<Maybe<GitDBBranch>>> selectBranch(final String branchName) {
|
||||||
@Test
|
return db -> db.branch(branchName);
|
||||||
void selectBranch_whenBranchNotExist_thenEmptyOptional() throws IOException {
|
|
||||||
//given
|
|
||||||
final GitDB gitDb = gitDB(dirDoesNotExist());
|
|
||||||
//when
|
|
||||||
final Optional<GitDBBranch> branch = gitDb.branch("unknown");
|
|
||||||
//then
|
|
||||||
assertThat(branch).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 = gitDB(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
|
||||||
|
|
||||||
private GitDBBranch gitDBBranch() throws IOException {
|
// When select a valid branch then a GitDbBranch is returned
|
||||||
final GitDB gitDB = gitDB(dirDoesNotExist());
|
@Test
|
||||||
final Optional<GitDBBranch> branchOptional = gitDB.branch("master");
|
void selectBranch_branchExists_thenReturnBranch() throws Throwable {
|
||||||
assumeThat(branchOptional).isNotEmpty();
|
//given
|
||||||
return branchOptional.get();
|
final Path dbDir = dirDoesNotExist();
|
||||||
|
final Result<GitDB> gitDb = gitDB(dbDir);
|
||||||
|
//when
|
||||||
|
final Result<Maybe<GitDBBranch>> branch = gitDb.flatMap(selectBranch("master"));
|
||||||
|
//then
|
||||||
|
assertThat(branch.orElseThrow().toOptional()).as("Branch master exists").isNotEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@Test
|
@Test
|
||||||
void getKey_whenKeyNotExist_thenReturnEmptyOptional() throws IOException, ClassNotFoundException {
|
void getKey_whenKeyNotExist_thenReturnEmptyOptional() {
|
||||||
//given
|
//given
|
||||||
final GitDBBranch branch = gitDBBranch();
|
final GitDBBranch branch = gitDBBranch();
|
||||||
//when
|
//when
|
||||||
final Optional<String> value = branch.get("unknown");
|
final Result<Maybe<String>> value = branch.get("unknown");
|
||||||
//then
|
//then
|
||||||
assertThat(value).isEmpty();
|
value.match(
|
||||||
|
success -> assertThat(success.toOptional()).isEmpty(),
|
||||||
|
failOnError()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private GitDBBranch gitDBBranch() {
|
||||||
|
try {
|
||||||
|
return gitDB(dirDoesNotExist())
|
||||||
|
.flatMap(selectBranch("master"))
|
||||||
|
.orElseThrow().orElse(null);
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
throw new RuntimeException("Couldn't create master branch", throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Consumer<Throwable> failOnError() {
|
||||||
|
return error -> fail("Not an error");
|
||||||
}
|
}
|
||||||
|
|
||||||
// When getting the format version it matches expected
|
// When getting the format version it matches expected
|
||||||
@Test
|
@Test
|
||||||
void getVersionFormat_thenFormatIsSet() throws IOException {
|
void getVersionFormat_thenFormatIsSet() {
|
||||||
//given
|
//given
|
||||||
final GitDBBranch gitDBBranch = gitDBBranch();
|
final GitDBBranch gitDBBranch = gitDBBranch();
|
||||||
//when
|
|
||||||
final Optional<Version> formatVersion = gitDBBranch.getFormatVersion();
|
|
||||||
//then
|
|
||||||
final Version version = new FormatVersion().getVersion();
|
final Version version = new FormatVersion().getVersion();
|
||||||
assertThat(formatVersion).contains(version);
|
//when
|
||||||
assertThat(formatVersion.get()).isNotSameAs(version);
|
final Result<Maybe<Version>> formatVersion = gitDBBranch.getFormatVersion();
|
||||||
|
//then
|
||||||
|
formatVersion.match(
|
||||||
|
success -> success.peek(v -> assertThat(v).isEqualTo(version).isNotSameAs(version)),
|
||||||
|
failOnError()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When putting a key/value pair then a GitDbBranch is returned
|
// When putting a key/value pair then a GitDbBranch is returned
|
||||||
@Test
|
@Test
|
||||||
void putValue_thenReturnUpdatedGitDBBranch() throws IOException {
|
void putValue_thenReturnUpdatedGitDBBranch() {
|
||||||
//given
|
//given
|
||||||
final GitDBBranch originalBranch = gitDBBranch();
|
final GitDBBranch originalBranch = gitDBBranch();
|
||||||
//when
|
//when
|
||||||
final GitDBBranch updatedBranch = originalBranch.put("key-name", "value");
|
final Result<GitDBBranch> updatedBranch = originalBranch.put("key-name", "value");
|
||||||
//then
|
//then
|
||||||
assertThat(updatedBranch).isNotNull();
|
updatedBranch.match(
|
||||||
assertThat(updatedBranch).isNotSameAs(originalBranch);
|
success -> assertThat(success).isNotNull().isNotSameAs(originalBranch),
|
||||||
|
failOnError()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When getting a key that does exist then the value is returned inside an Optional
|
// When getting a key that does exist then the value is returned inside an Optional
|
||||||
@Test
|
@Test
|
||||||
void getKey_whenExists_thenReturnValueInOptional() throws IOException, ClassNotFoundException {
|
void getKey_whenExists_thenReturnValueInOptional() {
|
||||||
//given
|
//given
|
||||||
final String key = stringSupplier.get();
|
final String key = stringSupplier.get();
|
||||||
final String value = stringSupplier.get();
|
final String value = stringSupplier.get();
|
||||||
final GitDBBranch originalBranch = gitDBBranch();
|
final GitDBBranch originalBranch = gitDBBranch();
|
||||||
final GitDBBranch updatedBranch = originalBranch.put(key, value);
|
final Result<GitDBBranch> updatedBranch = originalBranch.put(key, value);
|
||||||
//when
|
//when
|
||||||
final Optional<String> result = updatedBranch.get(key);
|
final Result<Maybe<String>> result = updatedBranch.flatMap(b -> b.get(key));
|
||||||
//then
|
//then
|
||||||
assertThat(result).contains(value);
|
result.match(
|
||||||
|
success -> success.map(v -> assertThat(v).contains(value)),
|
||||||
|
failOnError()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When removing a key that does not exist then the GitDbBranch is returned
|
// When removing a key that does not exist then the GitDbBranch is returned
|
||||||
@Test
|
@Test
|
||||||
void removeKey_whenNotExist_thenReturnOriginal() throws IOException {
|
void removeKey_whenNotExist_thenReturnOriginal() {
|
||||||
//given
|
//given
|
||||||
final GitDBBranch gitDBBranch = gitDBBranch();
|
final GitDBBranch gitDBBranch = gitDBBranch();
|
||||||
//when
|
//when
|
||||||
final GitDBBranch result = gitDBBranch.remove("unknown");
|
final Result<GitDBBranch> result = gitDBBranch.remove("unknown");
|
||||||
//then
|
//then
|
||||||
assertThat(result).isSameAs(gitDBBranch);
|
result.match(
|
||||||
|
success -> assertThat(success).isSameAs(gitDBBranch),
|
||||||
|
failOnError()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When removing a key that does exist then a GitDbBranch is returned
|
// When removing a key that does exist then a GitDbBranch is returned
|
||||||
@Test
|
@Test
|
||||||
void removeKey_whenExists_thenReturnUpdatedBranch() throws IOException {
|
void removeKey_whenExists_thenReturnUpdatedBranch() {
|
||||||
//given
|
//given
|
||||||
final String key = stringSupplier.get();
|
final String key = stringSupplier.get();
|
||||||
final String value = stringSupplier.get();
|
final String value = stringSupplier.get();
|
||||||
final GitDBBranch originalBranch = gitDBBranchWithKeyValue(key, value);
|
final Result<GitDBBranch> originalBranch = gitDBBranchWithKeyValue(key, value);
|
||||||
//when
|
//when
|
||||||
final GitDBBranch updatedBranch = originalBranch.remove(key);
|
final Result<GitDBBranch> updatedBranch = originalBranch.flatMap(b -> b.remove(key));
|
||||||
//then
|
//then
|
||||||
assertThat(updatedBranch).isNotSameAs(originalBranch);
|
updatedBranch.match(
|
||||||
|
success -> assertThat(success).isNotSameAs(originalBranch),
|
||||||
|
failOnError()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private GitDBBranch gitDBBranchWithKeyValue(final String key, final String value) throws IOException {
|
private Result<GitDBBranch> gitDBBranchWithKeyValue(final String key, final String value) {
|
||||||
return gitDBBranch().put(key, value);
|
return gitDBBranch().put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When removing a key that does exist then original GitDbBranch can still find it
|
// When removing a key that does exist then original GitDbBranch can still find it
|
||||||
@Test
|
@Test
|
||||||
void removeKey_whenExists_thenOriginalCanStillFind() throws IOException {
|
void removeKey_whenExists_thenOriginalCanStillFind() {
|
||||||
//given
|
//given
|
||||||
final String key = stringSupplier.get();
|
final String key = stringSupplier.get();
|
||||||
final String value = stringSupplier.get();
|
final String value = stringSupplier.get();
|
||||||
final GitDBBranch originalBranch = gitDBBranchWithKeyValue(key, value);
|
final Result<GitDBBranch> originalBranch = gitDBBranchWithKeyValue(key, value);
|
||||||
//when
|
//when
|
||||||
final GitDBBranch updatedBranch = originalBranch.remove(key);
|
final Result<GitDBBranch> updatedBranch = originalBranch.flatMap(b -> b.remove(key));
|
||||||
//then
|
//then
|
||||||
assertThat(originalBranch.get(key)).contains(value);
|
originalBranch.flatMap(b -> b.get(key))
|
||||||
|
.match(
|
||||||
|
success -> assertThat(success.toOptional()).contains(value),
|
||||||
|
failOnError()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When removing a key that does exist then the updated GitDbBranch can't find it
|
// When removing a key that does exist then the updated GitDbBranch can't find it
|
||||||
@Test
|
@Test
|
||||||
void removeKey_whenExists_thenUpdatedCanNotFind() throws IOException {
|
void removeKey_whenExists_thenUpdatedCanNotFind() {
|
||||||
//given
|
//given
|
||||||
final String key = stringSupplier.get();
|
final String key = stringSupplier.get();
|
||||||
final String value = stringSupplier.get();
|
final String value = stringSupplier.get();
|
||||||
final GitDBBranch originalBranch = gitDBBranchWithKeyValue(key, value);
|
final Result<GitDBBranch> originalBranch = gitDBBranchWithKeyValue(key, value);
|
||||||
//when
|
//when
|
||||||
final GitDBBranch updatedBranch = originalBranch.remove(key);
|
final Result<GitDBBranch> updatedBranch = originalBranch.flatMap(b -> b.remove(key));
|
||||||
//then
|
//then
|
||||||
assertThat(updatedBranch.get(key)).isEmpty();
|
updatedBranch.flatMap(b -> b.get(key))
|
||||||
|
.match(
|
||||||
|
success -> assertThat(success.toOptional()).isEmpty(),
|
||||||
|
failOnError()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue