blob: 6ae40ff552c007db5796f6db2385a09ac452a0e0 [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.commitgraph;
import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.COMMIT_DATA_WIDTH;
import static org.eclipse.jgit.internal.storage.commitgraph.CommitGraphConstants.GRAPH_EDGE_LAST_MASK;
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 java.text.MessageFormat;
import java.util.Arrays;
import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.commitgraph.CommitGraph.CommitData;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.NB;
/**
* Represent the collection of {@link CommitData}.
*/
class GraphCommitData {
private static final int[] NO_PARENTS = {};
private final byte[] data;
private final byte[] extraList;
private final int hashLength;
private final int commitDataLength;
/**
* Initialize the GraphCommitData.
*
* @param hashLength
* length of object hash.
* @param commitData
* content of CommitData Chunk.
* @param extraList
* content of Extra Edge List Chunk.
*/
GraphCommitData(int hashLength, @NonNull byte[] commitData,
byte[] extraList) {
this.data = commitData;
this.extraList = extraList;
this.hashLength = hashLength;
this.commitDataLength = hashLength + COMMIT_DATA_WIDTH;
}
/**
* Get the metadata of a commit。
*
* @param graphPos
* the position in the commit-graph of the object.
* @return the metadata of a commit or null if not found.
*/
CommitData getCommitData(int graphPos) {
int dataIdx = commitDataLength * graphPos;
// parse tree
ObjectId tree = ObjectId.fromRaw(data, dataIdx);
// parse date
long dateHigh = NB.decodeUInt32(data, dataIdx + hashLength + 8) & 0x3;
long dateLow = NB.decodeUInt32(data, dataIdx + hashLength + 12);
long commitTime = dateHigh << 32 | dateLow;
// parse generation
int generation = NB.decodeInt32(data, dataIdx + hashLength + 8) >> 2;
// parse first parent
int parent1 = NB.decodeInt32(data, dataIdx + hashLength);
if (parent1 == GRAPH_NO_PARENT) {
return new CommitDataImpl(tree, NO_PARENTS, commitTime, generation);
}
// parse second parent
int parent2 = NB.decodeInt32(data, dataIdx + hashLength + 4);
if (parent2 == GRAPH_NO_PARENT) {
return new CommitDataImpl(tree, new int[] { parent1 }, commitTime,
generation);
}
if ((parent2 & GRAPH_EXTRA_EDGES_NEEDED) == 0) {
return new CommitDataImpl(tree, new int[] { parent1, parent2 },
commitTime, generation);
}
// parse parents for octopus merge
return new CommitDataImpl(tree,
findParentsForOctopusMerge(parent1,
parent2 & GRAPH_EDGE_LAST_MASK),
commitTime, generation);
}
private int[] findParentsForOctopusMerge(int parent1, int extraEdgePos) {
int maxOffset = extraList.length - 4;
int offset = extraEdgePos * 4;
if (offset < 0 || offset > maxOffset) {
throw new IllegalArgumentException(MessageFormat.format(
JGitText.get().invalidExtraEdgeListPosition,
Integer.valueOf(extraEdgePos)));
}
int[] pList = new int[32];
pList[0] = parent1;
int count = 1;
int parentPosition;
for (; offset <= maxOffset; offset += 4) {
if (count >= pList.length) {
// expand the pList
pList = Arrays.copyOf(pList, pList.length + 32);
}
parentPosition = NB.decodeInt32(extraList, offset);
if ((parentPosition & GRAPH_LAST_EDGE) != 0) {
pList[count++] = parentPosition & GRAPH_EDGE_LAST_MASK;
break;
}
pList[count++] = parentPosition;
}
return Arrays.copyOf(pList, count);
}
private static class CommitDataImpl implements CommitData {
private final ObjectId tree;
private final int[] parents;
private final long commitTime;
private final int generation;
public CommitDataImpl(ObjectId tree, int[] parents, long commitTime,
int generation) {
this.tree = tree;
this.parents = parents;
this.commitTime = commitTime;
this.generation = generation;
}
@Override
public ObjectId getTree() {
return tree;
}
@Override
public int[] getParents() {
return parents;
}
@Override
public long getCommitTime() {
return commitTime;
}
@Override
public int getGeneration() {
return generation;
}
}
}