diff --git a/src/main/java/net/kemitix/gitdb/GitDBBranch.java b/src/main/java/net/kemitix/gitdb/GitDBBranch.java index b8eda62..deeb3b4 100644 --- a/src/main/java/net/kemitix/gitdb/GitDBBranch.java +++ b/src/main/java/net/kemitix/gitdb/GitDBBranch.java @@ -46,7 +46,7 @@ public interface GitDBBranch { * @param key the key to place the value under * @param value the value (must be Serializable) * @return an updated branch containing the new key/value - * @throws IOException if there was an error writing the value + * @throws IOException if there was an error writing the key/value */ GitDBBranch put(String key, String value) throws IOException; @@ -55,6 +55,7 @@ public interface GitDBBranch { * * @param key the key to remove * @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); + GitDBBranch remove(String key) throws IOException; } diff --git a/src/main/java/net/kemitix/gitdb/impl/GitDBBranchImpl.java b/src/main/java/net/kemitix/gitdb/impl/GitDBBranchImpl.java index 3b59ca8..65a828b 100644 --- a/src/main/java/net/kemitix/gitdb/impl/GitDBBranchImpl.java +++ b/src/main/java/net/kemitix/gitdb/impl/GitDBBranchImpl.java @@ -85,7 +85,17 @@ class GitDBBranchImpl implements GitDBBranch { } @Override - public GitDBBranch remove(final String key) { + public GitDBBranch remove(final String key) throws IOException { + final Optional newTree = gitDBRepo.removeKey(branchRef, KEY_PREFIX + key); + if (newTree.isPresent()) { + final Ref newBranch = + gitDBRepo.writeCommit( + branchRef, newTree.get(), + commitMessageForRemove(key), + userName, + userEmailAddress); + return select(newBranch, gitDBRepo, userName, userEmailAddress); + } return this; } @@ -93,4 +103,8 @@ class GitDBBranchImpl implements GitDBBranch { return String.format("Add key [%s] = [%s]", key, value); } + private String commitMessageForRemove(final String key) { + return String.format("Remove Key [%s]", key); + } + } diff --git a/src/main/java/net/kemitix/gitdb/impl/GitDBRepo.java b/src/main/java/net/kemitix/gitdb/impl/GitDBRepo.java index 33aaeab..d90a59e 100644 --- a/src/main/java/net/kemitix/gitdb/impl/GitDBRepo.java +++ b/src/main/java/net/kemitix/gitdb/impl/GitDBRepo.java @@ -41,6 +41,7 @@ class GitDBRepo { private final ValueWriter valueWriter; private final KeyWriter keyWriter; private final CommitWriter commitWriter; + private final KeyRemover keyRemover; /** * Creates a new instance of this class. @@ -52,6 +53,7 @@ class GitDBRepo { valueWriter = new ValueWriter(repository); keyWriter = new KeyWriter(repository); commitWriter = new CommitWriter(repository); + keyRemover = new KeyRemover(repository); } /** @@ -157,11 +159,11 @@ class GitDBRepo { /** * Add the key/value to the repo, returning the tree containing the update. * - *

N.B. this creates a tree that has not been committed remains unaware of the update.

+ *

N.B. this creates a tree that has not been committed, the branch remains unaware of the update.

* * @param branchRef the branch to start from * @param key the key to place the value under - * @param value the value (must be Serializable) + * @param value the value * @return the id of the updated tree containing the update * @throws IOException if there was an error writing the value */ @@ -210,4 +212,19 @@ class GitDBRepo { ) throws IOException { return commitWriter.write(treeId, ObjectId.zeroId(), initMessage, initUser, initEmail); } + + /** + * Remove the key from the branch, returning the tree containing the update. + * + *

N.B. this creates a tree that has not been committed, the branch remains unaware of the update.

+ * + * @param branchRef the branch to start from + * @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 + * 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 removeKey(final Ref branchRef, final String key) throws IOException { + return keyRemover.remove(branchRef, key); + } } diff --git a/src/main/java/net/kemitix/gitdb/impl/KeyRemover.java b/src/main/java/net/kemitix/gitdb/impl/KeyRemover.java new file mode 100644 index 0000000..81a8754 --- /dev/null +++ b/src/main/java/net/kemitix/gitdb/impl/KeyRemover.java @@ -0,0 +1,113 @@ +/* + 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.impl; + +import lombok.RequiredArgsConstructor; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.lib.TreeFormatter; + +import java.io.IOException; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; +import java.util.function.Predicate; + +/** + * Remove Key from the Git Repository. + * + * @author Paul Campbell (pcampbell@kemitix.net) + */ +@RequiredArgsConstructor +class KeyRemover { + + private final Repository repository; + + /** + * Sets the boolean to true if the key matches a NamedRevBlob's name. + * + * @param key the key to match + * @param removed the boolean to update + * @return a Consumer + */ + private static Consumer flagIfFound(final String key, final AtomicBoolean removed) { + return nvb -> { + if (nvb.getName().equals(key)) { + removed.set(true); + } + }; + } + + /** + * Filter to exclude named blobs where the name matches the key. + * + * @param key the key to match + * @return a Predicate + */ + private static Predicate isNotKey(final String key) { + return item -> !item.getName().equals(key); + } + + /** + * Adds the named value blob to the tree formatter. + * + * @param treeFormatter the tree formatter to add to + * @return a Consumer + */ + private static Consumer addToTree(final TreeFormatter treeFormatter) { + 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 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. + * + * @param treeFormatter the formatter containing the proposed tree's data. + * @return the name of the tree object. + * @throws IOException the object could not be stored. + */ + private ObjectId insertTree(final TreeFormatter treeFormatter) throws IOException { + return repository.getObjectDatabase().newInserter().insert(treeFormatter); + } +} diff --git a/src/test/java/net/kemitix/gitdb/test/GitDBTest.java b/src/test/java/net/kemitix/gitdb/test/GitDBTest.java index 99886fe..a876aa8 100644 --- a/src/test/java/net/kemitix/gitdb/test/GitDBTest.java +++ b/src/test/java/net/kemitix/gitdb/test/GitDBTest.java @@ -249,6 +249,16 @@ class GitDBTest implements WithAssertions { } // When removing a key that does exist then a GitDbBranch is returned + @Test + void removeKey_whenExists_thenReturnUpdatedBranch() throws IOException { + //given + final GitDBBranch originalBranch = gitDBBranch().put("key-name", "value"); + //when + final GitDBBranch updatedBranch = originalBranch.remove("key-name"); + //then + assertThat(updatedBranch).isNotSameAs(originalBranch); + } + // When removing a key that does exist then original GitDbBranch can still find it // When removing a key that does exist then the updated GitDbBranch can't find it