]> source.dussan.org Git - jgit.git/commitdiff
Split out loose object handling from ObjectDirectory 60/122060/6
authorMartin Fick <mfick@codeaurora.org>
Thu, 26 Apr 2018 16:53:57 +0000 (10:53 -0600)
committerMatthias Sohn <matthias.sohn@sap.com>
Mon, 14 Dec 2020 23:18:32 +0000 (00:18 +0100)
The ObjectDirectory class manages the interactions for the entire object
database, this includes loose objects, packfiles, alternates, and
shallow commits. To help reduce the complexity of this class, abstract
some of the loose object specific details into a class which understands
just this, leaving the ObjectDirectory to focus more on the interactions
between the different mechanisms.

Change-Id: I39f3a74d6308f042a2a2baa57769f4acde5ba5e0
Signed-off-by: Martin Fick <mfick@codeaurora.org>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java [new file with mode: 0644]
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/ObjectDirectory.java

diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/LooseObjects.java
new file mode 100644 (file)
index 0000000..fefac36
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2009, Google Inc. and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+package org.eclipse.jgit.internal.storage.file;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.StandardCopyOption;
+import java.util.Set;
+
+import org.eclipse.jgit.internal.storage.file.FileObjectDatabase.InsertLooseObjectResult;
+import org.eclipse.jgit.lib.AbbreviatedObjectId;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.util.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Traditional file system based loose objects handler.
+ * <p>
+ * This is the loose object representation for a Git object database,
+ * where objects are stored loose by hashing them into directories by their
+ * {@link org.eclipse.jgit.lib.ObjectId}.
+ */
+class LooseObjects {
+       private static final Logger LOG = LoggerFactory
+                       .getLogger(LooseObjects.class);
+
+       private final File directory;
+
+       private final UnpackedObjectCache unpackedObjectCache;
+
+       /**
+        * Initialize a reference to an on-disk object directory.
+        *
+        * @param dir
+        *            the location of the <code>objects</code> directory.
+        */
+       LooseObjects(File dir) {
+               directory = dir;
+               unpackedObjectCache = new UnpackedObjectCache();
+       }
+
+       /**
+        * <p>Getter for the field <code>directory</code>.</p>
+        *
+        * @return the location of the <code>objects</code> directory.
+        */
+       File getDirectory() {
+               return directory;
+       }
+
+       void create() throws IOException {
+               FileUtils.mkdirs(directory);
+       }
+
+       void close() {
+               unpackedObjectCache.clear();
+       }
+
+       /** {@inheritDoc} */
+       @Override
+       public String toString() {
+               return "LooseObjects[" + directory + "]"; //$NON-NLS-1$ //$NON-NLS-2$
+       }
+
+       boolean hasCached(AnyObjectId id) {
+               return unpackedObjectCache.isUnpacked(id);
+       }
+
+       /**
+        * Does the requested object exist as a loose object?
+        *
+        * @param objectId
+        *            identity of the object to test for existence of.
+        * @return {@code true} if the specified object is stored as a loose object.
+        */
+       boolean has(AnyObjectId objectId) {
+               return fileFor(objectId).exists();
+       }
+
+       /**
+        * Find objects matching the prefix abbreviation.
+        *
+        * @param matches
+        *            set to add any located ObjectIds to. This is an output
+        *            parameter.
+        * @param id
+        *            prefix to search for.
+        * @param matchLimit
+        *            maximum number of results to return. At most this many
+        *            ObjectIds should be added to matches before returning.
+        * @return {@code true} if the matches were exhausted before reaching
+        *            {@code maxLimit}.
+        */
+       boolean resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
+                       int matchLimit) {
+               String fanOut = id.name().substring(0, 2);
+               String[] entries = new File(directory, fanOut).list();
+               if (entries != null) {
+                       for (String e : entries) {
+                               if (e.length() != Constants.OBJECT_ID_STRING_LENGTH - 2)
+                                       continue;
+                               try {
+                                       ObjectId entId = ObjectId.fromString(fanOut + e);
+                                       if (id.prefixCompare(entId) == 0)
+                                               matches.add(entId);
+                               } catch (IllegalArgumentException notId) {
+                                       continue;
+                               }
+                               if (matches.size() > matchLimit)
+                                       return false;
+                       }
+               }
+               return true;
+       }
+
+       ObjectLoader open(WindowCursor curs, AnyObjectId id)
+                       throws IOException {
+               File path = fileFor(id);
+               try (FileInputStream in = new FileInputStream(path)) {
+                       unpackedObjectCache.add(id);
+                       return UnpackedObject.open(in, path, id, curs);
+               } catch (FileNotFoundException noFile) {
+                       if (path.exists()) {
+                               throw noFile;
+                       }
+                       unpackedObjectCache.remove(id);
+                       return null;
+               }
+       }
+
+       long getSize(WindowCursor curs, AnyObjectId id)
+                       throws IOException {
+               File f = fileFor(id);
+               try (FileInputStream in = new FileInputStream(f)) {
+                       unpackedObjectCache.add(id);
+                       return UnpackedObject.getSize(in, id, curs);
+               } catch (FileNotFoundException noFile) {
+                       if (f.exists()) {
+                               throw noFile;
+                       }
+                       unpackedObjectCache.remove(id);
+                       return -1;
+               }
+       }
+
+       InsertLooseObjectResult insert(File tmp, ObjectId id) throws IOException {
+               final File dst = fileFor(id);
+               if (dst.exists()) {
+                       // We want to be extra careful and avoid replacing an object
+                       // that already exists. We can't be sure renameTo() would
+                       // fail on all platforms if dst exists, so we check first.
+                       //
+                       FileUtils.delete(tmp, FileUtils.RETRY);
+                       return InsertLooseObjectResult.EXISTS_LOOSE;
+               }
+
+               try {
+                       return tryMove(tmp, dst, id);
+               } catch (NoSuchFileException e) {
+                       // It's possible the directory doesn't exist yet as the object
+                       // directories are always lazily created. Note that we try the
+                       // rename/move first as the directory likely does exist.
+                       //
+                       // Create the directory.
+                       //
+                       FileUtils.mkdir(dst.getParentFile(), true);
+               } catch (IOException e) {
+                       // Any other IO error is considered a failure.
+                       //
+                       LOG.error(e.getMessage(), e);
+                       FileUtils.delete(tmp, FileUtils.RETRY);
+                       return InsertLooseObjectResult.FAILURE;
+               }
+
+               try {
+                       return tryMove(tmp, dst, id);
+               } catch (IOException e) {
+                       // The object failed to be renamed into its proper location and
+                       // it doesn't exist in the repository either. We really don't
+                       // know what went wrong, so fail.
+                       //
+                       LOG.error(e.getMessage(), e);
+                       FileUtils.delete(tmp, FileUtils.RETRY);
+                       return InsertLooseObjectResult.FAILURE;
+               }
+       }
+
+       private InsertLooseObjectResult tryMove(File tmp, File dst,
+                       ObjectId id)
+                       throws IOException {
+               Files.move(FileUtils.toPath(tmp), FileUtils.toPath(dst),
+                               StandardCopyOption.ATOMIC_MOVE);
+               dst.setReadOnly();
+               unpackedObjectCache.add(id);
+               return InsertLooseObjectResult.INSERTED;
+       }
+
+       /**
+        * Compute the location of a loose object file.
+        *
+        * @param objectId
+        *            identity of the object to get the File location for.
+        * @return {@link java.io.File} location of the specified loose object.
+        */
+       File fileFor(AnyObjectId objectId) {
+               String n = objectId.name();
+               String d = n.substring(0, 2);
+               String f = n.substring(2);
+               return new File(new File(getDirectory(), d), f);
+       }
+}
index b4a336f7bdf6655f12f401e88d3e387f3d4e093e..4a40db68ddd9923afbc0dde1a08bfc983fc67edb 100644 (file)
@@ -16,12 +16,9 @@ import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
 
 import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
