Find BECOMES edges for high-level commits (#201)
Test Plan: Unit tests included. I verified that the hashes in the snapshot are correct. wchargin-branch: high-level-becomes-commits
This commit is contained in:
parent
a76d01ab75
commit
c572b7f880
|
@ -1392,3 +1392,35 @@ Object {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`findBecomesEdgesForCommits works on the example repository 1`] = `
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"from": Object {
|
||||||
|
"name": "src",
|
||||||
|
"tree": "819fc546cea489476ce8dc90785e9ba7753d0a8f",
|
||||||
|
},
|
||||||
|
"path": Array [
|
||||||
|
"src",
|
||||||
|
],
|
||||||
|
"to": Object {
|
||||||
|
"name": "src",
|
||||||
|
"tree": "bbf3b8b3d26a4f884b5c022d46851f593d329192",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"from": Object {
|
||||||
|
"name": "quantum_gravity.py",
|
||||||
|
"tree": "7b79d579b62994faba3b69fdf8aa442586c32681",
|
||||||
|
},
|
||||||
|
"path": Array [
|
||||||
|
"src",
|
||||||
|
"quantum_gravity.py",
|
||||||
|
],
|
||||||
|
"to": Object {
|
||||||
|
"name": "quantum_gravity.py",
|
||||||
|
"tree": "78fc9c83023386854c6bfdc5761c0e58f68e226f",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
|
@ -196,6 +196,59 @@ class GitGraphCreator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type BecomesEdge = {|
|
||||||
|
+from: {|
|
||||||
|
+tree: Hash,
|
||||||
|
+name: string,
|
||||||
|
|},
|
||||||
|
+to: {|
|
||||||
|
+tree: Hash,
|
||||||
|
+name: string,
|
||||||
|
|},
|
||||||
|
+path: $ReadOnlyArray<string>,
|
||||||
|
|};
|
||||||
|
|
||||||
|
export function* findBecomesEdgesForCommits(
|
||||||
|
repository: Repository,
|
||||||
|
childCommit: Hash,
|
||||||
|
parentCommit: Hash
|
||||||
|
): Iterator<BecomesEdge> {
|
||||||
|
const workUnits = [
|
||||||
|
{
|
||||||
|
path: [],
|
||||||
|
beforeTreeHash: repository.commits[parentCommit].treeHash,
|
||||||
|
afterTreeHash: repository.commits[childCommit].treeHash,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
while (workUnits.length > 0) {
|
||||||
|
const {path, beforeTreeHash, afterTreeHash} = workUnits.pop();
|
||||||
|
const beforeTree = repository.trees[beforeTreeHash];
|
||||||
|
const afterTree = repository.trees[afterTreeHash];
|
||||||
|
for (const name of Object.keys(beforeTree.entries)) {
|
||||||
|
if (!(name in afterTree.entries)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const beforeEntry = beforeTree.entries[name];
|
||||||
|
const afterEntry = afterTree.entries[name];
|
||||||
|
const subpath = [...path, name];
|
||||||
|
if (beforeEntry.hash !== afterEntry.hash) {
|
||||||
|
yield {
|
||||||
|
from: {tree: beforeTreeHash, name},
|
||||||
|
to: {tree: afterTreeHash, name},
|
||||||
|
path: subpath,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (beforeEntry.type === "tree" && afterEntry.type === "tree") {
|
||||||
|
workUnits.push({
|
||||||
|
path: subpath,
|
||||||
|
beforeTreeHash: beforeEntry.hash,
|
||||||
|
afterTreeHash: afterEntry.hash,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function createGraph(
|
export function createGraph(
|
||||||
repository: Repository
|
repository: Repository
|
||||||
): Graph<NodePayload, EdgePayload> {
|
): Graph<NodePayload, EdgePayload> {
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
import cloneDeep from "lodash.clonedeep";
|
import cloneDeep from "lodash.clonedeep";
|
||||||
|
|
||||||
import {createGraph} from "./createGraph";
|
import type {BecomesEdge} from "./createGraph";
|
||||||
|
import type {Hash, Tree} from "./types";
|
||||||
|
import {createGraph, findBecomesEdgesForCommits} from "./createGraph";
|
||||||
import {
|
import {
|
||||||
BLOB_NODE_TYPE,
|
BLOB_NODE_TYPE,
|
||||||
COMMIT_NODE_TYPE,
|
COMMIT_NODE_TYPE,
|
||||||
|
@ -241,3 +243,221 @@ describe("createGraph", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("findBecomesEdgesForCommits", () => {
|
||||||
|
function fromTrees(
|
||||||
|
beforeTree: Hash,
|
||||||
|
afterTree: Hash,
|
||||||
|
trees: {[Hash]: Tree}
|
||||||
|
): BecomesEdge[] {
|
||||||
|
const repo = {
|
||||||
|
commits: {
|
||||||
|
commit1: {
|
||||||
|
hash: "commit1",
|
||||||
|
parentHashes: [],
|
||||||
|
treeHash: beforeTree,
|
||||||
|
submoduleUrls: {},
|
||||||
|
},
|
||||||
|
commit2: {
|
||||||
|
hash: "commit2",
|
||||||
|
parentHashes: ["commit1"],
|
||||||
|
treeHash: afterTree,
|
||||||
|
submoduleUrls: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
trees,
|
||||||
|
};
|
||||||
|
return Array.from(findBecomesEdgesForCommits(repo, "commit2", "commit1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
it("works on the example repository", () => {
|
||||||
|
const data = makeData();
|
||||||
|
const childCommitHash = "69c5aad50eec8f2a0a07c988c3b283a6490eb45b";
|
||||||
|
expect(data.commits[childCommitHash]).toEqual(expect.anything());
|
||||||
|
expect(data.commits[childCommitHash].parentHashes).toHaveLength(1);
|
||||||
|
const parentCommitHash = data.commits[childCommitHash].parentHashes[0];
|
||||||
|
expect(
|
||||||
|
Array.from(
|
||||||
|
findBecomesEdgesForCommits(data, childCommitHash, parentCommitHash)
|
||||||
|
)
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("works on empty trees", () => {
|
||||||
|
expect(
|
||||||
|
fromTrees("tree1", "tree2", {
|
||||||
|
tree1: {
|
||||||
|
hash: "tree1",
|
||||||
|
entries: {},
|
||||||
|
},
|
||||||
|
tree2: {
|
||||||
|
hash: "tree2",
|
||||||
|
entries: {},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("finds differences and non-differences at the root", () => {
|
||||||
|
expect(
|
||||||
|
fromTrees("tree1", "tree2", {
|
||||||
|
tree1: {
|
||||||
|
hash: "tree1",
|
||||||
|
entries: {
|
||||||
|
"color.txt": {
|
||||||
|
type: "blob",
|
||||||
|
name: "color.txt",
|
||||||
|
hash: "blue",
|
||||||
|
},
|
||||||
|
"number.txt": {
|
||||||
|
type: "blob",
|
||||||
|
name: "number.txt",
|
||||||
|
hash: "twelve",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tree2: {
|
||||||
|
hash: "tree2",
|
||||||
|
entries: {
|
||||||
|
"color.txt": {
|
||||||
|
type: "blob",
|
||||||
|
name: "color.txt",
|
||||||
|
hash: "yellow",
|
||||||
|
},
|
||||||
|
"number.txt": {
|
||||||
|
type: "blob",
|
||||||
|
name: "number.txt",
|
||||||
|
hash: "twelve",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).toEqual([
|
||||||
|
{
|
||||||
|
from: {
|
||||||
|
tree: "tree1",
|
||||||
|
name: "color.txt",
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
tree: "tree2",
|
||||||
|
name: "color.txt",
|
||||||
|
},
|
||||||
|
path: ["color.txt"],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles cases where files of the same name appear in different trees", () => {
|
||||||
|
const result = fromTrees("tree1", "tree2", {
|
||||||
|
tree1: {
|
||||||
|
hash: "tree1",
|
||||||
|
entries: {
|
||||||
|
"color.txt": {
|
||||||
|
type: "blob",
|
||||||
|
name: "color.txt",
|
||||||
|
hash: "blue",
|
||||||
|
},
|
||||||
|
"number.txt": {
|
||||||
|
type: "blob",
|
||||||
|
name: "number.txt",
|
||||||
|
hash: "twelve",
|
||||||
|
},
|
||||||
|
mirror_universe: {
|
||||||
|
type: "tree",
|
||||||
|
name: "mirror_universe",
|
||||||
|
hash: "eert1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
eert1: {
|
||||||
|
hash: "eert1",
|
||||||
|
entries: {
|
||||||
|
"color.txt": {
|
||||||
|
type: "blob",
|
||||||
|
name: "color.txt",
|
||||||
|
hash: "eulb",
|
||||||
|
},
|
||||||
|
"number.txt": {
|
||||||
|
type: "blob",
|
||||||
|
name: "number.txt",
|
||||||
|
hash: "evlewt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tree2: {
|
||||||
|
hash: "tree2",
|
||||||
|
entries: {
|
||||||
|
"color.txt": {
|
||||||
|
type: "blob",
|
||||||
|
name: "color.txt",
|
||||||
|
hash: "yellow",
|
||||||
|
},
|
||||||
|
"number.txt": {
|
||||||
|
type: "blob",
|
||||||
|
name: "number.txt",
|
||||||
|
hash: "twelve",
|
||||||
|
},
|
||||||
|
mirror_universe: {
|
||||||
|
type: "tree",
|
||||||
|
name: "mirror_universe",
|
||||||
|
hash: "eert2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
eert2: {
|
||||||
|
hash: "eert1",
|
||||||
|
entries: {
|
||||||
|
"color.txt": {
|
||||||
|
type: "blob",
|
||||||
|
name: "color.txt",
|
||||||
|
hash: "eulb",
|
||||||
|
},
|
||||||
|
"number.txt": {
|
||||||
|
type: "blob",
|
||||||
|
name: "number.txt",
|
||||||
|
hash: "neetneves",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const expected = [
|
||||||
|
{
|
||||||
|
from: {
|
||||||
|
tree: "tree1",
|
||||||
|
name: "color.txt",
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
tree: "tree2",
|
||||||
|
name: "color.txt",
|
||||||
|
},
|
||||||
|
path: ["color.txt"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: {
|
||||||
|
tree: "eert1",
|
||||||
|
name: "number.txt",
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
tree: "eert2",
|
||||||
|
name: "number.txt",
|
||||||
|
},
|
||||||
|
path: ["mirror_universe", "number.txt"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: {
|
||||||
|
tree: "tree1",
|
||||||
|
name: "mirror_universe",
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
tree: "tree2",
|
||||||
|
name: "mirror_universe",
|
||||||
|
},
|
||||||
|
path: ["mirror_universe"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
expect(result).toEqual(expect.arrayContaining(expected));
|
||||||
|
expect(expected).toEqual(
|
||||||
|
expect.arrayContaining((result.slice(): $ReadOnlyArray<mixed>).slice())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue