Refactoring: extract GitTreeReader
This commit is contained in:
parent
ffc2b56b05
commit
c57a17b937
4 changed files with 174 additions and 78 deletions
|
@ -28,7 +28,6 @@ import org.eclipse.jgit.lib.Ref;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@ -58,8 +57,8 @@ public class GitDBBranch {
|
||||||
/**
|
/**
|
||||||
* Initialise the creation of new GitDBBranch instances.
|
* Initialise the creation of new GitDBBranch instances.
|
||||||
*
|
*
|
||||||
* @param repository the Git Repository
|
* @param repository the Git Repository
|
||||||
* @param userName the user name to record against changes
|
* @param userName the user name to record against changes
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
|
@ -68,7 +67,7 @@ public class GitDBBranch {
|
||||||
final String userName,
|
final String userName,
|
||||||
final String userEmailAddress
|
final String userEmailAddress
|
||||||
) {
|
) {
|
||||||
return ref -> select(ref, GitDBRepo.in(repository), userName, userEmailAddress);
|
return ref -> select(ref, new GitDBRepo(repository), userName, userEmailAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,11 +90,14 @@ public class GitDBBranch {
|
||||||
* @throws IOException if there was an error writing the value
|
* @throws IOException if there was an error writing the value
|
||||||
*/
|
*/
|
||||||
public GitDBBranch put(final String key, final String value) throws IOException {
|
public GitDBBranch put(final String key, final String value) throws IOException {
|
||||||
final ObjectId objectId = insertBlob(value.getBytes(StandardCharsets.UTF_8));
|
final ObjectId newTree = gitDBRepo.writeValue(branchRef, KEY_PREFIX + key, value);
|
||||||
final ObjectId treeId = insertTree(KEY_PREFIX + key, objectId);
|
final Ref newBranch =
|
||||||
final String commitMessage = String.format("Add key [%s] = [%s]", key, value);
|
gitDBRepo.writeCommit(branchRef, newTree, commitMessageForAdd(key, value), userName, userEmailAddress);
|
||||||
final ObjectId commitId = insertCommit(treeId, commitMessage);
|
return select(newBranch, gitDBRepo, userName, userEmailAddress);
|
||||||
return updateBranch(commitId);
|
}
|
||||||
|
|
||||||
|
private String commitMessageForAdd(final String key, final String value) {
|
||||||
|
return String.format("Add key [%s] = [%s]", key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,21 +110,4 @@ public class GitDBBranch {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObjectId insertBlob(final byte[] blob) throws IOException {
|
|
||||||
return gitDBRepo.insertBlob(blob);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObjectId insertTree(final String key, final ObjectId valueId) throws IOException {
|
|
||||||
return gitDBRepo.insertTree(branchRef, key, valueId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObjectId insertCommit(final ObjectId treeId, final String message) throws IOException {
|
|
||||||
final ObjectId headCommitId = branchRef.getObjectId();
|
|
||||||
return gitDBRepo.insertCommit(treeId, message, userName, userEmailAddress, headCommitId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private GitDBBranch updateBranch(final ObjectId commitId) throws IOException {
|
|
||||||
final Ref updatedRef = gitDBRepo.writeHead(branchRef.getName(), commitId);
|
|
||||||
return select(updatedRef, gitDBRepo, userName, userEmailAddress);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,12 +21,9 @@
|
||||||
|
|
||||||
package net.kemitix.gitdb;
|
package net.kemitix.gitdb;
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.val;
|
||||||
import org.eclipse.jgit.lib.*;
|
import org.eclipse.jgit.lib.*;
|
||||||
import org.eclipse.jgit.revwalk.RevWalk;
|
|
||||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
|
||||||
import org.eclipse.jgit.treewalk.filter.PathFilter;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
@ -39,21 +36,11 @@ import java.util.*;
|
||||||
*
|
*
|
||||||
* @author Paul Campbell (pcampbell@kemitix.net)
|
* @author Paul Campbell (pcampbell@kemitix.net)
|
||||||
*/
|
*/
|
||||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
@RequiredArgsConstructor
|
||||||
class GitDBRepo {
|
class GitDBRepo {
|
||||||
|
|
||||||
private final Repository repository;
|
private final Repository repository;
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a GitDBRepo wrapper for the Repository.
|
|
||||||
*
|
|
||||||
* @param repository the repository to wrap
|
|
||||||
* @return the GitDBRepo wrapper
|
|
||||||
*/
|
|
||||||
public static GitDBRepo in(final Repository repository) {
|
|
||||||
return new GitDBRepo(repository);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert a blob into the store, returning its unique id.
|
* Insert a blob into the store, returning its unique id.
|
||||||
*
|
*
|
||||||
|
@ -98,15 +85,6 @@ class GitDBRepo {
|
||||||
return writeTree(key, valueId, treeFormatterForBranch(branchRef));
|
return writeTree(key, valueId, treeFormatterForBranch(branchRef));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObjectId writeTree(
|
|
||||||
final String key,
|
|
||||||
final ObjectId valueId,
|
|
||||||
final TreeFormatter treeFormatter
|
|
||||||
) throws IOException {
|
|
||||||
treeFormatter.append(key, FileMode.REGULAR_FILE, valueId);
|
|
||||||
return repository.getObjectDatabase().newInserter().insert(treeFormatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert a commit into the store, returning its unique id.
|
* Insert a commit into the store, returning its unique id.
|
||||||
*
|
*
|
||||||
|
@ -135,15 +113,7 @@ class GitDBRepo {
|
||||||
return repository.getObjectDatabase().newInserter().insert(commitBuilder);
|
return repository.getObjectDatabase().newInserter().insert(commitBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private Ref writeHead(
|
||||||
* Updates the branch to point to the new commit.
|
|
||||||
*
|
|
||||||
* @param branchName the branch to update
|
|
||||||
* @param commitId the commit to point the branch at
|
|
||||||
* @return the Ref of the updated branch
|
|
||||||
* @throws IOException if there was an error writing the branch
|
|
||||||
*/
|
|
||||||
Ref writeHead(
|
|
||||||
final String branchName,
|
final String branchName,
|
||||||
final ObjectId commitId
|
final ObjectId commitId
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
|
@ -169,33 +139,68 @@ class GitDBRepo {
|
||||||
final Ref branchRef,
|
final Ref branchRef,
|
||||||
final String key
|
final String key
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
try (TreeWalk treeWalk = getTreeWalk(branchRef)) {
|
val blob = new GitTreeReader(repository)
|
||||||
treeWalk.setFilter(PathFilter.create(key));
|
.treeFilter(key)
|
||||||
if (treeWalk.next()) {
|
.stream(branchRef)
|
||||||
return Optional.of(new String(
|
.findFirst();
|
||||||
repository.open(treeWalk.getObjectId(0), Constants.OBJ_BLOB).getBytes()));
|
if (blob.isPresent()) {
|
||||||
}
|
return Optional.of(blob.get().blobAsString());
|
||||||
}
|
}
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ObjectId writeTree(
|
||||||
|
final String key,
|
||||||
|
final ObjectId valueId,
|
||||||
|
final TreeFormatter treeFormatter
|
||||||
|
) throws IOException {
|
||||||
|
treeFormatter.append(key, FileMode.REGULAR_FILE, valueId);
|
||||||
|
return repository.getObjectDatabase().newInserter().insert(treeFormatter);
|
||||||
|
}
|
||||||
|
|
||||||
private TreeFormatter treeFormatterForBranch(final Ref branchRef) throws IOException {
|
private TreeFormatter treeFormatterForBranch(final Ref branchRef) throws IOException {
|
||||||
final TreeFormatter treeFormatter = new TreeFormatter();
|
final TreeFormatter treeFormatter = new TreeFormatter();
|
||||||
try (TreeWalk treeWalk = getTreeWalk(branchRef)) {
|
final GitTreeReader gitTreeReader = new GitTreeReader(repository);
|
||||||
while (treeWalk.next()) {
|
gitTreeReader.stream(branchRef)
|
||||||
treeFormatter.append(
|
.forEach(item -> treeFormatter.append(item.getName(), item.getRevBlob()));
|
||||||
treeWalk.getNameString(),
|
|
||||||
new RevWalk(repository).lookupBlob(treeWalk.getObjectId(0)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return treeFormatter;
|
return treeFormatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TreeWalk getTreeWalk(final Ref branchRef) throws IOException {
|
/**
|
||||||
final TreeWalk treeWalk = new TreeWalk(repository);
|
* Add the key/value to the repo, returning the tree containing the update.
|
||||||
treeWalk.addTree(new RevWalk(repository).parseCommit(branchRef.getObjectId()).getTree());
|
*
|
||||||
treeWalk.setRecursive(false);
|
* <p>N.B. this creates a tree that has not been committed remains unaware of the update.</p>
|
||||||
return treeWalk;
|
*
|
||||||
|
* @param branchRef the branch to start from
|
||||||
|
* @param key the key to place the value under
|
||||||
|
* @param value the value (must be Serializable)
|
||||||
|
* @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 {
|
||||||
|
final ObjectId blob = insertBlob(value.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return insertTree(branchRef, key, blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the branch to point to the new commit.
|
||||||
|
*
|
||||||
|
* @param branchRef the branch to update
|
||||||
|
* @param tree the tree to commit onto the branch
|
||||||
|
* @param message the commit message
|
||||||
|
* @param userName the user name
|
||||||
|
* @param userEmailAddress the use email address
|
||||||
|
* @return the Ref of the updated branch
|
||||||
|
* @throws IOException if there was an error writing the branch
|
||||||
|
*/
|
||||||
|
Ref writeCommit(
|
||||||
|
final Ref branchRef,
|
||||||
|
final ObjectId tree,
|
||||||
|
final String message,
|
||||||
|
final String userName,
|
||||||
|
final String userEmailAddress
|
||||||
|
) throws IOException {
|
||||||
|
final ObjectId commitId = insertCommit(tree, message, userName, userEmailAddress, branchRef.getObjectId());
|
||||||
|
return writeHead(branchRef.getName(), commitId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
106
src/main/java/net/kemitix/gitdb/GitTreeReader.java
Normal file
106
src/main/java/net/kemitix/gitdb/GitTreeReader.java
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
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 lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.eclipse.jgit.lib.Constants;
|
||||||
|
import org.eclipse.jgit.lib.Ref;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.eclipse.jgit.revwalk.RevBlob;
|
||||||
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
|
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||||
|
import org.eclipse.jgit.treewalk.filter.PathFilter;
|
||||||
|
import org.eclipse.jgit.treewalk.filter.TreeFilter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the entries in a Git Tree object.
|
||||||
|
*
|
||||||
|
* @author Paul Campbell (pcampbell@kemitix.net)
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
class GitTreeReader {
|
||||||
|
|
||||||
|
private final Repository repository;
|
||||||
|
|
||||||
|
private TreeFilter treeFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a stream of entries found on the branch.
|
||||||
|
*
|
||||||
|
* @param branchRef the branch to read
|
||||||
|
* @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 {
|
||||||
|
final TreeWalk treeWalk = new TreeWalk(repository);
|
||||||
|
treeWalk.addTree(new RevWalk(repository).parseCommit(branchRef.getObjectId()).getTree());
|
||||||
|
treeWalk.setRecursive(false);
|
||||||
|
Optional.ofNullable(treeFilter)
|
||||||
|
.ifPresent(treeWalk::setFilter);
|
||||||
|
final Stream.Builder<NamedRevBlob> builder = Stream.builder();
|
||||||
|
while (treeWalk.next()) {
|
||||||
|
builder.add(new NamedRevBlob(
|
||||||
|
treeWalk.getNameString(),
|
||||||
|
new RevWalk(repository).lookupBlob(treeWalk.getObjectId(0))));
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a path filter to limit the stream by.
|
||||||
|
*
|
||||||
|
* @param path the path to filter by
|
||||||
|
* @return the GitTreeReader
|
||||||
|
*/
|
||||||
|
GitTreeReader treeFilter(final String path) {
|
||||||
|
this.treeFilter = PathFilter.create(path);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the key/value pairs read from the tree.
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
class NamedRevBlob {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final RevBlob revBlob;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the blob to a String.
|
||||||
|
*
|
||||||
|
* @return a string
|
||||||
|
* @throws IOException of there was an error reading the blob
|
||||||
|
*/
|
||||||
|
String blobAsString() throws IOException {
|
||||||
|
return new String(repository.open(revBlob.getId(), Constants.OBJ_BLOB).getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -64,7 +64,7 @@ class InitGitDBRepo {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createInitialBranchOnMaster(final Repository repository) throws IOException {
|
private void createInitialBranchOnMaster(final Repository repository) throws IOException {
|
||||||
final GitDBRepo repo = GitDBRepo.in(repository);
|
final GitDBRepo repo = new GitDBRepo(repository);
|
||||||
final ObjectId objectId = repo.insertBlob(new byte[0]);
|
final ObjectId objectId = repo.insertBlob(new byte[0]);
|
||||||
final ObjectId treeId = repo.insertNewTree(IS_GIT_DB, objectId);
|
final ObjectId treeId = repo.insertNewTree(IS_GIT_DB, objectId);
|
||||||
final ObjectId commitId = repo.insertCommit(treeId, INIT_MESSAGE, INIT_USER, INIT_EMAIL, ObjectId.zeroId());
|
final ObjectId commitId = repo.insertCommit(treeId, INIT_MESSAGE, INIT_USER, INIT_EMAIL, ObjectId.zeroId());
|
||||||
|
|
Loading…
Reference in a new issue