From aa071ceab38f3228cc280f31f6f416f85d89c1f3 Mon Sep 17 00:00:00 2001 From: William Chargin Date: Thu, 26 Apr 2018 20:11:44 -0700 Subject: [PATCH] Include a submodule in the main example repository (#156) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: The main example repository now covers the currently desired features: it has blobs, subtrees, and submodules, and commits that change each of these. (We don’t have merge commits yet—we can add those once we start to care about them.) Once this is merged, I will push the two repositories to GitHub. Test Plan: Verifying and understanding is easier than ever before. You can run the following commands to create the repositories in question on your disk: ```shell $ yarn backend $ node bin/createExampleRepo.js /tmp/repo $ node bin/createExampleRepo.js --submodule /tmp/subrepo ``` You can then explore these repositories at your leisure. For instance, to check that the `loadRepository` snapshot has the right set of commits, inspect the output of the following command: ```shell $ git -C /tmp/repo log --format='%H %T' ``` Or, to check that a particular tree has the right contents, just run: ```shell $ git -C /tmp/repo ls-tree TREE_SHA ``` Verifying the `exampleRepo` snapshot is similarly easy: just check that the lists of commit SHAs in `/tmp/repo` and `/tmp/subrepo` are correct. wchargin-branch: include-submodule --- .../__snapshots__/loadRepository.test.js.snap | 187 +++++++++++++++--- .../__snapshots__/exampleRepo.test.js.snap | 11 +- src/plugins/git/demoData/exampleRepo.js | 83 ++++++-- src/plugins/git/demoData/exampleRepo.test.js | 14 +- src/plugins/git/loadRepository.test.js | 30 --- 5 files changed, 249 insertions(+), 76 deletions(-) diff --git a/src/plugins/git/__snapshots__/loadRepository.test.js.snap b/src/plugins/git/__snapshots__/loadRepository.test.js.snap index 40c4b10..b3325d5 100644 --- a/src/plugins/git/__snapshots__/loadRepository.test.js.snap +++ b/src/plugins/git/__snapshots__/loadRepository.test.js.snap @@ -3,43 +3,75 @@ exports[`loadRepository loads from HEAD 1`] = ` Object { "commits": Map { - "677b340674bde17fdaac3b5f5eef929139ef2a52" => Object { - "hash": "677b340674bde17fdaac3b5f5eef929139ef2a52", - "treeHash": "6152a37dba8aa54dc4bc2d59c1f01c2afeba74b0", + "3507b7c8690e329477c76b73f8511f78fdbafe52" => Object { + "hash": "3507b7c8690e329477c76b73f8511f78fdbafe52", + "treeHash": "aeb32ba2769e46068cc09aa05723b5dd81c2e3a0", }, - "4be43f1cda04e51e42fec0cfe8e1e2dff116e839" => Object { - "hash": "4be43f1cda04e51e42fec0cfe8e1e2dff116e839", - "treeHash": "93642dbd1793e84a6f529a1e1b1b4f87a4f5c878", + "1468ff8437299dc4aa426996784be30a87226007" => Object { + "hash": "1468ff8437299dc4aa426996784be30a87226007", + "treeHash": "77e66e35964b9c0a2cde52fb2640d3004b742feb", }, - "cbb26b570d1eed3c681b8f03ff31231c1bffd6d6" => Object { - "hash": "cbb26b570d1eed3c681b8f03ff31231c1bffd6d6", - "treeHash": "f6736d27cd7eb7e35ae22a906854c700eb5cf6c1", + "d6693032379c8c55aafc72ad355db29ce71396b0" => Object { + "hash": "d6693032379c8c55aafc72ad355db29ce71396b0", + "treeHash": "c32b1ddc3a622e7a7e30c0b3f56a78eaed19cfab", }, - "301749e9af8cd6e9aee3a49a64029b98a4695e34" => Object { - "hash": "301749e9af8cd6e9aee3a49a64029b98a4695e34", - "treeHash": "4d5f2603a4b63aa68b8e51facf542a62e4c1d065", + "fb67eef11f7f34e4ad530ce36624c47fef0cd2e2" => Object { + "hash": "fb67eef11f7f34e4ad530ce36624c47fef0cd2e2", + "treeHash": "b936e6133d3316250f0a91779f72c72141279941", + }, + "849572148969b2c3480e8479604ccba3b189092a" => Object { + "hash": "849572148969b2c3480e8479604ccba3b189092a", + "treeHash": "e5e3b135d4f7d03e9906aead28c7757aa2b676bd", + }, + "db7608c2b4eba9ca72c6e3d636a7795cf9bcb97d" => Object { + "hash": "db7608c2b4eba9ca72c6e3d636a7795cf9bcb97d", + "treeHash": "5822dfcc81bf3ef8f99f68a21a33d07386fbd7b8", + }, + "486bb61573b09069dc9a55023b06e72049f8556d" => Object { + "hash": "486bb61573b09069dc9a55023b06e72049f8556d", + "treeHash": "79ee2359849733d6a51fd1b5da1cebdbaa29254f", }, }, "trees": Map { - "6152a37dba8aa54dc4bc2d59c1f01c2afeba74b0" => Object { + "aeb32ba2769e46068cc09aa05723b5dd81c2e3a0" => Object { "entries": Map { + ".gitmodules" => Object { + "hash": "8c6cac301e763aa6d36466e0a775eb804be2c311", + "name": ".gitmodules", + "type": "blob", + }, "README.txt" => Object { - "hash": "f1f2514ca6d7a6a1a0511957021b1995bf9ace1c", + "hash": "6e70bfb555b270a017c743df4cd7d35d5337e0ef", "name": "README.txt", "type": "blob", }, + "pygravitydefier" => Object { + "hash": "29ef158bc982733e2ba429fcf73e2f7562244188", + "name": "pygravitydefier", + "type": "commit", + }, + "science.txt" => Object { + "hash": "f1f2514ca6d7a6a1a0511957021b1995bf9ace1c", + "name": "science.txt", + "type": "blob", + }, "src" => Object { "hash": "78fc9c83023386854c6bfdc5761c0e58f68e226f", "name": "src", "type": "tree", }, }, - "hash": "6152a37dba8aa54dc4bc2d59c1f01c2afeba74b0", + "hash": "aeb32ba2769e46068cc09aa05723b5dd81c2e3a0", }, - "93642dbd1793e84a6f529a1e1b1b4f87a4f5c878" => Object { + "77e66e35964b9c0a2cde52fb2640d3004b742feb" => Object { "entries": Map { + ".gitmodules" => Object { + "hash": "8c6cac301e763aa6d36466e0a775eb804be2c311", + "name": ".gitmodules", + "type": "blob", + }, "README.txt" => Object { - "hash": "f1f2514ca6d7a6a1a0511957021b1995bf9ace1c", + "hash": "6e70bfb555b270a017c743df4cd7d35d5337e0ef", "name": "README.txt", "type": "blob", }, @@ -48,18 +80,33 @@ Object { "name": "TODOS.txt", "type": "blob", }, + "pygravitydefier" => Object { + "hash": "29ef158bc982733e2ba429fcf73e2f7562244188", + "name": "pygravitydefier", + "type": "commit", + }, + "science.txt" => Object { + "hash": "f1f2514ca6d7a6a1a0511957021b1995bf9ace1c", + "name": "science.txt", + "type": "blob", + }, "src" => Object { "hash": "78fc9c83023386854c6bfdc5761c0e58f68e226f", "name": "src", "type": "tree", }, }, - "hash": "93642dbd1793e84a6f529a1e1b1b4f87a4f5c878", + "hash": "77e66e35964b9c0a2cde52fb2640d3004b742feb", }, - "f6736d27cd7eb7e35ae22a906854c700eb5cf6c1" => Object { + "c32b1ddc3a622e7a7e30c0b3f56a78eaed19cfab" => Object { "entries": Map { + ".gitmodules" => Object { + "hash": "8c6cac301e763aa6d36466e0a775eb804be2c311", + "name": ".gitmodules", + "type": "blob", + }, "README.txt" => Object { - "hash": "f1f2514ca6d7a6a1a0511957021b1995bf9ace1c", + "hash": "6e70bfb555b270a017c743df4cd7d35d5337e0ef", "name": "README.txt", "type": "blob", }, @@ -68,23 +115,108 @@ Object { "name": "TODOS.txt", "type": "blob", }, + "pygravitydefier" => Object { + "hash": "29ef158bc982733e2ba429fcf73e2f7562244188", + "name": "pygravitydefier", + "type": "commit", + }, + "science.txt" => Object { + "hash": "f1f2514ca6d7a6a1a0511957021b1995bf9ace1c", + "name": "science.txt", + "type": "blob", + }, "src" => Object { "hash": "7b79d579b62994faba3b69fdf8aa442586c32681", "name": "src", "type": "tree", }, }, - "hash": "f6736d27cd7eb7e35ae22a906854c700eb5cf6c1", + "hash": "c32b1ddc3a622e7a7e30c0b3f56a78eaed19cfab", }, - "4d5f2603a4b63aa68b8e51facf542a62e4c1d065" => Object { + "b936e6133d3316250f0a91779f72c72141279941" => Object { + "entries": Map { + ".gitmodules" => Object { + "hash": "8c6cac301e763aa6d36466e0a775eb804be2c311", + "name": ".gitmodules", + "type": "blob", + }, + "README.txt" => Object { + "hash": "6e70bfb555b270a017c743df4cd7d35d5337e0ef", + "name": "README.txt", + "type": "blob", + }, + "TODOS.txt" => Object { + "hash": "ddec7477206c30c31b81482e56b877a0b3c2638b", + "name": "TODOS.txt", + "type": "blob", + }, + "pygravitydefier" => Object { + "hash": "762c062fbdc7ec198cd693e95d55b374a08ff3e3", + "name": "pygravitydefier", + "type": "commit", + }, + "science.txt" => Object { + "hash": "f1f2514ca6d7a6a1a0511957021b1995bf9ace1c", + "name": "science.txt", + "type": "blob", + }, + "src" => Object { + "hash": "7b79d579b62994faba3b69fdf8aa442586c32681", + "name": "src", + "type": "tree", + }, + }, + "hash": "b936e6133d3316250f0a91779f72c72141279941", + }, + "e5e3b135d4f7d03e9906aead28c7757aa2b676bd" => Object { + "entries": Map { + ".gitmodules" => Object { + "hash": "8c6cac301e763aa6d36466e0a775eb804be2c311", + "name": ".gitmodules", + "type": "blob", + }, + "README.txt" => Object { + "hash": "6e70bfb555b270a017c743df4cd7d35d5337e0ef", + "name": "README.txt", + "type": "blob", + }, + "pygravitydefier" => Object { + "hash": "762c062fbdc7ec198cd693e95d55b374a08ff3e3", + "name": "pygravitydefier", + "type": "commit", + }, + "science.txt" => Object { + "hash": "f1f2514ca6d7a6a1a0511957021b1995bf9ace1c", + "name": "science.txt", + "type": "blob", + }, + }, + "hash": "e5e3b135d4f7d03e9906aead28c7757aa2b676bd", + }, + "5822dfcc81bf3ef8f99f68a21a33d07386fbd7b8" => Object { "entries": Map { "README.txt" => Object { + "hash": "6e70bfb555b270a017c743df4cd7d35d5337e0ef", + "name": "README.txt", + "type": "blob", + }, + "science.txt" => Object { "hash": "f1f2514ca6d7a6a1a0511957021b1995bf9ace1c", + "name": "science.txt", + "type": "blob", + }, + }, + "hash": "5822dfcc81bf3ef8f99f68a21a33d07386fbd7b8", + }, + "79ee2359849733d6a51fd1b5da1cebdbaa29254f" => Object { + "entries": Map { + "README.txt" => Object { + "hash": "6e70bfb555b270a017c743df4cd7d35d5337e0ef", "name": "README.txt", "type": "blob", }, }, - "hash": "4d5f2603a4b63aa68b8e51facf542a62e4c1d065", + "hash": "79ee2359849733d6a51fd1b5da1cebdbaa29254f", }, "78fc9c83023386854c6bfdc5761c0e58f68e226f" => Object { "entries": Map { @@ -123,13 +255,12 @@ Object { exports[`loadRepository processes an old commit 1`] = ` Object { "commits": Set { - "cbb26b570d1eed3c681b8f03ff31231c1bffd6d6", - "301749e9af8cd6e9aee3a49a64029b98a4695e34", + "db7608c2b4eba9ca72c6e3d636a7795cf9bcb97d", + "486bb61573b09069dc9a55023b06e72049f8556d", }, "trees": Set { - "f6736d27cd7eb7e35ae22a906854c700eb5cf6c1", - "4d5f2603a4b63aa68b8e51facf542a62e4c1d065", - "7b79d579b62994faba3b69fdf8aa442586c32681", + "5822dfcc81bf3ef8f99f68a21a33d07386fbd7b8", + "79ee2359849733d6a51fd1b5da1cebdbaa29254f", }, } `; diff --git a/src/plugins/git/demoData/__snapshots__/exampleRepo.test.js.snap b/src/plugins/git/demoData/__snapshots__/exampleRepo.test.js.snap index 6931f83..c3a5541 100644 --- a/src/plugins/git/demoData/__snapshots__/exampleRepo.test.js.snap +++ b/src/plugins/git/demoData/__snapshots__/exampleRepo.test.js.snap @@ -2,10 +2,13 @@ exports[`createExampleRepo is deterministic 1`] = ` Array [ - "301749e9af8cd6e9aee3a49a64029b98a4695e34", - "cbb26b570d1eed3c681b8f03ff31231c1bffd6d6", - "4be43f1cda04e51e42fec0cfe8e1e2dff116e839", - "677b340674bde17fdaac3b5f5eef929139ef2a52", + "486bb61573b09069dc9a55023b06e72049f8556d", + "db7608c2b4eba9ca72c6e3d636a7795cf9bcb97d", + "849572148969b2c3480e8479604ccba3b189092a", + "fb67eef11f7f34e4ad530ce36624c47fef0cd2e2", + "d6693032379c8c55aafc72ad355db29ce71396b0", + "1468ff8437299dc4aa426996784be30a87226007", + "3507b7c8690e329477c76b73f8511f78fdbafe52", ] `; diff --git a/src/plugins/git/demoData/exampleRepo.js b/src/plugins/git/demoData/exampleRepo.js index e0f470d..daf5f88 100644 --- a/src/plugins/git/demoData/exampleRepo.js +++ b/src/plugins/git/demoData/exampleRepo.js @@ -1,6 +1,7 @@ // @flow import mkdirp from "mkdirp"; +import tmp from "tmp"; import {makeUtils} from "../gitUtils"; import type {Hash} from "../types"; @@ -10,17 +11,70 @@ type RepositoryInfo = {| +commits: $ReadOnlyArray, // in oldest-to-newest order |}; +// For determinism, the example repository's submodule URL must be set +// to a fixed value. We choose the URL of the GitHub mirror (it's as +// good as any other, and makes sense). +export const SUBMODULE_REMOTE_URL = + "https://github.com/sourcecred/example-git-submodule.git"; + +// The example repository checks out two different versions of the +// submodule, as given by the following two hashes. These must exist +// within the submodule; this property is checked by the test file for +// this module. +export const SUBMODULE_COMMIT_1: Hash = + "762c062fbdc7ec198cd693e95d55b374a08ff3e3"; +export const SUBMODULE_COMMIT_2: Hash = + "29ef158bc982733e2ba429fcf73e2f7562244188"; + +/** + * Create the main example repository. + */ export function createExampleRepo(intoDirectory: string): RepositoryInfo { const repositoryPath = intoDirectory; mkdirp(repositoryPath); const git = makeUtils(repositoryPath); const commits = []; + function commit(message) { + git.deterministicCommit(message); + commits.push(git.head()); + } git.exec(["init"]); - git.writeAndStage("README.txt", "Amazing physics going on...\n"); - git.deterministicCommit("Initial commit"); - commits.push(git.head()); + git.writeAndStage( + "README.txt", + [ + "example-git\n", + "-----------\n\n", + "This repository provides example data for the SourceCred Git plugin.\n", + "Pay no attention to the contents behind the curtain.\n", + ].join() + ); + commit("Initial commit"); + + git.writeAndStage("science.txt", "Amazing physics going on...\n"); + commit("Add repository description"); + + const tmpdir = tmp.dirSync({unsafeCleanup: true}); + const submoduleName = "pygravitydefier"; + createExampleSubmoduleRepo(tmpdir.name); + git.exec(["submodule", "add", "--quiet", tmpdir.name, submoduleName]); + // After cloning the submodule, we don't need it anymore. + tmpdir.removeCallback(); + // We need all our commits to be deterministic, which means that the + // submodule URL needs to be independent of the actual location on + // disk (which may be, e.g., a temporary directory). + git.exec([ + "config", + "--file=.gitmodules", + `submodule.${submoduleName}.url`, + SUBMODULE_REMOTE_URL, + ]); + git.exec(["submodule", "sync", "--quiet"]); + git.exec(["add", ".gitmodules"]); + git.exec(["-C", submoduleName, "checkout", SUBMODULE_COMMIT_1, "--quiet"]); + git.exec(["add", submoduleName]); + commit("Add gravity defiance module"); git.writeAndStage("src/index.py", "import antigravity\n"); git.writeAndStage( @@ -28,19 +82,20 @@ export function createExampleRepo(intoDirectory: string): RepositoryInfo { 'raise NotImplementedError("TODO(physicists)")\n' ); git.writeAndStage("TODOS.txt", "1. Resolve quantum gravity\n"); - git.deterministicCommit("Discover gravity"); - commits.push(git.head()); + commit("Discover gravity"); + + git.exec(["-C", submoduleName, "checkout", SUBMODULE_COMMIT_2, "--quiet"]); + git.exec(["add", submoduleName]); + commit("Pull quantum gravity defiance from upstream"); git.writeAndStage( "src/quantum_gravity.py", "import random\nif random.random() < 0.5:\n import antigravity\n" ); - git.deterministicCommit("Solve quantum gravity"); - commits.push(git.head()); + commit("Solve quantum gravity"); git.exec(["rm", "TODOS.txt"]); - git.deterministicCommit("Clean up TODOS"); - commits.push(git.head()); + commit("Clean up TODOS"); return {path: repositoryPath, commits}; } @@ -57,6 +112,10 @@ export function createExampleSubmoduleRepo( mkdirp(repositoryPath); const git = makeUtils(repositoryPath); const commits = []; + function commit(message) { + git.deterministicCommit(message); + commits.push(git.head()); + } git.exec(["init"]); @@ -69,12 +128,10 @@ export function createExampleSubmoduleRepo( "a submodule in a larger repository.\n", ].join("") ); - git.deterministicCommit("Initial commit"); - commits.push(git.head()); + commit("Initial commit"); git.writeAndStage("useless.txt", "Nothing to see here; move along.\n"); - git.deterministicCommit("Add a file, so that we have multiple commits"); - commits.push(git.head()); + commit("Add a file, so that we have multiple commits"); return {path: repositoryPath, commits}; } diff --git a/src/plugins/git/demoData/exampleRepo.test.js b/src/plugins/git/demoData/exampleRepo.test.js index 27d2e65..3365ff1 100644 --- a/src/plugins/git/demoData/exampleRepo.test.js +++ b/src/plugins/git/demoData/exampleRepo.test.js @@ -2,7 +2,12 @@ import tmp from "tmp"; -import {createExampleRepo, createExampleSubmoduleRepo} from "./exampleRepo"; +import { + createExampleRepo, + createExampleSubmoduleRepo, + SUBMODULE_COMMIT_1, + SUBMODULE_COMMIT_2, +} from "./exampleRepo"; const cleanups: (() => void)[] = []; afterAll(() => { @@ -27,4 +32,11 @@ describe("createExampleSubmoduleRepo", () => { it("is deterministic", () => { expect(createExampleSubmoduleRepo(mkdtemp()).commits).toMatchSnapshot(); }); + + it("includes all the SHAs that it should", () => { + const commits = createExampleSubmoduleRepo(mkdtemp()).commits; + expect(commits).toEqual( + expect.arrayContaining([SUBMODULE_COMMIT_1, SUBMODULE_COMMIT_2]) + ); + }); }); diff --git a/src/plugins/git/loadRepository.test.js b/src/plugins/git/loadRepository.test.js index 7ca45bb..8f951f3 100644 --- a/src/plugins/git/loadRepository.test.js +++ b/src/plugins/git/loadRepository.test.js @@ -44,34 +44,4 @@ describe("loadRepository", () => { trees: new Set(part.trees.keys()), }).toMatchSnapshot(); }); - - it("works with submodules", () => { - const repositoryPath = mkdtemp(); - const git = makeUtils(repositoryPath); - - const subproject = createExampleRepo(mkdtemp()); - - git.exec(["init"]); - git.exec(["submodule", "--quiet", "add", subproject.path, "physics"]); - git.deterministicCommit("Initial commit"); - - const head = git.head(); - - const repository = loadRepository(repositoryPath, "HEAD"); - const commit = repository.commits.get(head); - expect(commit).toEqual(expect.anything()); - if (commit == null) { - throw new Error("Unreachable"); - } - const tree = repository.trees.get(commit.treeHash); - expect(tree).toEqual(expect.anything()); - if (tree == null) { - throw new Error("Unreachable"); - } - expect(tree.entries.get("physics")).toEqual({ - type: "commit", - name: "physics", - hash: subproject.commits[subproject.commits.length - 1], - }); - }); });