blob: 5172963e2e68c5e16487e32e20462ce4364d798f [file] [log] [blame]
/*
* 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.
* <p>
* 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<GraphSnapshot> baseGraph;
/**
* Initialize a reference to an on-disk commit-graph.
*
* @param objectsDir
* the location of the <code>objects</code> 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;
}
}
}
}