/* * Copyright (C) 2022, Tencent. * * 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.FileNotFoundException; import java.io.IOException; import java.text.MessageFormat; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.storage.commitgraph.CommitGraphFormatException; import org.eclipse.jgit.internal.storage.commitgraph.CommitGraphLoader; import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph; import org.eclipse.jgit.lib.Constants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Traditional file system for commit-graph. *

* This is the commit-graph file representation for a Git object database. Each * call to {@link FileCommitGraph#get()} will recheck for newer versions. */ public class FileCommitGraph { private final static Logger LOG = LoggerFactory .getLogger(FileCommitGraph.class); private final AtomicReference baseGraph; /** * Initialize a reference to an on-disk commit-graph. * * @param objectsDir * the location of the objects directory. */ FileCommitGraph(File objectsDir) { this.baseGraph = new AtomicReference<>(new GraphSnapshot( new File(objectsDir, Constants.INFO_COMMIT_GRAPH))); } /** * The method will first scan whether the ".git/objects/info/commit-graph" * has been modified, if so, it will re-parse the file, otherwise it will * return the same result as the last time. * * @return commit-graph or null if commit-graph file does not exist or * corrupt. */ CommitGraph get() { GraphSnapshot original = baseGraph.get(); synchronized (baseGraph) { GraphSnapshot o, n; do { o = baseGraph.get(); if (o != original) { // Another thread did the scan for us, while we // were blocked on the monitor above. // return o.getCommitGraph(); } n = o.refresh(); if (n == o) { return n.getCommitGraph(); } } while (!baseGraph.compareAndSet(o, n)); return n.getCommitGraph(); } } private static final class GraphSnapshot { private final File file; private final FileSnapshot snapshot; private final CommitGraph graph; GraphSnapshot(@NonNull File file) { this(file, null, null); } GraphSnapshot(@NonNull File file, FileSnapshot snapshot, CommitGraph graph) { this.file = file; this.snapshot = snapshot; this.graph = graph; } CommitGraph getCommitGraph() { return graph; } GraphSnapshot refresh() { if (graph == null && !file.exists()) { // commit-graph file didn't exist return this; } if (snapshot != null && !snapshot.isModified(file)) { // commit-graph file was not modified return this; } return new GraphSnapshot(file, FileSnapshot.save(file), open(file)); } private static CommitGraph open(File file) { try { return CommitGraphLoader.open(file); } catch (FileNotFoundException noFile) { // ignore if file do not exist return null; } catch (IOException e) { if (e instanceof CommitGraphFormatException) { LOG.warn( MessageFormat.format( JGitText.get().corruptCommitGraph, file), e); } else { LOG.error(MessageFormat.format( JGitText.get().exceptionWhileLoadingCommitGraph, file), e); } return null; } } } }