-import java.nio.file.StandardCopyOption;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -47,8 +44,6 @@ import org.eclipse.jgit.lib.RepositoryCache;
 import org.eclipse.jgit.lib.RepositoryCache.FileKey;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.FileUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Traditional file system based {@link org.eclipse.jgit.lib.ObjectDatabase}.
@@ -69,9 +64,6 @@ import org.slf4j.LoggerFactory;
  * considered.
  */
 public class ObjectDirectory extends FileObjectDatabase {
-       private static final Logger LOG = LoggerFactory
-                       .getLogger(ObjectDirectory.class);
-
        /** Maximum number of candidates offered as resolutions of abbreviation. */
        private static final int RESOLVE_ABBREV_LIMIT = 256;
 
@@ -83,6 +75,8 @@ public class ObjectDirectory extends FileObjectDatabase {
 
        private final File infoDirectory;
 
+       private final LooseObjects loose;
+
        private final PackDirectory packed;
 
        private final File preservedDirectory;
@@ -93,8 +87,6 @@ public class ObjectDirectory extends FileObjectDatabase {
 
        private final AtomicReference<AlternateHandle[]> alternates;
 
-       private final UnpackedObjectCache unpackedObjectCache;
-
        private final File shallowFile;
 
        private FileSnapshot shallowFileSnapshot = FileSnapshot.DIRTY;
@@ -127,8 +119,8 @@ public class ObjectDirectory extends FileObjectDatabase {
                File packDirectory = new File(objects, "pack"); //$NON-NLS-1$
                preservedDirectory = new File(packDirectory, "preserved"); //$NON-NLS-1$
                alternatesFile = new File(objects, Constants.INFO_ALTERNATES);
+               loose = new LooseObjects(objects);
                packed = new PackDirectory(config, packDirectory);
-               unpackedObjectCache = new UnpackedObjectCache();
                this.fs = fs;
                this.shallowFile = shallowFile;
 
@@ -146,7 +138,7 @@ public class ObjectDirectory extends FileObjectDatabase {
        /** {@inheritDoc} */
        @Override
        public final File getDirectory() {
-               return objects;
+               return loose.getDirectory();
        }
 
        /**
@@ -176,7 +168,7 @@ public class ObjectDirectory extends FileObjectDatabase {
        /** {@inheritDoc} */
        @Override
        public void create() throws IOException {
-               FileUtils.mkdirs(objects);
+               loose.create();
                FileUtils.mkdir(infoDirectory);
                packed.create();
        }
@@ -200,7 +192,7 @@ public class ObjectDirectory extends FileObjectDatabase {
        /** {@inheritDoc} */
        @Override
        public void close() {
-               unpackedObjectCache.clear();
+               loose.close();
 
                packed.close();
 
@@ -257,7 +249,7 @@ public class ObjectDirectory extends FileObjectDatabase {
        /** {@inheritDoc} */
        @Override
        public boolean has(AnyObjectId objectId) {
-               return unpackedObjectCache.isUnpacked(objectId)
+               return loose.hasCached(objectId)
                                || hasPackedInSelfOrAlternate(objectId, null)
                                || hasLooseInSelfOrAlternate(objectId, null);
        }
@@ -280,7 +272,7 @@ public class ObjectDirectory extends FileObjectDatabase {
 
        private boolean hasLooseInSelfOrAlternate(AnyObjectId objectId,
                        Set<AlternateHandle.Id> skips) {
-               if (fileFor(objectId).exists()) {
+               if (loose.has(objectId)) {
                        return true;
                }
                skips = addMe(skips);
@@ -310,23 +302,8 @@ public class ObjectDirectory extends FileObjectDatabase {
                if (!packed.resolve(matches, id, RESOLVE_ABBREV_LIMIT))
                        return;
 
-               String fanOut = id.name().substring(0, 2);
-               String[] entries = new File(getDirectory(), fanOut).list();
-               if (entries != null) {
-                       for (String e : entries) {
-                               if (e.length() != Constants.OBJECT_ID_STRING_LENGTH - 2)
-                                       continue;
-                               try {
-                                       ObjectId entId = ObjectId.fromString(fanOut + e);
-                                       if (id.prefixCompare(entId) == 0)
-                                               matches.add(entId);
-                               } catch (IllegalArgumentException notId) {
-                                       continue;
-                               }
-                               if (matches.size() > RESOLVE_ABBREV_LIMIT)
-                                       return;
-                       }
-               }
+               if (!loose.resolve(matches, id, RESOLVE_ABBREV_LIMIT))
+                       return;
 
                skips = addMe(skips);
                for (AlternateHandle alt : myAlternates()) {
@@ -342,7 +319,7 @@ public class ObjectDirectory extends FileObjectDatabase {
        @Override
        ObjectLoader openObject(WindowCursor curs, AnyObjectId objectId)
                        throws IOException {
-               if (unpackedObjectCache.isUnpacked(objectId)) {
+               if (loose.hasCached(objectId)) {
                        ObjectLoader ldr = openLooseObject(curs, objectId);
                        if (ldr != null) {
                                return ldr;
@@ -399,24 +376,14 @@ public class ObjectDirectory extends FileObjectDatabase {
        @Override
        ObjectLoader openLooseObject(WindowCursor curs, AnyObjectId id)
                        throws IOException {
-               File path = fileFor(id);
-               try (FileInputStream in = new FileInputStream(path)) {
-                       unpackedObjectCache.add(id);
-                       return UnpackedObject.open(in, path, id, curs);
-               } catch (FileNotFoundException noFile) {
-                       if (path.exists()) {
-                               throw noFile;
-                       }
-                       unpackedObjectCache.remove(id);
-                       return null;
-               }
+               return loose.open(curs, id);
        }
 
        @Override
        long getObjectSize(WindowCursor curs, AnyObjectId id)
                        throws IOException {
-               if (unpackedObjectCache.isUnpacked(id)) {
-                       long len = getLooseObjectSize(curs, id);
+               if (loose.hasCached(id)) {
+                       long len = loose.getSize(curs, id);
                        if (0 <= len) {
                                return len;
                        }
@@ -448,7 +415,7 @@ public class ObjectDirectory extends FileObjectDatabase {
 
        private long getLooseSizeFromSelfOrAlternate(WindowCursor curs,
                        AnyObjectId id, Set<AlternateHandle.Id> skips) throws IOException {
-               long len = getLooseObjectSize(curs, id);
+               long len = loose.getSize(curs, id);
                if (0 <= len) {
                        return len;
                }
@@ -464,21 +431,6 @@ public class ObjectDirectory extends FileObjectDatabase {
                return -1;
        }
 
-       private long getLooseObjectSize(WindowCursor curs, AnyObjectId id)
-                       throws IOException {
-               File f = fileFor(id);
-               try (FileInputStream in = new FileInputStream(f)) {
-                       unpackedObjectCache.add(id);
-                       return UnpackedObject.getSize(in, id, curs);
-               } catch (FileNotFoundException noFile) {
-                       if (f.exists()) {
-                               throw noFile;
-                       }
-                       unpackedObjectCache.remove(id);
-                       return -1;
-               }
-       }
-
        @Override
        void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
                        WindowCursor curs) throws IOException {
@@ -502,7 +454,7 @@ public class ObjectDirectory extends FileObjectDatabase {
                        boolean createDuplicate) throws IOException {
                // If the object is already in the repository, remove temporary file.
                //
-               if (unpackedObjectCache.isUnpacked(id)) {
+               if (loose.hasCached(id)) {
                        FileUtils.delete(tmp, FileUtils.RETRY);
                        return InsertLooseObjectResult.EXISTS_LOOSE;
                }
@@ -510,56 +462,7 @@ public class ObjectDirectory extends FileObjectDatabase {
                        FileUtils.delete(tmp, FileUtils.RETRY);
                        return InsertLooseObjectResult.EXISTS_PACKED;
                }
-
-               final File dst = fileFor(id);
-               if (dst.exists()) {
-                       // We want to be extra careful and avoid replacing an object
-                       // that already exists. We can't be sure renameTo() would
-                       // fail on all platforms if dst exists, so we check first.
-                       //
-                       FileUtils.delete(tmp, FileUtils.RETRY);
-                       return InsertLooseObjectResult.EXISTS_LOOSE;
-               }
-
-               try {
-                       return tryMove(tmp, dst, id);
-               } catch (NoSuchFileException e) {
-                       // It's possible the directory doesn't exist yet as the object
-                       // directories are always lazily created. Note that we try the
-                       // rename/move first as the directory likely does exist.
-                       //
-                       // Create the directory.
-                       //
-                       FileUtils.mkdir(dst.getParentFile(), true);
-               } catch (IOException e) {
-                       // Any other IO error is considered a failure.
-                       //
-                       LOG.error(e.getMessage(), e);
-                       FileUtils.delete(tmp, FileUtils.RETRY);
-                       return InsertLooseObjectResult.FAILURE;
-               }
-
-               try {
-                       return tryMove(tmp, dst, id);
-               } catch (IOException e) {
-                       // The object failed to be renamed into its proper location and
-                       // it doesn't exist in the repository either. We really don't
-                       // know what went wrong, so fail.
-                       //
-                       LOG.error(e.getMessage(), e);
-                       FileUtils.delete(tmp, FileUtils.RETRY);
-                       return InsertLooseObjectResult.FAILURE;
-               }
-       }
-
-       private InsertLooseObjectResult tryMove(File tmp, File dst,
-                       ObjectId id)
-                       throws IOException {
-               Files.move(FileUtils.toPath(tmp), FileUtils.toPath(dst),
-                               StandardCopyOption.ATOMIC_MOVE);
-               dst.setReadOnly();
-               unpackedObjectCache.add(id);
-               return InsertLooseObjectResult.INSERTED;
+               return loose.insert(tmp, id);
        }
 
        @Override
@@ -676,16 +579,11 @@ public class ObjectDirectory extends FileObjectDatabase {
        }
 
        /**
-        * {@inheritDoc}
-        * <p>
         * Compute the location of a loose object file.
         */
        @Override
        public File fileFor(AnyObjectId objectId) {
-               String n = objectId.name();
-               String d = n.substring(0, 2);
-               String f = n.substring(2);
-               return new File(new File(getDirectory(), d), f);
+               return loose.fileFor(objectId);
        }
 
        static class AlternateHandle {