LooseObjects.java
- /*
- * 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();
- }
- /**
- * Getter for the field <code>directory</code>.
- *
- * @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);
- }
- }