mirror of
https://github.com/status-im/sourcecred.git
synced 2025-02-25 02:35:32 +00:00
Add BECOMES edges in the Git graph (#203)
Summary: If a commit causes a tree entry to change hash while keeping the same name, we now add a BECOMES edge between the corresponding entries. Test Plan: Snapshot changes are readable enough to manually verify. Programmatic tests also added. wchargin-branch: graph-becomes-edges
This commit is contained in:
parent
e9ecb8c608
commit
315f66cc4c
@ -1136,6 +1136,64 @@ Object {
|
||||
"type": "COMMIT",
|
||||
},
|
||||
},
|
||||
"{\\"id\\":\\"{\\\\\\"childCommit\\\\\\":\\\\\\"69c5aad50eec8f2a0a07c988c3b283a6490eb45b\\\\\\",\\\\\\"parentCommit\\\\\\":\\\\\\"e8b7a8f19701cd5a25e4a097d513ead60e5f8bcc\\\\\\",\\\\\\"path\\\\\\":[\\\\\\"src\\\\\\",\\\\\\"quantum_gravity.py\\\\\\"]}\\",\\"pluginName\\":\\"sourcecred/git-beta\\",\\"type\\":\\"BECOMES\\"}": Object {
|
||||
"dst": Object {
|
||||
"id": "78fc9c83023386854c6bfdc5761c0e58f68e226f:quantum_gravity.py",
|
||||
"pluginName": "sourcecred/git-beta",
|
||||
"type": "TREE_ENTRY",
|
||||
},
|
||||
"payload": Object {
|
||||
"childCommit": "69c5aad50eec8f2a0a07c988c3b283a6490eb45b",
|
||||
"parentCommit": "e8b7a8f19701cd5a25e4a097d513ead60e5f8bcc",
|
||||
"path": Array [
|
||||
"src",
|
||||
"quantum_gravity.py",
|
||||
],
|
||||
},
|
||||
"src": Object {
|
||||
"id": "7b79d579b62994faba3b69fdf8aa442586c32681:quantum_gravity.py",
|
||||
"pluginName": "sourcecred/git-beta",
|
||||
"type": "TREE_ENTRY",
|
||||
},
|
||||
},
|
||||
"{\\"id\\":\\"{\\\\\\"childCommit\\\\\\":\\\\\\"69c5aad50eec8f2a0a07c988c3b283a6490eb45b\\\\\\",\\\\\\"parentCommit\\\\\\":\\\\\\"e8b7a8f19701cd5a25e4a097d513ead60e5f8bcc\\\\\\",\\\\\\"path\\\\\\":[\\\\\\"src\\\\\\"]}\\",\\"pluginName\\":\\"sourcecred/git-beta\\",\\"type\\":\\"BECOMES\\"}": Object {
|
||||
"dst": Object {
|
||||
"id": "bbf3b8b3d26a4f884b5c022d46851f593d329192:src",
|
||||
"pluginName": "sourcecred/git-beta",
|
||||
"type": "TREE_ENTRY",
|
||||
},
|
||||
"payload": Object {
|
||||
"childCommit": "69c5aad50eec8f2a0a07c988c3b283a6490eb45b",
|
||||
"parentCommit": "e8b7a8f19701cd5a25e4a097d513ead60e5f8bcc",
|
||||
"path": Array [
|
||||
"src",
|
||||
],
|
||||
},
|
||||
"src": Object {
|
||||
"id": "819fc546cea489476ce8dc90785e9ba7753d0a8f:src",
|
||||
"pluginName": "sourcecred/git-beta",
|
||||
"type": "TREE_ENTRY",
|
||||
},
|
||||
},
|
||||
"{\\"id\\":\\"{\\\\\\"childCommit\\\\\\":\\\\\\"e8b7a8f19701cd5a25e4a097d513ead60e5f8bcc\\\\\\",\\\\\\"parentCommit\\\\\\":\\\\\\"d160cca97611e9dfed642522ad44408d0292e8ea\\\\\\",\\\\\\"path\\\\\\":[\\\\\\"pygravitydefier\\\\\\"]}\\",\\"pluginName\\":\\"sourcecred/git-beta\\",\\"type\\":\\"BECOMES\\"}": Object {
|
||||
"dst": Object {
|
||||
"id": "819fc546cea489476ce8dc90785e9ba7753d0a8f:pygravitydefier",
|
||||
"pluginName": "sourcecred/git-beta",
|
||||
"type": "TREE_ENTRY",
|
||||
},
|
||||
"payload": Object {
|
||||
"childCommit": "e8b7a8f19701cd5a25e4a097d513ead60e5f8bcc",
|
||||
"parentCommit": "d160cca97611e9dfed642522ad44408d0292e8ea",
|
||||
"path": Array [
|
||||
"pygravitydefier",
|
||||
],
|
||||
},
|
||||
"src": Object {
|
||||
"id": "569e1d383759903134df75230d63c0090196d4cb:pygravitydefier",
|
||||
"pluginName": "sourcecred/git-beta",
|
||||
"type": "TREE_ENTRY",
|
||||
},
|
||||
},
|
||||
},
|
||||
"nodes": Object {
|
||||
"{\\"id\\":\\"0fb31858c8e3710be77e1dbb8880acf8a5543d82\\",\\"pluginName\\":\\"sourcecred/git-beta\\",\\"type\\":\\"BLOB\\"}": Object {
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
import type {Edge, Node} from "../../core/graph";
|
||||
import type {
|
||||
BecomesEdgePayload,
|
||||
BlobNodePayload,
|
||||
Commit,
|
||||
EdgePayload,
|
||||
@ -17,6 +18,7 @@ import type {
|
||||
} from "./types";
|
||||
import {Graph, edgeID} from "../../core/graph";
|
||||
import {
|
||||
BECOMES_EDGE_TYPE,
|
||||
BLOB_NODE_TYPE,
|
||||
COMMIT_NODE_TYPE,
|
||||
HAS_CONTENTS_EDGE_TYPE,
|
||||
@ -26,6 +28,7 @@ import {
|
||||
SUBMODULE_COMMIT_NODE_TYPE,
|
||||
TREE_ENTRY_NODE_TYPE,
|
||||
TREE_NODE_TYPE,
|
||||
becomesEdgeId,
|
||||
hasParentEdgeId,
|
||||
includesEdgeId,
|
||||
submoduleCommitId,
|
||||
@ -45,6 +48,7 @@ class GitGraphCreator {
|
||||
...Object.keys(repository.trees).map((hash) =>
|
||||
this.treeGraph(repository.trees[hash], treeAndNameToSubmoduleUrls)
|
||||
),
|
||||
this.becomesEdges(repository),
|
||||
];
|
||||
return graphs.reduce((g, h) => Graph.mergeConservative(g, h), new Graph());
|
||||
}
|
||||
@ -194,6 +198,34 @@ class GitGraphCreator {
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
becomesEdges(repository: Repository): Graph<NodePayload, EdgePayload> {
|
||||
const result = new Graph();
|
||||
for (const {
|
||||
childCommit,
|
||||
parentCommit,
|
||||
becomesEdge: {from, to, path},
|
||||
} of findBecomesEdges(repository)) {
|
||||
result.addEdge(
|
||||
({
|
||||
address: _makeAddress(
|
||||
BECOMES_EDGE_TYPE,
|
||||
becomesEdgeId(childCommit, parentCommit, path)
|
||||
),
|
||||
src: _makeAddress(
|
||||
TREE_ENTRY_NODE_TYPE,
|
||||
treeEntryId(from.tree, from.name)
|
||||
),
|
||||
dst: _makeAddress(
|
||||
TREE_ENTRY_NODE_TYPE,
|
||||
treeEntryId(to.tree, to.name)
|
||||
),
|
||||
payload: {childCommit, parentCommit, path},
|
||||
}: Edge<BecomesEdgePayload>)
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export type BecomesEdge = {|
|
||||
|
@ -2,14 +2,18 @@
|
||||
|
||||
import cloneDeep from "lodash.clonedeep";
|
||||
|
||||
import type {Address} from "../../core/address";
|
||||
import type {Edge} from "../../core/graph";
|
||||
import type {BecomesEdge} from "./createGraph";
|
||||
import type {Hash, Tree} from "./types";
|
||||
import type {BecomesEdgePayload, Hash, Tree} from "./types";
|
||||
import {_makeAddress} from "./address";
|
||||
import {
|
||||
createGraph,
|
||||
findBecomesEdges,
|
||||
findBecomesEdgesForCommits,
|
||||
} from "./createGraph";
|
||||
import {
|
||||
BECOMES_EDGE_TYPE,
|
||||
BLOB_NODE_TYPE,
|
||||
COMMIT_NODE_TYPE,
|
||||
GIT_PLUGIN_NAME,
|
||||
@ -143,11 +147,73 @@ describe("createGraph", () => {
|
||||
direction: "OUT",
|
||||
})
|
||||
).toHaveLength(1);
|
||||
expect(graph.neighborhood(entryAddress)).toHaveLength(2);
|
||||
const becomesCount = graph.neighborhood(entryAddress, {
|
||||
edgeType: BECOMES_EDGE_TYPE,
|
||||
}).length;
|
||||
["OUT", "IN"].forEach((direction) => {
|
||||
expect(
|
||||
graph.neighborhood(entryAddress, {
|
||||
edgeType: BECOMES_EDGE_TYPE,
|
||||
direction,
|
||||
}).length
|
||||
).toBeLessThanOrEqual(1);
|
||||
});
|
||||
expect(graph.neighborhood(entryAddress)).toHaveLength(becomesCount + 2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('has "becomes" edges with valid paths', () => {
|
||||
const data = makeData();
|
||||
const graph = createGraph(data);
|
||||
const becomings: $ReadOnlyArray<Edge<BecomesEdgePayload>> = graph
|
||||
.edges({type: BECOMES_EDGE_TYPE})
|
||||
.map((edge) => ((edge: Edge<any>): Edge<BecomesEdgePayload>));
|
||||
expect(becomings).not.toHaveLength(0);
|
||||
becomings.forEach((edge) => {
|
||||
expect(edge.dst).not.toEqual(edge.src);
|
||||
const expectedPayload = {
|
||||
name: edge.payload.path[edge.payload.path.length - 1],
|
||||
};
|
||||
expect(graph.node(edge.src)).toEqual(
|
||||
expect.objectContaining({payload: expectedPayload})
|
||||
);
|
||||
expect(graph.node(edge.dst)).toEqual(
|
||||
expect.objectContaining({payload: expectedPayload})
|
||||
);
|
||||
|
||||
const {payload: {childCommit, parentCommit, path}} = edge;
|
||||
expect(path).not.toHaveLength(0);
|
||||
expect(data.commits[childCommit].parentHashes).toEqual(
|
||||
expect.arrayContaining([parentCommit])
|
||||
);
|
||||
function expectedTreeEntryAddress(commit: Hash): Address {
|
||||
const {tree, name} = path.slice(1).reduce(
|
||||
({tree, name}, newName) => {
|
||||
if (!(tree in data.trees)) {
|
||||
throw new Error(
|
||||
"Unexpected leaf along " +
|
||||
JSON.stringify(path) +
|
||||
" from " +
|
||||
commit
|
||||
);
|
||||
}
|
||||
return {
|
||||
tree: data.trees[tree].entries[name].hash,
|
||||
name: newName,
|
||||
};
|
||||
},
|
||||
{tree: data.commits[commit].treeHash, name: path[0]}
|
||||
);
|
||||
return _makeAddress(TREE_ENTRY_NODE_TYPE, treeEntryId(tree, name));
|
||||
}
|
||||
const parentEntryAddress = expectedTreeEntryAddress(parentCommit);
|
||||
const childEntryAddress = expectedTreeEntryAddress(childCommit);
|
||||
expect(parentEntryAddress).toEqual(edge.src);
|
||||
expect(childEntryAddress).toEqual(edge.dst);
|
||||
});
|
||||
});
|
||||
|
||||
describe("has specific paths:", () => {
|
||||
const headCommitHash = "3715ddfb8d4c4fd2a6f6af75488c82f84c92ec2f";
|
||||
if (makeData().commits[headCommitHash] == null) {
|
||||
|
@ -1,5 +1,7 @@
|
||||
// @flow
|
||||
|
||||
import stringify from "json-stable-stringify";
|
||||
|
||||
export const GIT_PLUGIN_NAME = "sourcecred/git-beta";
|
||||
|
||||
// Logical types
|
||||
@ -112,7 +114,18 @@ export function includesEdgeId(treeSha: string, name: string): string {
|
||||
|
||||
// TreeEntryNode -> TreeEntryNode
|
||||
export const BECOMES_EDGE_TYPE: "BECOMES" = "BECOMES";
|
||||
export type BecomesEdgePayload = {||};
|
||||
export type BecomesEdgePayload = {|
|
||||
+childCommit: Hash,
|
||||
+parentCommit: Hash,
|
||||
+path: $ReadOnlyArray<string>,
|
||||
|};
|
||||
export function becomesEdgeId(
|
||||
childCommit: Hash,
|
||||
parentCommit: Hash,
|
||||
path: $ReadOnlyArray<string>
|
||||
) {
|
||||
return stringify({childCommit, parentCommit, path});
|
||||
}
|
||||
|
||||
// TreeEntryNode -> BlobNode | TreeNode
|
||||
export const HAS_CONTENTS_EDGE_TYPE: "HAS_CONTENTS" = "HAS_CONTENTS";
|
||||
|
Loading…
x
Reference in New Issue
Block a user