Add commit parent edges in the Git graph (#178)

Test Plan:
To verify the snapshot change, either believe the programmatic tests, or
use the following script to verify that the right edges were added:
```bash
#!/bin/bash
set -eu
example_repo="$(mktemp -d)"
yarn backend >/dev/null 2>/dev/null
node bin/createExampleRepo.js "${example_repo}"
expected() {
    git -C "${example_repo}" log --format='%H %P' \
        | awk '{ if (NF > 1) { print $2 " " $1 } }' \
        | sort
}
actual() {
    git diff HEAD^..HEAD | grep -A 1 -F -e src -e dst \
        | sed -n 's/^+.*"id": "\(.\+\)".*/\1 /p' \
        | tr -d $'\n' | cat - <(echo) \
        | fold -s -w 82 | sed 's/ *$//' \
        | sort
}
diff -u <(expected) <(actual)
```

wchargin-branch: graph-commit-parents
This commit is contained in:
William Chargin 2018-04-30 18:08:40 -07:00 committed by GitHub
parent 56ddb5cf9b
commit 16e8e399e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 159 additions and 4 deletions

View File

@ -33,6 +33,23 @@ Object {
"type": "TREE",
},
},
"{\\"id\\":\\"3715ddfb8d4c4fd2a6f6af75488c82f84c92ec2f^1\\",\\"pluginName\\":\\"sourcecred/git-beta\\",\\"repositoryName\\":\\"sourcecred/example-git\\",\\"type\\":\\"HAS_PARENT\\"}": Object {
"dst": Object {
"id": "69c5aad50eec8f2a0a07c988c3b283a6490eb45b",
"pluginName": "sourcecred/git-beta",
"repositoryName": "sourcecred/example-git",
"type": "COMMIT",
},
"payload": Object {
"parentIndex": 1,
},
"src": Object {
"id": "3715ddfb8d4c4fd2a6f6af75488c82f84c92ec2f",
"pluginName": "sourcecred/git-beta",
"repositoryName": "sourcecred/example-git",
"type": "COMMIT",
},
},
"{\\"id\\":\\"3dfb84795e07341b05fad3a0d5a55f8304b2d7d8:.gitmodules\\",\\"pluginName\\":\\"sourcecred/git-beta\\",\\"repositoryName\\":\\"sourcecred/example-git\\",\\"type\\":\\"INCLUDES\\"}": Object {
"dst": Object {
"id": "3dfb84795e07341b05fad3a0d5a55f8304b2d7d8:.gitmodules",
@ -183,6 +200,23 @@ Object {
"type": "TREE",
},
},
"{\\"id\\":\\"69c5aad50eec8f2a0a07c988c3b283a6490eb45b^1\\",\\"pluginName\\":\\"sourcecred/git-beta\\",\\"repositoryName\\":\\"sourcecred/example-git\\",\\"type\\":\\"HAS_PARENT\\"}": Object {
"dst": Object {
"id": "e8b7a8f19701cd5a25e4a097d513ead60e5f8bcc",
"pluginName": "sourcecred/git-beta",
"repositoryName": "sourcecred/example-git",
"type": "COMMIT",
},
"payload": Object {
"parentIndex": 1,
},
"src": Object {
"id": "69c5aad50eec8f2a0a07c988c3b283a6490eb45b",
"pluginName": "sourcecred/git-beta",
"repositoryName": "sourcecred/example-git",
"type": "COMMIT",
},
},
"{\\"id\\":\\"78fc9c83023386854c6bfdc5761c0e58f68e226f:index.py\\",\\"pluginName\\":\\"sourcecred/git-beta\\",\\"repositoryName\\":\\"sourcecred/example-git\\",\\"type\\":\\"INCLUDES\\"}": Object {
"dst": Object {
"id": "78fc9c83023386854c6bfdc5761c0e58f68e226f:index.py",
@ -408,6 +442,23 @@ Object {
"type": "TREE",
},
},
"{\\"id\\":\\"8d287c3bfbf8455ef30187bf5153ffc1b6eef268^1\\",\\"pluginName\\":\\"sourcecred/git-beta\\",\\"repositoryName\\":\\"sourcecred/example-git\\",\\"type\\":\\"HAS_PARENT\\"}": Object {
"dst": Object {
"id": "c08ee3a4edea384d5291ffcbf06724a13ed72325",
"pluginName": "sourcecred/git-beta",
"repositoryName": "sourcecred/example-git",
"type": "COMMIT",
},
"payload": Object {
"parentIndex": 1,
},
"src": Object {
"id": "8d287c3bfbf8455ef30187bf5153ffc1b6eef268",
"pluginName": "sourcecred/git-beta",
"repositoryName": "sourcecred/example-git",
"type": "COMMIT",
},
},
"{\\"id\\":\\"[{\\\\\\"id\\\\\\":\\\\\\"2f7155e359fd0ecb96ffdca66fa45b6ed5792809:README.txt\\\\\\",\\\\\\"pluginName\\\\\\":\\\\\\"sourcecred/git-beta\\\\\\",\\\\\\"repositoryName\\\\\\":\\\\\\"sourcecred/example-git\\\\\\",\\\\\\"type\\\\\\":\\\\\\"TREE_ENTRY\\\\\\"},{\\\\\\"id\\\\\\":\\\\\\"0fb31858c8e3710be77e1dbb8880acf8a5543d82\\\\\\",\\\\\\"pluginName\\\\\\":\\\\\\"sourcecred/git-beta\\\\\\",\\\\\\"repositoryName\\\\\\":\\\\\\"sourcecred/example-git\\\\\\",\\\\\\"type\\\\\\":\\\\\\"BLOB\\\\\\"}]\\",\\"pluginName\\":\\"sourcecred/git-beta\\",\\"repositoryName\\":\\"sourcecred/example-git\\",\\"type\\":\\"HAS_CONTENTS\\"}": Object {
"dst": Object {
"id": "0fb31858c8e3710be77e1dbb8880acf8a5543d82",
@ -1053,6 +1104,57 @@ Object {
"type": "TREE",
},
},
"{\\"id\\":\\"c08ee3a4edea384d5291ffcbf06724a13ed72325^1\\",\\"pluginName\\":\\"sourcecred/git-beta\\",\\"repositoryName\\":\\"sourcecred/example-git\\",\\"type\\":\\"HAS_PARENT\\"}": Object {
"dst": Object {
"id": "c2b51945e7457546912a8ce158ed9d294558d294",
"pluginName": "sourcecred/git-beta",
"repositoryName": "sourcecred/example-git",
"type": "COMMIT",
},
"payload": Object {
"parentIndex": 1,
},
"src": Object {
"id": "c08ee3a4edea384d5291ffcbf06724a13ed72325",
"pluginName": "sourcecred/git-beta",
"repositoryName": "sourcecred/example-git",
"type": "COMMIT",
},
},
"{\\"id\\":\\"d160cca97611e9dfed642522ad44408d0292e8ea^1\\",\\"pluginName\\":\\"sourcecred/git-beta\\",\\"repositoryName\\":\\"sourcecred/example-git\\",\\"type\\":\\"HAS_PARENT\\"}": Object {
"dst": Object {
"id": "8d287c3bfbf8455ef30187bf5153ffc1b6eef268",
"pluginName": "sourcecred/git-beta",
"repositoryName": "sourcecred/example-git",
"type": "COMMIT",
},
"payload": Object {
"parentIndex": 1,
},
"src": Object {
"id": "d160cca97611e9dfed642522ad44408d0292e8ea",
"pluginName": "sourcecred/git-beta",
"repositoryName": "sourcecred/example-git",
"type": "COMMIT",
},
},
"{\\"id\\":\\"e8b7a8f19701cd5a25e4a097d513ead60e5f8bcc^1\\",\\"pluginName\\":\\"sourcecred/git-beta\\",\\"repositoryName\\":\\"sourcecred/example-git\\",\\"type\\":\\"HAS_PARENT\\"}": Object {
"dst": Object {
"id": "d160cca97611e9dfed642522ad44408d0292e8ea",
"pluginName": "sourcecred/git-beta",
"repositoryName": "sourcecred/example-git",
"type": "COMMIT",
},
"payload": Object {
"parentIndex": 1,
},
"src": Object {
"id": "e8b7a8f19701cd5a25e4a097d513ead60e5f8bcc",
"pluginName": "sourcecred/git-beta",
"repositoryName": "sourcecred/example-git",
"type": "COMMIT",
},
},
},
"nodes": Object {
"{\\"id\\":\\"0fb31858c8e3710be77e1dbb8880acf8a5543d82\\",\\"pluginName\\":\\"sourcecred/git-beta\\",\\"repositoryName\\":\\"sourcecred/example-git\\",\\"type\\":\\"BLOB\\"}": Object {

View File

@ -18,8 +18,10 @@ import {
TREE_ENTRY_NODE_TYPE,
INCLUDES_EDGE_TYPE,
HAS_CONTENTS_EDGE_TYPE,
HAS_PARENT_EDGE_TYPE,
HAS_TREE_EDGE_TYPE,
GIT_PLUGIN_NAME,
hasParentEdgeId,
includesEdgeId,
treeEntryId,
} from "./types";
@ -61,7 +63,7 @@ class GitGraphCreator {
address: this.makeAddress(TREE_NODE_TYPE, commit.treeHash),
payload: {},
};
const edge = {
const hasTreeEdge = {
address: this.makeAddress(
HAS_TREE_EDGE_TYPE,
edgeID(commitNode.address, treeNode.address)
@ -70,10 +72,27 @@ class GitGraphCreator {
dst: treeNode.address,
payload: {},
};
return new Graph()
const result = new Graph()
.addNode(commitNode)
.addNode(treeNode)
.addEdge(edge);
.addEdge(hasTreeEdge);
commit.parentHashes.forEach((parentHash, index) => {
const oneBasedParentIndex = index + 1;
const parentAddress = this.makeAddress(COMMIT_NODE_TYPE, parentHash);
const parentEdge = {
address: this.makeAddress(
HAS_PARENT_EDGE_TYPE,
hasParentEdgeId(commit.hash, oneBasedParentIndex)
),
src: commitNode.address,
dst: parentAddress,
payload: {
parentIndex: oneBasedParentIndex,
},
};
result.addEdge(parentEdge);
});
return result;
}
treeGraph(tree: Tree) {

View File

@ -8,6 +8,7 @@ import {
COMMIT_NODE_TYPE,
GIT_PLUGIN_NAME,
HAS_CONTENTS_EDGE_TYPE,
HAS_PARENT_EDGE_TYPE,
HAS_TREE_EDGE_TYPE,
INCLUDES_EDGE_TYPE,
TREE_ENTRY_NODE_TYPE,
@ -41,13 +42,22 @@ describe("createGraph", () => {
id: hash,
};
expect(graph.node(address)).toEqual({address, payload: {}});
expect(graph.neighborhood(address)).toHaveLength(1);
expect(
graph.neighborhood(address, {
nodeType: TREE_NODE_TYPE,
edgeType: HAS_TREE_EDGE_TYPE,
})
).toHaveLength(1);
expect(
graph.neighborhood(address, {
nodeType: COMMIT_NODE_TYPE,
edgeType: HAS_PARENT_EDGE_TYPE,
direction: "OUT",
})
).toHaveLength(data.commits[hash].parentHashes.length);
expect(graph.neighborhood(address, {direction: "OUT"})).toHaveLength(
1 + data.commits[hash].parentHashes.length
);
});
});

View File

@ -55,6 +55,28 @@ export type NodeType =
// Edges
// CommitNode -> CommitNode
export const HAS_PARENT_EDGE_TYPE: "HAS_PARENT" = "HAS_PARENT";
export type HasParentEdgePayload = {|
+parentIndex: number, // one-based
|};
export function hasParentEdgeId(
childCommitHash: Hash,
oneBasedParentIndex: number
) {
if (
!isFinite(oneBasedParentIndex) ||
oneBasedParentIndex !== Math.floor(oneBasedParentIndex) ||
oneBasedParentIndex < 1
) {
throw new Error(
"Expected positive integer parent index, " +
`but got: ${String(oneBasedParentIndex)}`
);
}
return `${childCommitHash}^${String(oneBasedParentIndex)}`;
}
// CommitNode -> TreeNode
export const HAS_TREE_EDGE_TYPE: "HAS_TREE" = "HAS_TREE";
export type HasTreeEdgePayload = {||};
@ -76,12 +98,14 @@ export type HasContentsEdgePayload = {||};
export type EdgeType =
| typeof HAS_TREE_EDGE_TYPE
| typeof HAS_PARENT_EDGE_TYPE
| typeof INCLUDES_EDGE_TYPE
| typeof BECOMES_EDGE_TYPE
| typeof HAS_CONTENTS_EDGE_TYPE;
export type EdgePayload =
| HasTreeEdgePayload
| HasParentEdgePayload
| IncludesEdgePayload
| BecomesEdgePayload
| HasContentsEdgePayload;