package com.beijunyi.parallelgit.utils; import java.io.IOException; import java.util.Iterator; import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.beijunyi.parallelgit.utils.exceptions.NoSuchCacheDirectoryException; import com.beijunyi.parallelgit.utils.exceptions.NoSuchCacheEntryException; import com.beijunyi.parallelgit.utils.exceptions.NoSuchCommitException; import com.beijunyi.parallelgit.utils.io.CacheEntryUpdate; import com.beijunyi.parallelgit.utils.io.CacheIterator; import com.beijunyi.parallelgit.utils.io.CacheNode; import org.eclipse.jgit.dircache.*; import org.eclipse.jgit.lib.*; import static com.beijunyi.parallelgit.utils.TreeUtils.normalizeNodePath; import static org.eclipse.jgit.dircache.DirCacheEntry.STAGE_0; import static org.eclipse.jgit.lib.Constants.encode; import static org.eclipse.jgit.lib.FileMode.*; public final class CacheUtils { @Nonnull public static DirCacheEntry newDirCacheEntry(String path) { return new DirCacheEntry(normalizeNodePath(path)); } @Nonnull public static DirCacheEditor.DeletePath deleteEntry(String path) { return new DirCacheEditor.DeletePath(normalizeNodePath(path)); } @Nonnull public static DirCacheEditor.DeleteTree deleteChildren(String path) { return new DirCacheEditor.DeleteTree(normalizeNodePath(path)); } public static void loadTree(AnyObjectId treeId, DirCache cache, ObjectReader reader) throws IOException { addTree("", treeId, cache, reader); } public static void loadRevision(AnyObjectId revision, DirCache cache, ObjectReader reader) throws IOException { loadTree(CommitUtils.getCommit(revision, reader).getTree(), cache, reader); } @Nonnull public static DirCache forTree(AnyObjectId treeId, ObjectReader reader) throws IOException { DirCache cache = DirCache.newInCore(); loadTree(treeId, cache, reader); return cache; } @Nonnull public static DirCache forTree(AnyObjectId treeId, Repository repo) throws IOException { try(ObjectReader reader = repo.newObjectReader()) { return forTree(treeId, reader); } } @Nonnull public static DirCache forRevision(AnyObjectId revision, ObjectReader reader) throws IOException { DirCache cache = DirCache.newInCore(); loadRevision(revision, cache, reader); return cache; } @Nonnull public static DirCache forRevision(AnyObjectId revision, Repository repo) throws IOException { try(ObjectReader reader = repo.newObjectReader()) { return forRevision(revision, reader); } } @Nonnull public static DirCache forRevision(Ref revision, Repository repo) throws IOException { return forRevision(revision.getObjectId(), repo); } @Nonnull public static DirCache forRevision(String revision, Repository repo) throws IOException { AnyObjectId revisionId = repo.resolve(revision); if(revisionId == null) throw new NoSuchCommitException(revision); return forRevision(revisionId, repo); } @Nonnull public static DirCacheBuilder keepEverything(DirCache cache) { DirCacheBuilder builder = cache.builder(); int count = cache.getEntryCount(); if(count > 0) builder.keep(0, count); return builder; } public static void addTree(String path, AnyObjectId treeId, DirCacheBuilder builder, ObjectReader reader) throws IOException { builder.addTree(encode(normalizeNodePath(path)), STAGE_0, reader, treeId); } public static void addTree(String path, AnyObjectId treeId, DirCache cache, ObjectReader reader) throws IOException { DirCacheBuilder builder = keepEverything(cache); addTree(path, treeId, builder, reader); builder.finish(); } public static void addFile(String path, FileMode mode, AnyObjectId blobId, DirCacheBuilder builder) { DirCacheEntry entry = newDirCacheEntry(path); entry.setFileMode(mode); entry.setObjectId(blobId); builder.add(entry); } public static void addFile(String path, FileMode mode, AnyObjectId blobId, DirCache cache) { DirCacheBuilder builder = keepEverything(cache); addFile(path, mode, blobId, builder); builder.finish(); } public static void addFile(String path, AnyObjectId blobId, DirCache cache) { addFile(path, REGULAR_FILE, blobId, cache); } public static void deleteFile(String path, DirCacheEditor editor) { editor.add(deleteEntry(path)); } public static void deleteFile(String path, DirCache cache) { DirCacheEditor editor = cache.editor(); deleteFile(path, editor); editor.finish(); } public static void deleteDirectory(String path, DirCacheEditor editor) { editor.add(deleteChildren(path)); } public static void deleteDirectory(String path, DirCache cache) { DirCacheEditor editor = cache.editor(); deleteDirectory(path, editor); editor.finish(); } @Nullable public static DirCacheEntry getEntry(String path, DirCache cache) { return cache.getEntry(normalizeNodePath(path)); } public static void updateFile(CacheEntryUpdate update, DirCacheEditor editor) { editor.add(update); } public static void updateFile(CacheEntryUpdate update, DirCache cache) { DirCacheEditor editor = cache.editor(); updateFile(update, editor); editor.finish(); } public static void updateFileBlob(String path, ObjectId blob, FileMode mode, DirCache cache) { updateFile(new CacheEntryUpdate(path).setNewBlob(blob).setNewFileMode(mode), cache); } public static void updateFileBlob(String path, ObjectId blob, DirCache cache) { updateFile(new CacheEntryUpdate(path).setNewBlob(blob), cache); } public static void updateFileMode(String path, FileMode mode, DirCache cache) { updateFile(new CacheEntryUpdate(path).setNewFileMode(mode), cache); } public static int findEntry(String path, DirCache cache) { return cache.findEntry(normalizeNodePath(path)); } public static boolean entryExists(String path, DirCache cache) { return findEntry(path, cache) >= 0; } @Nonnull public static ObjectId getBlob(String path, DirCache cache) throws NoSuchCacheEntryException { return ensureEntry(path, cache).getObjectId(); } @Nonnull public static FileMode getFileMode(String path, DirCache cache) throws NoSuchCacheEntryException { return ensureEntry(path, cache).getFileMode(); } public static boolean isFile(String path, DirCache cache) { return entryExists(path, cache); } public static boolean isSymbolicLink(String path, DirCache cache) { DirCacheEntry entry = getEntry(path, cache); return entry != null && entry.getFileMode() == SYMLINK; } public static boolean isRegularFile(String path, DirCache cache) { DirCacheEntry entry = getEntry(path, cache); return entry != null && entry.getFileMode() == REGULAR_FILE; } public static boolean isExecutableFile(String path, DirCache cache) { DirCacheEntry entry = getEntry(path, cache); return entry != null && entry.getFileMode() == EXECUTABLE_FILE; } public static boolean isRegularOrExecutableFile(String path, DirCache cache) { DirCacheEntry entry = getEntry(path, cache); return entry != null && (entry.getFileMode() == REGULAR_FILE || entry.getFileMode() == EXECUTABLE_FILE); } public static boolean isNonEmptyDirectory(String path, DirCache cache) { path = normalizeNodePath(path) + "/"; if(path.equals("/")) // if it is root return true; int startIndex = cache.findEntry(path); // find the index of this path if(startIndex < 0) // when the path is a directory, its index is negative startIndex = -(startIndex + 1); // indices of children paths inside this directory start from -(index + 1) if(startIndex >= cache.getEntryCount()) // if beyond the total return false; String childPath = cache.getEntry(startIndex).getPathString(); // potentially the first child inside this directory return childPath.startsWith(path); // if it is indeed a child path, then this directory is non-trivial } @Nonnull public static Iterator<CacheNode> iterateDirectory(String path, boolean recursive, DirCache cache) { path = normalizeNodePath(path); DirCacheEntry[] entries = cache.getEntriesWithin(path); if(entries.length == 0) throw new NoSuchCacheDirectoryException("/" + path); return recursive ? new CacheIterator(entries) : new CacheIterator(entries, path); } @Nonnull public static Iterator<CacheNode> iterateDirectory(String path, DirCache cache) { return iterateDirectory(path, false, cache); } @Nonnull public static ObjectId writeTree(DirCache cache, Repository repo) throws IOException { try(ObjectInserter inserter = repo.newObjectInserter()) { ObjectId tree = cache.writeTree(inserter); inserter.flush(); return tree; } } @Nonnull private static DirCacheEntry ensureEntry(String path, DirCache cache) { DirCacheEntry entry = getEntry(path, cache); if(entry == null) throw new NoSuchCacheEntryException("/" + normalizeNodePath(path)); return entry; } }