CommitGraph: implement commit-graph writer
Teach JGit to write a commit-graph formatted file by walking commit graph from specified commit objects. See: https://git-scm.com/docs/commit-graph-format/2.21.0 Bug: 574368 Change-Id: I34f9f28f8729080c275f86215ebf30b2d05af41d Signed-off-by: kylezhao <kylezhao@tencent.com>
This commit is contained in:
parent
1d5a6c77a6
commit
cf70e7cbe4
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021, 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.commitgraph;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.internal.storage.file.FileRepository;
|
||||||
|
import org.eclipse.jgit.junit.RepositoryTestCase;
|
||||||
|
import org.eclipse.jgit.junit.TestRepository;
|
||||||
|
import org.eclipse.jgit.lib.NullProgressMonitor;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
|
import org.eclipse.jgit.util.NB;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class CommitGraphWriterTest extends RepositoryTestCase {
|
||||||
|
|
||||||
|
private TestRepository<FileRepository> tr;
|
||||||
|
|
||||||
|
private ByteArrayOutputStream os;
|
||||||
|
|
||||||
|
private CommitGraphWriter writer;
|
||||||
|
|
||||||
|
private RevWalk walk;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
os = new ByteArrayOutputStream();
|
||||||
|
tr = new TestRepository<>(db, new RevWalk(db), mockSystemReader);
|
||||||
|
walk = new RevWalk(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriteInEmptyRepo() throws Exception {
|
||||||
|
NullProgressMonitor m = NullProgressMonitor.INSTANCE;
|
||||||
|
writer = new CommitGraphWriter(
|
||||||
|
GraphCommits.fromWalk(m, Collections.emptySet(), walk));
|
||||||
|
writer.write(m, os);
|
||||||
|
assertEquals(0, os.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriterWithExtraEdgeList() throws Exception {
|
||||||
|
RevCommit root = commit();
|
||||||
|
RevCommit a = commit(root);
|
||||||
|
RevCommit b = commit(root);
|
||||||
|
RevCommit c = commit(root);
|
||||||
|
RevCommit tip = commit(a, b, c);
|
||||||
|
|
||||||
|
Set<ObjectId> wants = Collections.singleton(tip);
|
||||||
|
NullProgressMonitor m = NullProgressMonitor.INSTANCE;
|
||||||
|
GraphCommits graphCommits = GraphCommits.fromWalk(m, wants, walk);
|
||||||
|
writer = new CommitGraphWriter(graphCommits);
|
||||||
|
writer.write(m, os);
|
||||||
|
|
||||||
|
assertEquals(5, graphCommits.size());
|
||||||
|
byte[] data = os.toByteArray();
|
||||||
|
assertTrue(data.length > 0);
|
||||||
|
byte[] headers = new byte[8];
|
||||||
|
System.arraycopy(data, 0, headers, 0, 8);
|
||||||
|
assertArrayEquals(new byte[] {'C', 'G', 'P', 'H', 1, 1, 4, 0}, headers);
|
||||||
|
assertEquals(CommitGraphConstants.CHUNK_ID_OID_FANOUT, NB.decodeInt32(data, 8));
|
||||||
|
assertEquals(CommitGraphConstants.CHUNK_ID_OID_LOOKUP, NB.decodeInt32(data, 20));
|
||||||
|
assertEquals(CommitGraphConstants.CHUNK_ID_COMMIT_DATA, NB.decodeInt32(data, 32));
|
||||||
|
assertEquals(CommitGraphConstants.CHUNK_ID_EXTRA_EDGE_LIST, NB.decodeInt32(data, 44));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWriterWithoutExtraEdgeList() throws Exception {
|
||||||
|
RevCommit root = commit();
|
||||||
|
RevCommit a = commit(root);
|
||||||
|
RevCommit b = commit(root);
|
||||||
|
RevCommit tip = commit(a, b);
|
||||||
|
|
||||||
|
Set<ObjectId> wants = Collections.singleton(tip);
|
||||||
|
NullProgressMonitor m = NullProgressMonitor.INSTANCE;
|
||||||
|
GraphCommits graphCommits = GraphCommits.fromWalk(m, wants, walk);
|
||||||
|
writer = new CommitGraphWriter(graphCommits);
|
||||||
|
writer.write(m, os);
|
||||||
|
|
||||||
|
assertEquals(4, graphCommits.size());
|
||||||
|
byte[] data = os.toByteArray();
|
||||||
|
assertTrue(data.length > 0);
|
||||||
|
byte[] headers = new byte[8];
|
||||||
|
System.arraycopy(data, 0, headers, 0, 8);
|
||||||
|
assertArrayEquals(new byte[] {'C', 'G', 'P', 'H', 1, 1, 3, 0}, headers);
|
||||||
|
assertEquals(CommitGraphConstants.CHUNK_ID_OID_FANOUT, NB.decodeInt32(data, 8));
|
||||||
|
assertEquals(CommitGraphConstants.CHUNK_ID_OID_LOOKUP, NB.decodeInt32(data, 20));
|
||||||
|
assertEquals(CommitGraphConstants.CHUNK_ID_COMMIT_DATA, NB.decodeInt32(data, 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
RevCommit commit(RevCommit... parents) throws Exception {
|
||||||
|
return tr.commit(parents);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021, 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.commitgraph;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.errors.MissingObjectException;
|
||||||
|
import org.eclipse.jgit.internal.storage.file.FileRepository;
|
||||||
|
import org.eclipse.jgit.junit.RepositoryTestCase;
|
||||||
|
import org.eclipse.jgit.junit.TestRepository;
|
||||||
|
import org.eclipse.jgit.lib.NullProgressMonitor;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class GraphCommitsTest extends RepositoryTestCase {
|
||||||
|
|
||||||
|
private TestRepository<FileRepository> tr;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
tr = new TestRepository<>(db, new RevWalk(db), mockSystemReader);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmptyRepo() throws Exception {
|
||||||
|
try (RevWalk rw = new RevWalk(db)) {
|
||||||
|
GraphCommits commits = GraphCommits.fromWalk(
|
||||||
|
NullProgressMonitor.INSTANCE, Collections.emptySet(), rw);
|
||||||
|
assertEquals(0, commits.size());
|
||||||
|
assertEquals(0, commits.getExtraEdgeCnt());
|
||||||
|
assertFalse(commits.iterator().hasNext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRepoWithoutExtraEdges() throws Exception {
|
||||||
|
try (RevWalk rw = new RevWalk(db)) {
|
||||||
|
RevCommit root = commit();
|
||||||
|
RevCommit a = commit(root);
|
||||||
|
RevCommit b = commit(root);
|
||||||
|
RevCommit tip = commit(a, b);
|
||||||
|
GraphCommits commits = GraphCommits.fromWalk(
|
||||||
|
NullProgressMonitor.INSTANCE, Collections.singleton(tip),
|
||||||
|
rw);
|
||||||
|
assertEquals(4, commits.size());
|
||||||
|
assertEquals(0, commits.getExtraEdgeCnt());
|
||||||
|
verifyOrderOfLookup(List.of(root, a, b, tip), commits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRepoWithExtraEdges() throws Exception {
|
||||||
|
try (RevWalk rw = new RevWalk(db)) {
|
||||||
|
RevCommit root = commit();
|
||||||
|
RevCommit a = commit(root);
|
||||||
|
RevCommit b = commit(root);
|
||||||
|
RevCommit c = commit(root);
|
||||||
|
RevCommit tip = commit(a, b, c);
|
||||||
|
GraphCommits commits = GraphCommits.fromWalk(
|
||||||
|
NullProgressMonitor.INSTANCE, Collections.singleton(tip),
|
||||||
|
rw);
|
||||||
|
assertEquals(5, commits.size());
|
||||||
|
assertEquals(2, commits.getExtraEdgeCnt());
|
||||||
|
verifyOrderOfLookup(List.of(root, a, b, c, tip), commits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCommitNotInLookup() throws Exception {
|
||||||
|
try (RevWalk rw = new RevWalk(db)) {
|
||||||
|
RevCommit a = commit();
|
||||||
|
RevCommit b = commit(a);
|
||||||
|
GraphCommits commits = GraphCommits.fromWalk(
|
||||||
|
NullProgressMonitor.INSTANCE, Collections.singleton(a), rw);
|
||||||
|
assertEquals(1, commits.size());
|
||||||
|
assertEquals(0, commits.getExtraEdgeCnt());
|
||||||
|
Iterator<RevCommit> iterator = commits.iterator();
|
||||||
|
assertEquals(a, iterator.next());
|
||||||
|
assertFalse(iterator.hasNext());
|
||||||
|
assertThrows(MissingObjectException.class,
|
||||||
|
() -> commits.getOidPosition(b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyOrderOfLookup(List<RevCommit> commits,
|
||||||
|
GraphCommits graphCommits) {
|
||||||
|
commits = new ArrayList<>(commits);
|
||||||
|
Collections.sort(commits);
|
||||||
|
Iterator<RevCommit> expected = commits.iterator();
|
||||||
|
Iterator<RevCommit> actual = graphCommits.iterator();
|
||||||
|
while (expected.hasNext()) {
|
||||||
|
assertEquals(expected.next(), actual.next());
|
||||||
|
}
|
||||||
|
assertFalse(actual.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
RevCommit commit(RevCommit... parents) throws Exception {
|
||||||
|
return tr.commit(parents);
|
||||||
|
}
|
||||||
|
}
|
|
@ -143,11 +143,13 @@ collisionOn=Collision on {0}
|
||||||
commandClosedStderrButDidntExit=Command {0} closed stderr stream but didn''t exit within timeout {1} seconds
|
commandClosedStderrButDidntExit=Command {0} closed stderr stream but didn''t exit within timeout {1} seconds
|
||||||
commandRejectedByHook=Rejected by "{0}" hook.\n{1}
|
commandRejectedByHook=Rejected by "{0}" hook.\n{1}
|
||||||
commandWasCalledInTheWrongState=Command {0} was called in the wrong state
|
commandWasCalledInTheWrongState=Command {0} was called in the wrong state
|
||||||
|
commitGraphWritingCancelled=commit-graph writing was canceled
|
||||||
commitMessageNotSpecified=commit message not specified
|
commitMessageNotSpecified=commit message not specified
|
||||||
commitOnRepoWithoutHEADCurrentlyNotSupported=Commit on repo without HEAD currently not supported
|
commitOnRepoWithoutHEADCurrentlyNotSupported=Commit on repo without HEAD currently not supported
|
||||||
commitAmendOnInitialNotPossible=Amending is not possible on initial commit.
|
commitAmendOnInitialNotPossible=Amending is not possible on initial commit.
|
||||||
commitsHaveAlreadyBeenMarkedAsStart=Commits have already been marked as walk starts.
|
commitsHaveAlreadyBeenMarkedAsStart=Commits have already been marked as walk starts.
|
||||||
compressingObjects=Compressing objects
|
compressingObjects=Compressing objects
|
||||||
|
computingCommitGeneration=Computing commit-graph generation numbers
|
||||||
configSubsectionContainsNewline=config subsection name contains newline
|
configSubsectionContainsNewline=config subsection name contains newline
|
||||||
configSubsectionContainsNullByte=config subsection name contains byte 0x00
|
configSubsectionContainsNullByte=config subsection name contains byte 0x00
|
||||||
configValueContainsNullByte=config value contains byte 0x00
|
configValueContainsNullByte=config value contains byte 0x00
|
||||||
|
@ -328,6 +330,7 @@ fileModeNotSetForPath=FileMode not set for path {0}
|
||||||
filterExecutionFailed=Execution of filter command ''{0}'' on file ''{1}'' failed
|
filterExecutionFailed=Execution of filter command ''{0}'' on file ''{1}'' failed
|
||||||
filterExecutionFailedRc=Execution of filter command ''{0}'' on file ''{1}'' failed with return code ''{2}'', message on stderr: ''{3}''
|
filterExecutionFailedRc=Execution of filter command ''{0}'' on file ''{1}'' failed with return code ''{2}'', message on stderr: ''{3}''
|
||||||
filterRequiresCapability=filter requires server to advertise that capability
|
filterRequiresCapability=filter requires server to advertise that capability
|
||||||
|
findingCommitsForCommitGraph=Finding commits for commit-graph
|
||||||
findingGarbage=Finding garbage
|
findingGarbage=Finding garbage
|
||||||
flagIsDisposed={0} is disposed.
|
flagIsDisposed={0} is disposed.
|
||||||
flagNotFromThis={0} not from this.
|
flagNotFromThis={0} not from this.
|
||||||
|
@ -839,6 +842,7 @@ writerAlreadyInitialized=Writer already initialized
|
||||||
writeTimedOut=Write timed out after {0} ms
|
writeTimedOut=Write timed out after {0} ms
|
||||||
writingNotPermitted=Writing not permitted
|
writingNotPermitted=Writing not permitted
|
||||||
writingNotSupported=Writing {0} not supported.
|
writingNotSupported=Writing {0} not supported.
|
||||||
|
writingOutCommitGraph=Writing out commit-graph in {0} passes
|
||||||
writingObjects=Writing objects
|
writingObjects=Writing objects
|
||||||
wrongDecompressedLength=wrong decompressed length
|
wrongDecompressedLength=wrong decompressed length
|
||||||
wrongRepositoryState=Wrong Repository State: {0}
|
wrongRepositoryState=Wrong Repository State: {0}
|
||||||
|
|
|
@ -171,11 +171,13 @@ public static JGitText get() {
|
||||||
/***/ public String commandClosedStderrButDidntExit;
|
/***/ public String commandClosedStderrButDidntExit;
|
||||||
/***/ public String commandRejectedByHook;
|
/***/ public String commandRejectedByHook;
|
||||||
/***/ public String commandWasCalledInTheWrongState;
|
/***/ public String commandWasCalledInTheWrongState;
|
||||||
|
/***/ public String commitGraphWritingCancelled;
|
||||||
/***/ public String commitMessageNotSpecified;
|
/***/ public String commitMessageNotSpecified;
|
||||||
/***/ public String commitOnRepoWithoutHEADCurrentlyNotSupported;
|
/***/ public String commitOnRepoWithoutHEADCurrentlyNotSupported;
|
||||||
/***/ public String commitAmendOnInitialNotPossible;
|
/***/ public String commitAmendOnInitialNotPossible;
|
||||||
/***/ public String commitsHaveAlreadyBeenMarkedAsStart;
|
/***/ public String commitsHaveAlreadyBeenMarkedAsStart;
|
||||||
/***/ public String compressingObjects;
|
/***/ public String compressingObjects;
|
||||||
|
/***/ public String computingCommitGeneration;
|
||||||
/***/ public String configSubsectionContainsNewline;
|
/***/ public String configSubsectionContainsNewline;
|
||||||
/***/ public String configSubsectionContainsNullByte;
|
/***/ public String configSubsectionContainsNullByte;
|
||||||
/***/ public String configValueContainsNullByte;
|
/***/ public String configValueContainsNullByte;
|
||||||
|
@ -356,6 +358,7 @@ public static JGitText get() {
|
||||||
/***/ public String filterExecutionFailed;
|
/***/ public String filterExecutionFailed;
|
||||||
/***/ public String filterExecutionFailedRc;
|
/***/ public String filterExecutionFailedRc;
|
||||||
/***/ public String filterRequiresCapability;
|
/***/ public String filterRequiresCapability;
|
||||||
|
/***/ public String findingCommitsForCommitGraph;
|
||||||
/***/ public String findingGarbage;
|
/***/ public String findingGarbage;
|
||||||
/***/ public String flagIsDisposed;
|
/***/ public String flagIsDisposed;
|
||||||
/***/ public String flagNotFromThis;
|
/***/ public String flagNotFromThis;
|
||||||
|
@ -867,6 +870,7 @@ public static JGitText get() {
|
||||||
/***/ public String writeTimedOut;
|
/***/ public String writeTimedOut;
|
||||||
/***/ public String writingNotPermitted;
|
/***/ public String writingNotPermitted;
|
||||||
/***/ public String writingNotSupported;
|
/***/ public String writingNotSupported;
|
||||||
|
/***/ public String writingOutCommitGraph;
|
||||||
/***/ public String writingObjects;
|
/***/ public String writingObjects;
|
||||||
/***/ public String wrongDecompressedLength;
|
/***/ public String wrongDecompressedLength;
|
||||||
/***/ public String wrongRepositoryState;
|
/***/ public String wrongRepositoryState;
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021, 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.commitgraph;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants relating to commit-graph.
|
||||||
|
*/
|
||||||
|
class CommitGraphConstants {
|
||||||
|
|
||||||
|
static final int COMMIT_GRAPH_MAGIC = 0x43475048; /* "CGPH" */
|
||||||
|
|
||||||
|
static final int CHUNK_ID_OID_FANOUT = 0x4f494446; /* "OIDF" */
|
||||||
|
|
||||||
|
static final int CHUNK_ID_OID_LOOKUP = 0x4f49444c; /* "OIDL" */
|
||||||
|
|
||||||
|
static final int CHUNK_ID_COMMIT_DATA = 0x43444154; /* "CDAT" */
|
||||||
|
|
||||||
|
static final int CHUNK_ID_EXTRA_EDGE_LIST = 0x45444745; /* "EDGE" */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First 4 bytes describe the chunk id. Value 0 is a terminating label.
|
||||||
|
* Other 8 bytes provide the byte-offset in current file for chunk to start.
|
||||||
|
*/
|
||||||
|
static final int CHUNK_LOOKUP_WIDTH = 12;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First 8 bytes are for the positions of the first two parents of the ith
|
||||||
|
* commit. The next 8 bytes store the generation number of the commit and
|
||||||
|
* the commit time in seconds since EPOCH.
|
||||||
|
*/
|
||||||
|
static final int COMMIT_DATA_WIDTH = 16;
|
||||||
|
|
||||||
|
/** Mask to make the last edgeValue into position */
|
||||||
|
static final int GRAPH_EDGE_LAST_MASK = 0x7fffffff;
|
||||||
|
|
||||||
|
/** EdgeValue & GRAPH_LAST_EDGE != 0 means it is the last edgeValue */
|
||||||
|
static final int GRAPH_LAST_EDGE = 0x80000000;
|
||||||
|
|
||||||
|
/** EdgeValue == GRAPH_NO_PARENT means it has no parents */
|
||||||
|
static final int GRAPH_NO_PARENT = 0x70000000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EdgeValue & GRAPH_EXTRA_EDGES_NEEDED != 0 means its other parents are in
|
||||||
|
* Chunk Extra Edge List
|
||||||
|
*/
|
||||||
|
static final int GRAPH_EXTRA_EDGES_NEEDED = 0x80000000;
|
||||||
|
}
|
|
@ -0,0 +1,335 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021, 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.commitgraph;
|
||||||
|
|
||||||
|
import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_ID_COMMIT_DATA;
|
||||||
|
import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_ID_EXTRA_EDGE_LIST;
|
||||||
|
import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_ID_OID_FANOUT;
|
||||||
|
import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_ID_OID_LOOKUP;
|
||||||
|
import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.CHUNK_LOOKUP_WIDTH;
|
||||||
|
import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.COMMIT_DATA_WIDTH;
|
||||||
|
import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.COMMIT_GRAPH_MAGIC;
|
||||||
|
import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.GRAPH_EXTRA_EDGES_NEEDED;
|
||||||
|
import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.GRAPH_LAST_EDGE;
|
||||||
|
import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.GRAPH_NO_PARENT;
|
||||||
|
import static org.eclipse.jgit.lib.Constants.COMMIT_GENERATION_NOT_COMPUTED;
|
||||||
|
import static org.eclipse.jgit.lib.Constants.COMMIT_GENERATION_UNKNOWN;
|
||||||
|
import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.annotations.NonNull;
|
||||||
|
import org.eclipse.jgit.errors.MissingObjectException;
|
||||||
|
import org.eclipse.jgit.internal.JGitText;
|
||||||
|
import org.eclipse.jgit.internal.storage.io.CancellableDigestOutputStream;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.eclipse.jgit.util.NB;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a commit-graph formatted file.
|
||||||
|
*/
|
||||||
|
public class CommitGraphWriter {
|
||||||
|
|
||||||
|
private static final int COMMIT_GRAPH_VERSION_GENERATED = 1;
|
||||||
|
|
||||||
|
private static final int OID_HASH_VERSION = 1;
|
||||||
|
|
||||||
|
private static final int GRAPH_FANOUT_SIZE = 4 * 256;
|
||||||
|
|
||||||
|
private static final int GENERATION_NUMBER_MAX = 0x3FFFFFFF;
|
||||||
|
|
||||||
|
private final int hashsz;
|
||||||
|
|
||||||
|
private final GraphCommits graphCommits;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create commit-graph writer for these commits.
|
||||||
|
*
|
||||||
|
* @param graphCommits
|
||||||
|
* the commits which will be writen to the commit-graph.
|
||||||
|
*/
|
||||||
|
public CommitGraphWriter(@NonNull GraphCommits graphCommits) {
|
||||||
|
this.graphCommits = graphCommits;
|
||||||
|
this.hashsz = OBJECT_ID_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write commit-graph to the supplied stream.
|
||||||
|
*
|
||||||
|
* @param monitor
|
||||||
|
* progress monitor to report the number of items written.
|
||||||
|
* @param commitGraphStream
|
||||||
|
* output stream of commit-graph data. The stream should be
|
||||||
|
* buffered by the caller. The caller is responsible for closing
|
||||||
|
* the stream.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void write(@NonNull ProgressMonitor monitor,
|
||||||
|
@NonNull OutputStream commitGraphStream) throws IOException {
|
||||||
|
if (graphCommits.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ChunkHeader> chunks = createChunks();
|
||||||
|
long writeCount = 256 + 2 * graphCommits.size()
|
||||||
|
+ graphCommits.getExtraEdgeCnt();
|
||||||
|
monitor.beginTask(
|
||||||
|
MessageFormat.format(JGitText.get().writingOutCommitGraph,
|
||||||
|
Integer.valueOf(chunks.size())),
|
||||||
|
(int) writeCount);
|
||||||
|
|
||||||
|
try (CancellableDigestOutputStream out = new CancellableDigestOutputStream(
|
||||||
|
monitor, commitGraphStream)) {
|
||||||
|
writeHeader(out, chunks.size());
|
||||||
|
writeChunkLookup(out, chunks);
|
||||||
|
writeChunks(monitor, out, chunks);
|
||||||
|
writeCheckSum(out);
|
||||||
|
} catch (InterruptedIOException e) {
|
||||||
|
throw new IOException(JGitText.get().commitGraphWritingCancelled);
|
||||||
|
} finally {
|
||||||
|
monitor.endTask();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ChunkHeader> createChunks() {
|
||||||
|
List<ChunkHeader> chunks = new ArrayList<>();
|
||||||
|
chunks.add(new ChunkHeader(CHUNK_ID_OID_FANOUT, GRAPH_FANOUT_SIZE));
|
||||||
|
chunks.add(new ChunkHeader(CHUNK_ID_OID_LOOKUP,
|
||||||
|
hashsz * graphCommits.size()));
|
||||||
|
chunks.add(new ChunkHeader(CHUNK_ID_COMMIT_DATA,
|
||||||
|
(hashsz + 16) * graphCommits.size()));
|
||||||
|
if (graphCommits.getExtraEdgeCnt() > 0) {
|
||||||
|
chunks.add(new ChunkHeader(CHUNK_ID_EXTRA_EDGE_LIST,
|
||||||
|
graphCommits.getExtraEdgeCnt() * 4));
|
||||||
|
}
|
||||||
|
return chunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeHeader(CancellableDigestOutputStream out, int numChunks)
|
||||||
|
throws IOException {
|
||||||
|
byte[] headerBuffer = new byte[8];
|
||||||
|
NB.encodeInt32(headerBuffer, 0, COMMIT_GRAPH_MAGIC);
|
||||||
|
byte[] buff = { (byte) COMMIT_GRAPH_VERSION_GENERATED,
|
||||||
|
(byte) OID_HASH_VERSION, (byte) numChunks, (byte) 0 };
|
||||||
|
System.arraycopy(buff, 0, headerBuffer, 4, 4);
|
||||||
|
out.write(headerBuffer, 0, 8);
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeChunkLookup(CancellableDigestOutputStream out,
|
||||||
|
List<ChunkHeader> chunks) throws IOException {
|
||||||
|
int numChunks = chunks.size();
|
||||||
|
long chunkOffset = 8 + (numChunks + 1) * CHUNK_LOOKUP_WIDTH;
|
||||||
|
byte[] buffer = new byte[CHUNK_LOOKUP_WIDTH];
|
||||||
|
for (ChunkHeader chunk : chunks) {
|
||||||
|
NB.encodeInt32(buffer, 0, chunk.id);
|
||||||
|
NB.encodeInt64(buffer, 4, chunkOffset);
|
||||||
|
out.write(buffer);
|
||||||
|
chunkOffset += chunk.size;
|
||||||
|
}
|
||||||
|
NB.encodeInt32(buffer, 0, 0);
|
||||||
|
NB.encodeInt64(buffer, 4, chunkOffset);
|
||||||
|
out.write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeChunks(ProgressMonitor monitor,
|
||||||
|
CancellableDigestOutputStream out, List<ChunkHeader> chunks)
|
||||||
|
throws IOException {
|
||||||
|
for (ChunkHeader chunk : chunks) {
|
||||||
|
int chunkId = chunk.id;
|
||||||
|
|
||||||
|
switch (chunkId) {
|
||||||
|
case CHUNK_ID_OID_FANOUT:
|
||||||
|
writeFanoutTable(out);
|
||||||
|
break;
|
||||||
|
case CHUNK_ID_OID_LOOKUP:
|
||||||
|
writeOidLookUp(out);
|
||||||
|
break;
|
||||||
|
case CHUNK_ID_COMMIT_DATA:
|
||||||
|
writeCommitData(monitor, out);
|
||||||
|
break;
|
||||||
|
case CHUNK_ID_EXTRA_EDGE_LIST:
|
||||||
|
writeExtraEdges(out);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeCheckSum(CancellableDigestOutputStream out)
|
||||||
|
throws IOException {
|
||||||
|
out.write(out.getDigest());
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeFanoutTable(CancellableDigestOutputStream out)
|
||||||
|
throws IOException {
|
||||||
|
byte[] tmp = new byte[4];
|
||||||
|
int[] fanout = new int[256];
|
||||||
|
for (RevCommit c : graphCommits) {
|
||||||
|
fanout[c.getFirstByte() & 0xff]++;
|
||||||
|
}
|
||||||
|
for (int i = 1; i < fanout.length; i++) {
|
||||||
|
fanout[i] += fanout[i - 1];
|
||||||
|
}
|
||||||
|
for (int n : fanout) {
|
||||||
|
NB.encodeInt32(tmp, 0, n);
|
||||||
|
out.write(tmp, 0, 4);
|
||||||
|
out.getWriteMonitor().update(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeOidLookUp(CancellableDigestOutputStream out)
|
||||||
|
throws IOException {
|
||||||
|
byte[] tmp = new byte[4 + hashsz];
|
||||||
|
|
||||||
|
for (RevCommit c : graphCommits) {
|
||||||
|
c.copyRawTo(tmp, 0);
|
||||||
|
out.write(tmp, 0, hashsz);
|
||||||
|
out.getWriteMonitor().update(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeCommitData(ProgressMonitor monitor,
|
||||||
|
CancellableDigestOutputStream out) throws IOException {
|
||||||
|
int[] generations = computeGenerationNumbers(monitor);
|
||||||
|
int num = 0;
|
||||||
|
byte[] tmp = new byte[hashsz + COMMIT_DATA_WIDTH];
|
||||||
|
int i = 0;
|
||||||
|
for (RevCommit commit : graphCommits) {
|
||||||
|
int edgeValue;
|
||||||
|
int[] packedDate = new int[2];
|
||||||
|
|
||||||
|
ObjectId treeId = commit.getTree();
|
||||||
|
treeId.copyRawTo(tmp, 0);
|
||||||
|
|
||||||
|
RevCommit[] parents = commit.getParents();
|
||||||
|
if (parents.length == 0) {
|
||||||
|
edgeValue = GRAPH_NO_PARENT;
|
||||||
|
} else {
|
||||||
|
RevCommit parent = parents[0];
|
||||||
|
edgeValue = graphCommits.getOidPosition(parent);
|
||||||
|
}
|
||||||
|
NB.encodeInt32(tmp, hashsz, edgeValue);
|
||||||
|
if (parents.length == 1) {
|
||||||
|
edgeValue = GRAPH_NO_PARENT;
|
||||||
|
} else if (parents.length == 2) {
|
||||||
|
RevCommit parent = parents[1];
|
||||||
|
edgeValue = graphCommits.getOidPosition(parent);
|
||||||
|
} else if (parents.length > 2) {
|
||||||
|
edgeValue = GRAPH_EXTRA_EDGES_NEEDED | num;
|
||||||
|
num += parents.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
NB.encodeInt32(tmp, hashsz + 4, edgeValue);
|
||||||
|
|
||||||
|
packedDate[0] = 0; // commitTime is an int in JGit now
|
||||||
|
packedDate[0] |= generations[i] << 2;
|
||||||
|
packedDate[1] = commit.getCommitTime();
|
||||||
|
NB.encodeInt32(tmp, hashsz + 8, packedDate[0]);
|
||||||
|
NB.encodeInt32(tmp, hashsz + 12, packedDate[1]);
|
||||||
|
|
||||||
|
out.write(tmp);
|
||||||
|
out.getWriteMonitor().update(1);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] computeGenerationNumbers(ProgressMonitor monitor)
|
||||||
|
throws MissingObjectException {
|
||||||
|
int[] generations = new int[graphCommits.size()];
|
||||||
|
monitor.beginTask(JGitText.get().computingCommitGeneration,
|
||||||
|
graphCommits.size());
|
||||||
|
for (RevCommit cmit : graphCommits) {
|
||||||
|
monitor.update(1);
|
||||||
|
int generation = generations[graphCommits.getOidPosition(cmit)];
|
||||||
|
if (generation != COMMIT_GENERATION_NOT_COMPUTED
|
||||||
|
&& generation != COMMIT_GENERATION_UNKNOWN) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stack<RevCommit> commitStack = new Stack<>();
|
||||||
|
commitStack.push(cmit);
|
||||||
|
|
||||||
|
while (!commitStack.empty()) {
|
||||||
|
int maxGeneration = 0;
|
||||||
|
boolean allParentComputed = true;
|
||||||
|
RevCommit current = commitStack.peek();
|
||||||
|
RevCommit parent;
|
||||||
|
|
||||||
|
for (int i = 0; i < current.getParentCount(); i++) {
|
||||||
|
parent = current.getParent(i);
|
||||||
|
generation = generations[graphCommits
|
||||||
|
.getOidPosition(parent)];
|
||||||
|
if (generation == COMMIT_GENERATION_NOT_COMPUTED
|
||||||
|
|| generation == COMMIT_GENERATION_UNKNOWN) {
|
||||||
|
allParentComputed = false;
|
||||||
|
commitStack.push(parent);
|
||||||
|
break;
|
||||||
|
} else if (generation > maxGeneration) {
|
||||||
|
maxGeneration = generation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allParentComputed) {
|
||||||
|
RevCommit commit = commitStack.pop();
|
||||||
|
generation = maxGeneration + 1;
|
||||||
|
if (generation > GENERATION_NUMBER_MAX) {
|
||||||
|
generation = GENERATION_NUMBER_MAX;
|
||||||
|
}
|
||||||
|
generations[graphCommits
|
||||||
|
.getOidPosition(commit)] = generation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
monitor.endTask();
|
||||||
|
return generations;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeExtraEdges(CancellableDigestOutputStream out)
|
||||||
|
throws IOException {
|
||||||
|
byte[] tmp = new byte[4];
|
||||||
|
for (RevCommit commit : graphCommits) {
|
||||||
|
RevCommit[] parents = commit.getParents();
|
||||||
|
if (parents.length > 2) {
|
||||||
|
int edgeValue;
|
||||||
|
for (int n = 1; n < parents.length; n++) {
|
||||||
|
RevCommit parent = parents[n];
|
||||||
|
edgeValue = graphCommits.getOidPosition(parent);
|
||||||
|
if (n == parents.length - 1) {
|
||||||
|
edgeValue |= GRAPH_LAST_EDGE;
|
||||||
|
}
|
||||||
|
NB.encodeInt32(tmp, 0, edgeValue);
|
||||||
|
out.write(tmp);
|
||||||
|
out.getWriteMonitor().update(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ChunkHeader {
|
||||||
|
final int id;
|
||||||
|
|
||||||
|
final long size;
|
||||||
|
|
||||||
|
public ChunkHeader(int id, long size) {
|
||||||
|
this.id = id;
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2021, 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.commitgraph;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.annotations.NonNull;
|
||||||
|
import org.eclipse.jgit.errors.MissingObjectException;
|
||||||
|
import org.eclipse.jgit.internal.JGitText;
|
||||||
|
import org.eclipse.jgit.lib.AnyObjectId;
|
||||||
|
import org.eclipse.jgit.lib.Constants;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
|
import org.eclipse.jgit.lib.ObjectIdOwnerMap;
|
||||||
|
import org.eclipse.jgit.lib.ProgressMonitor;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.eclipse.jgit.revwalk.RevObject;
|
||||||
|
import org.eclipse.jgit.revwalk.RevSort;
|
||||||
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
|
import org.eclipse.jgit.util.BlockList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The commits which are used by the commit-graph writer to:
|
||||||
|
* <ul>
|
||||||
|
* <li>List commits in SHA1 order.</li>
|
||||||
|
* <li>Get the position of a specific SHA1 in the list.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public class GraphCommits implements Iterable<RevCommit> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare and create the commits for
|
||||||
|
* {@link org.eclipse.jgit.internal.storage.commitgraph.CommitGraphWriter}
|
||||||
|
* from the RevWalk.
|
||||||
|
*
|
||||||
|
* @param pm
|
||||||
|
* progress monitor.
|
||||||
|
* @param wants
|
||||||
|
* the list of wanted objects, writer walks commits starting at
|
||||||
|
* these. Must not be {@code null}.
|
||||||
|
* @param walk
|
||||||
|
* the RevWalk to use. Must not be {@code null}.
|
||||||
|
* @return the commits' collection which are used by the commit-graph
|
||||||
|
* writer. Never null.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static GraphCommits fromWalk(ProgressMonitor pm,
|
||||||
|
@NonNull Set<? extends ObjectId> wants, @NonNull RevWalk walk)
|
||||||
|
throws IOException {
|
||||||
|
walk.reset();
|
||||||
|
walk.sort(RevSort.NONE);
|
||||||
|
walk.setRetainBody(false);
|
||||||
|
for (ObjectId id : wants) {
|
||||||
|
RevObject o = walk.parseAny(id);
|
||||||
|
if (o instanceof RevCommit) {
|
||||||
|
walk.markStart((RevCommit) o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<RevCommit> commits = new BlockList<>();
|
||||||
|
RevCommit c;
|
||||||
|
pm.beginTask(JGitText.get().findingCommitsForCommitGraph,
|
||||||
|
ProgressMonitor.UNKNOWN);
|
||||||
|
while ((c = walk.next()) != null) {
|
||||||
|
pm.update(1);
|
||||||
|
commits.add(c);
|
||||||
|
}
|
||||||
|
pm.endTask();
|
||||||
|
return new GraphCommits(commits);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final List<RevCommit> sortedCommits;
|
||||||
|
|
||||||
|
private final ObjectIdOwnerMap<CommitWithPosition> commitPosMap;
|
||||||
|
|
||||||
|
private final int extraEdgeCnt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the GraphCommits.
|
||||||
|
*
|
||||||
|
* @param commits
|
||||||
|
* list of commits with their headers already parsed.
|
||||||
|
*/
|
||||||
|
private GraphCommits(List<RevCommit> commits) {
|
||||||
|
Collections.sort(commits); // sorted by name
|
||||||
|
sortedCommits = commits;
|
||||||
|
commitPosMap = new ObjectIdOwnerMap<>();
|
||||||
|
int cnt = 0;
|
||||||
|
for (int i = 0; i < commits.size(); i++) {
|
||||||
|
RevCommit c = sortedCommits.get(i);
|
||||||
|
if (c.getParentCount() > 2) {
|
||||||
|
cnt += c.getParentCount() - 1;
|
||||||
|
}
|
||||||
|
commitPosMap.add(new CommitWithPosition(c, i));
|
||||||
|
}
|
||||||
|
this.extraEdgeCnt = cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getOidPosition(RevCommit c) throws MissingObjectException {
|
||||||
|
CommitWithPosition commitWithPosition = commitPosMap.get(c);
|
||||||
|
if (commitWithPosition == null) {
|
||||||
|
throw new MissingObjectException(c, Constants.OBJ_COMMIT);
|
||||||
|
}
|
||||||
|
return commitWithPosition.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
RevCommit getCommit(int oidPos) {
|
||||||
|
if (oidPos < 0 || oidPos >= sortedCommits.size()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return sortedCommits.get(oidPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getExtraEdgeCnt() {
|
||||||
|
return extraEdgeCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
int size() {
|
||||||
|
return sortedCommits.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public Iterator<RevCommit> iterator() {
|
||||||
|
return sortedCommits.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CommitWithPosition extends ObjectIdOwnerMap.Entry {
|
||||||
|
|
||||||
|
final int position;
|
||||||
|
|
||||||
|
CommitWithPosition(AnyObjectId id, int position) {
|
||||||
|
super(id);
|
||||||
|
this.position = position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -886,4 +886,10 @@ public final class ConfigConstants {
|
||||||
*/
|
*/
|
||||||
public static final String CONFIG_KEY_ABBREV = "abbrev";
|
public static final String CONFIG_KEY_ABBREV = "abbrev";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "writeCommitGraph" key
|
||||||
|
*
|
||||||
|
* @since 6.5
|
||||||
|
*/
|
||||||
|
public static final String CONFIG_KEY_WRITE_COMMIT_GRAPH = "writeCommitGraph";
|
||||||
}
|
}
|
||||||
|
|
|
@ -754,6 +754,23 @@ public static byte[] encode(String str) {
|
||||||
*/
|
*/
|
||||||
public static final int INFINITE_DEPTH = 0x7fffffff;
|
public static final int INFINITE_DEPTH = 0x7fffffff;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We use ({@value}) as generation number for commits not in the
|
||||||
|
* commit-graph file.
|
||||||
|
*
|
||||||
|
* @since 6.5
|
||||||
|
*/
|
||||||
|
public static int COMMIT_GENERATION_UNKNOWN = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a commit-graph file was written by a version of Git that did not
|
||||||
|
* compute generation numbers, then those commits will have generation
|
||||||
|
* number represented by ({@value}).
|
||||||
|
*
|
||||||
|
* @since 6.5
|
||||||
|
*/
|
||||||
|
public static int COMMIT_GENERATION_NOT_COMPUTED = 0;
|
||||||
|
|
||||||
private Constants() {
|
private Constants() {
|
||||||
// Hide the default constructor
|
// Hide the default constructor
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue