From a460704ea805ebaed97c6e46be3556b193d9b67f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dandelion=20Man=C3=A9?= Date: Thu, 16 Aug 2018 11:38:36 -0700 Subject: [PATCH] Add `createMinimalGraph` for a tiny git graph (#689) This implements the approach suggested in [1]. Instead of forking the git plugin entirely, we'll fork the createGraph method and the pluginAdapter so that we have instances that produce a lightweight git graph. createMinimalGraph is a fork of createGraph that only adds commit nodes and has_parent edges. New unit tests ensure that only the whitelisted nodes and edges appear. Supersedes #683 and #684. Test plan: `yarn test` [1]: https://github.com/sourcecred/sourcecred/issues/627#issuecomment-413623784 --- .../createMinimalGraph.test.js.snap | 148 ++++++++++++++++++ src/plugins/git/createMinimalGraph.js | 41 +++++ src/plugins/git/createMinimalGraph.test.js | 38 +++++ 3 files changed, 227 insertions(+) create mode 100644 src/plugins/git/__snapshots__/createMinimalGraph.test.js.snap create mode 100644 src/plugins/git/createMinimalGraph.js create mode 100644 src/plugins/git/createMinimalGraph.test.js diff --git a/src/plugins/git/__snapshots__/createMinimalGraph.test.js.snap b/src/plugins/git/__snapshots__/createMinimalGraph.test.js.snap new file mode 100644 index 0000000..0aa24b2 --- /dev/null +++ b/src/plugins/git/__snapshots__/createMinimalGraph.test.js.snap @@ -0,0 +1,148 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`plugins/git/createMinimalGraph createMinimalGraph processes a simple repository 1`] = ` +Array [ + Object { + "type": "sourcecred/graph", + "version": "0.4.0", + }, + Object { + "edges": Array [ + Object { + "address": Array [ + "sourcecred", + "git", + "HAS_PARENT", + "2", + "COMMIT", + "3715ddfb8d4c4fd2a6f6af75488c82f84c92ec2f", + "2", + "COMMIT", + "69c5aad50eec8f2a0a07c988c3b283a6490eb45b", + ], + "dstIndex": 1, + "srcIndex": 0, + }, + Object { + "address": Array [ + "sourcecred", + "git", + "HAS_PARENT", + "2", + "COMMIT", + "69c5aad50eec8f2a0a07c988c3b283a6490eb45b", + "2", + "COMMIT", + "e8b7a8f19701cd5a25e4a097d513ead60e5f8bcc", + ], + "dstIndex": 6, + "srcIndex": 1, + }, + Object { + "address": Array [ + "sourcecred", + "git", + "HAS_PARENT", + "2", + "COMMIT", + "8d287c3bfbf8455ef30187bf5153ffc1b6eef268", + "2", + "COMMIT", + "c08ee3a4edea384d5291ffcbf06724a13ed72325", + ], + "dstIndex": 3, + "srcIndex": 2, + }, + Object { + "address": Array [ + "sourcecred", + "git", + "HAS_PARENT", + "2", + "COMMIT", + "c08ee3a4edea384d5291ffcbf06724a13ed72325", + "2", + "COMMIT", + "c2b51945e7457546912a8ce158ed9d294558d294", + ], + "dstIndex": 4, + "srcIndex": 3, + }, + Object { + "address": Array [ + "sourcecred", + "git", + "HAS_PARENT", + "2", + "COMMIT", + "d160cca97611e9dfed642522ad44408d0292e8ea", + "2", + "COMMIT", + "8d287c3bfbf8455ef30187bf5153ffc1b6eef268", + ], + "dstIndex": 2, + "srcIndex": 5, + }, + Object { + "address": Array [ + "sourcecred", + "git", + "HAS_PARENT", + "2", + "COMMIT", + "e8b7a8f19701cd5a25e4a097d513ead60e5f8bcc", + "2", + "COMMIT", + "d160cca97611e9dfed642522ad44408d0292e8ea", + ], + "dstIndex": 5, + "srcIndex": 6, + }, + ], + "nodes": Array [ + Array [ + "sourcecred", + "git", + "COMMIT", + "3715ddfb8d4c4fd2a6f6af75488c82f84c92ec2f", + ], + Array [ + "sourcecred", + "git", + "COMMIT", + "69c5aad50eec8f2a0a07c988c3b283a6490eb45b", + ], + Array [ + "sourcecred", + "git", + "COMMIT", + "8d287c3bfbf8455ef30187bf5153ffc1b6eef268", + ], + Array [ + "sourcecred", + "git", + "COMMIT", + "c08ee3a4edea384d5291ffcbf06724a13ed72325", + ], + Array [ + "sourcecred", + "git", + "COMMIT", + "c2b51945e7457546912a8ce158ed9d294558d294", + ], + Array [ + "sourcecred", + "git", + "COMMIT", + "d160cca97611e9dfed642522ad44408d0292e8ea", + ], + Array [ + "sourcecred", + "git", + "COMMIT", + "e8b7a8f19701cd5a25e4a097d513ead60e5f8bcc", + ], + ], + }, +] +`; diff --git a/src/plugins/git/createMinimalGraph.js b/src/plugins/git/createMinimalGraph.js new file mode 100644 index 0000000..384fa1e --- /dev/null +++ b/src/plugins/git/createMinimalGraph.js @@ -0,0 +1,41 @@ +// @flow + +import {Graph} from "../../core/graph"; + +import * as GT from "./types"; +import * as GN from "./nodes"; +import * as GE from "./edges"; + +export function createMinimalGraph(repository: GT.Repository): Graph { + const creator = new MinimalGraphCreator(); + creator.addRepository(repository); + return creator.graph; +} + +class MinimalGraphCreator { + +graph: Graph; + + constructor() { + this.graph = new Graph(); + } + + addNode(a: GN.StructuredAddress) { + this.graph.addNode(GN.toRaw(a)); + } + + addRepository(repository: GT.Repository) { + for (const commitHash of Object.keys(repository.commits)) { + this.addCommit(repository.commits[commitHash]); + } + } + + addCommit(commit: GT.Commit) { + const node: GN.CommitAddress = {type: GN.COMMIT_TYPE, hash: commit.hash}; + this.graph.addNode(GN.toRaw(node)); + for (const parentHash of commit.parentHashes) { + const parent: GN.CommitAddress = {type: GN.COMMIT_TYPE, hash: parentHash}; + this.graph.addNode(GN.toRaw(parent)); + this.graph.addEdge(GE.createEdge.hasParent(node, parent)); + } + } +} diff --git a/src/plugins/git/createMinimalGraph.test.js b/src/plugins/git/createMinimalGraph.test.js new file mode 100644 index 0000000..88dcc24 --- /dev/null +++ b/src/plugins/git/createMinimalGraph.test.js @@ -0,0 +1,38 @@ +// @flow + +import cloneDeep from "lodash.clonedeep"; + +import {createMinimalGraph} from "./createMinimalGraph"; +import {GraphView} from "./graphView"; +import {_Prefix as NodePrefix} from "./nodes"; +import {_Prefix as EdgePrefix} from "./edges"; +import {NodeAddress, EdgeAddress} from "../../core/graph"; + +const makeData = () => cloneDeep(require("./example/example-git")); + +describe("plugins/git/createMinimalGraph", () => { + describe("createMinimalGraph", () => { + it("processes a simple repository", () => { + expect(createMinimalGraph(makeData())).toMatchSnapshot(); + }); + + it("satisfies the GraphView invariants", () => { + const graph = createMinimalGraph(makeData()); + expect(() => new GraphView(graph)).not.toThrow(); + }); + + it("only has commit nodes and has_parent edges", () => { + const graph = createMinimalGraph(makeData()); + for (const n of graph.nodes()) { + if (!NodeAddress.hasPrefix(n, NodePrefix.commit)) { + throw new Error("Found non-commit node"); + } + } + for (const {address} of graph.edges()) { + if (!EdgeAddress.hasPrefix(address, EdgePrefix.hasParent)) { + throw new Error("Found non-has-parent edge"); + } + } + }); + }); +});