Skip to content

Commit 174ca58

Browse files
committed
setup first test case experiment.spec.ts
Signed-off-by: Kipras Melnikovas <kipras@kipras.org>
1 parent 0ecff8c commit 174ca58

File tree

5 files changed

+269
-5
lines changed

5 files changed

+269
-5
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
},
1111
"scripts": {
1212
"prebuild": "node ./script/prebuild.js",
13+
"test": "ts-node-dev ./test/run.ts",
1314
"build": "yarn tsc -b",
1415
"postbuild": "node ./script/postbuild.js",
1516
"prepack": "yarn build"

parse-todo-of-stacked-rebase/validator.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export const regularRebaseCommands = {
143143
// m: standardCommand,
144144
} as const;
145145

146-
type RegularRebaseCommand = keyof typeof regularRebaseCommands;
146+
export type RegularRebaseCommand = keyof typeof regularRebaseCommands;
147147

148148
/**
149149
* TODO: assert each value is `RegularRebaseCommand`,
@@ -219,7 +219,7 @@ export const stackedRebaseCommands = {
219219
}),
220220
} as const;
221221

222-
type StackedRebaseCommand = keyof typeof stackedRebaseCommands;
222+
export type StackedRebaseCommand = keyof typeof stackedRebaseCommands;
223223

224224
// const allowedCommandAliasesFromGitStackedRebase: { [key: string]: AllowedGitStackedRebaseCommand } = {
225225
const stackedRebaseCommandAliases = {
@@ -229,10 +229,10 @@ const stackedRebaseCommandAliases = {
229229

230230
type StackedRebaseCommandAlias = keyof typeof stackedRebaseCommandAliases;
231231

232-
type EitherRebaseCommand = RegularRebaseCommand | StackedRebaseCommand;
233-
type EitherRebaseCommandAlias = RegularRebaseCommandAlias | StackedRebaseCommandAlias;
232+
export type EitherRebaseCommand = RegularRebaseCommand | StackedRebaseCommand;
233+
export type EitherRebaseCommandAlias = RegularRebaseCommandAlias | StackedRebaseCommandAlias;
234234

235-
type EitherRebaseEitherCommandOrAlias = EitherRebaseCommand | EitherRebaseCommandAlias;
235+
export type EitherRebaseEitherCommandOrAlias = EitherRebaseCommand | EitherRebaseCommandAlias;
236236

237237
type MapOfAllowedRebaseCommands = {
238238
[key in EitherRebaseCommand]: Command;

test/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
folders-to-delete
2+
.tmp

test/experiment.spec.ts

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
#!/usr/bin/env ts-node-dev
2+
3+
import fs from "fs";
4+
import path from "path";
5+
import assert from "assert";
6+
7+
import Git from "nodegit";
8+
9+
import { gitStackedRebase, defaultGitCmd } from "../git-stacked-rebase";
10+
11+
import { RegularRebaseCommand } from "../parse-todo-of-stacked-rebase/validator";
12+
import { createExecSyncInRepo } from "../util/execSyncInRepo";
13+
import { configKeys } from "../configKeys";
14+
15+
export async function testCase() {
16+
const {
17+
repo, //
18+
config,
19+
sig,
20+
dir,
21+
} = await setupRepo();
22+
23+
const commitOidsInInitial: Git.Oid[] = [];
24+
const initialBranch: Git.Reference = await appendCommitsTo(commitOidsInInitial, 3, repo, sig);
25+
26+
const latestStackedBranch: Git.Reference = await Git.Branch.create(
27+
repo,
28+
"stack-latest",
29+
await repo.getHeadCommit(),
30+
0
31+
);
32+
await repo.checkoutBranch(latestStackedBranch);
33+
34+
const execSyncInRepo = createExecSyncInRepo(repo);
35+
36+
// const read = () => execSyncInRepo("read");
37+
const read = () => void 0;
38+
39+
read();
40+
41+
const commitOidsInLatestStacked: Git.Oid[] = [];
42+
await appendCommitsTo(commitOidsInLatestStacked, 12, repo, sig);
43+
44+
const newPartialBranches = [
45+
["partial-1", 4],
46+
["partial-2", 6],
47+
["partial-3", 8],
48+
] as const;
49+
50+
console.log("launching 1st rebase to create partial branches");
51+
await gitStackedRebase(initialBranch.shorthand(), {
52+
gitDir: dir,
53+
getGitConfig: () => config,
54+
editor: async ({ filePath }) => {
55+
console.log("filePath %s", filePath);
56+
57+
for (const [newPartial, nthCommit] of newPartialBranches) {
58+
await humanOpAppendLineAfterNthCommit(
59+
filePath,
60+
commitOidsInLatestStacked[nthCommit].tostrS(),
61+
`branch-end-new ${newPartial}`
62+
);
63+
}
64+
65+
console.log("finished editor");
66+
67+
read();
68+
},
69+
});
70+
71+
console.log("looking up branches to make sure they were created successfully");
72+
read();
73+
for (const [newPartial] of newPartialBranches) {
74+
/**
75+
* will throw if branch does not exist
76+
* TODO "properly" expect to not throw
77+
*/
78+
await Git.Branch.lookup(repo, newPartial, Git.Branch.BRANCH.LOCAL);
79+
}
80+
81+
/**
82+
*
83+
*/
84+
console.log("launching 2nd rebase to change command of nth commit");
85+
read();
86+
87+
const nthCommit2ndRebase = 5;
88+
89+
await gitStackedRebase(initialBranch.shorthand(), {
90+
gitDir: dir,
91+
getGitConfig: () => config,
92+
editor: async ({ filePath }) => {
93+
const SHA = commitOidsInLatestStacked[nthCommit2ndRebase].tostrS();
94+
95+
humanOpChangeCommandOfNthCommitInto("edit", SHA, filePath);
96+
},
97+
});
98+
/**
99+
* rebase will now exit because of the "edit" command,
100+
* and so will our stacked rebase,
101+
* allowing us to edit.
102+
*/
103+
104+
fs.writeFileSync(nthCommit2ndRebase.toString(), "new data from 2nd rebase\n");
105+
106+
execSyncInRepo(`${defaultGitCmd} add .`);
107+
execSyncInRepo(`${defaultGitCmd} -c commit.gpgSign=false commit --amend --no-edit`);
108+
109+
execSyncInRepo(`${defaultGitCmd} rebase --continue`);
110+
111+
execSyncInRepo(`${defaultGitCmd} status`);
112+
read();
113+
114+
/**
115+
* now some partial branches will be "gone" from the POV of the latestBranch<->initialBranch.
116+
*
117+
* TODO verify they are gone (history got modified successfully)
118+
*/
119+
120+
// TODO
121+
122+
/**
123+
* TODO continue with --apply
124+
* TODO and then verify that partial branches are "back" in our POV properly.
125+
*/
126+
127+
console.log("attempting early 3rd rebase to --apply");
128+
read();
129+
130+
await gitStackedRebase(initialBranch.shorthand(), {
131+
gitDir: dir,
132+
getGitConfig: () => config,
133+
apply: true,
134+
});
135+
}
136+
137+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
138+
export async function setupRepo() {
139+
const dir: string = path.join(__dirname, ".tmp");
140+
if (fs.existsSync(dir)) {
141+
fs.rmdirSync(dir, { recursive: true });
142+
}
143+
fs.mkdirSync(dir);
144+
console.log("tmpdir path %s", dir);
145+
146+
const foldersToDeletePath: string = path.join(__dirname, "folders-to-delete");
147+
fs.appendFileSync(foldersToDeletePath, dir + "\n", { encoding: "utf-8" });
148+
149+
process.chdir(dir);
150+
console.log("chdir to tmpdir");
151+
152+
const isBare = 0;
153+
const repo: Git.Repository = await Git.Repository.init(dir, isBare);
154+
155+
const config: Git.Config = await repo.config();
156+
157+
await config.setBool(configKeys.autoApplyIfNeeded, Git.Config.MAP.FALSE);
158+
await config.setString("user.email", "tester@test.com");
159+
await config.setString("user.name", "tester");
160+
161+
/**
162+
* fixups / not implemented in libgit2.
163+
* though, would be better if received empty/minimal config by default..
164+
*/
165+
await config.setString("merge.conflictStyle", "diff3"); // zdiff3
166+
167+
const sig: Git.Signature = await Git.Signature.default(repo);
168+
console.log("sig %s", sig);
169+
170+
const inicialCommitId = "Initial commit (from setupRepo)";
171+
const initialCommit: Git.Oid = await fs.promises
172+
.writeFile(inicialCommitId, inicialCommitId) //
173+
.then(() => repo.createCommitOnHead([inicialCommitId], sig, sig, inicialCommitId));
174+
175+
return {
176+
dir,
177+
repo,
178+
config,
179+
sig,
180+
initialCommit,
181+
} as const;
182+
}
183+
184+
async function appendCommitsTo(
185+
alreadyExistingCommits: Git.Oid[],
186+
n: number,
187+
repo: Git.Repository, //
188+
sig: Git.Signature
189+
): Promise<Git.Reference> {
190+
assert(n > 0, "cannot append <= 0 commits");
191+
192+
const commits: string[] = new Array(n)
193+
.fill(0) //
194+
.map((_, i) => "a".charCodeAt(0) + i + alreadyExistingCommits.length)
195+
.map((ascii) => String.fromCharCode(ascii));
196+
197+
for (const c of commits) {
198+
const branchName: string = repo.isEmpty() ? "<initial>" : (await repo.getCurrentBranch()).shorthand();
199+
const cInBranch: string = c + " in " + branchName;
200+
201+
const oid: Git.Oid = await fs.promises
202+
.writeFile(c, cInBranch) //
203+
.then(() => repo.createCommitOnHead([c], sig, sig, cInBranch));
204+
205+
alreadyExistingCommits.push(oid);
206+
207+
console.log(`oid of commit "%s" in branch "%s": %s`, c, branchName, oid);
208+
}
209+
210+
return repo.getCurrentBranch();
211+
}
212+
213+
/**
214+
* TODO general "HumanOp" for `appendLineAfterNthCommit` & similar utils
215+
*/
216+
async function humanOpAppendLineAfterNthCommit(
217+
filePath: string, //
218+
commitSHA: string,
219+
newLine: string
220+
): Promise<void> {
221+
const file = await fs.promises.readFile(filePath, { encoding: "utf-8" });
222+
const lines = file.split("\n");
223+
const lineIdx: number = lines.findIndex((line) => line.startsWith(`pick ${commitSHA}`));
224+
225+
console.log("commitSHA: %s, lineIdx: %s, newLine: %s", commitSHA, lineIdx, newLine);
226+
227+
lines.splice(lineIdx, 0, newLine);
228+
229+
await fs.promises.writeFile(filePath, lines.join("\n"));
230+
}
231+
232+
function humanOpChangeCommandOfNthCommitInto(
233+
newCommand: RegularRebaseCommand, //
234+
commitSHA: string,
235+
filePath: string
236+
): void {
237+
const file = fs.readFileSync(filePath, { encoding: "utf-8" });
238+
const lines = file.split("\n");
239+
const lineIdx: number = lines.findIndex((line) => line.startsWith(`pick ${commitSHA}`));
240+
241+
console.log("commitSHA: %s, lineIdx: %s, newCommand: %s", commitSHA, lineIdx, newCommand);
242+
243+
const parts = lines[lineIdx].split(" ");
244+
parts[0] = newCommand;
245+
lines[lineIdx] = parts.join(" ");
246+
247+
fs.writeFileSync(filePath, lines.join("\n"));
248+
}

test/run.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env ts-node-dev
2+
3+
import { testCase } from "./experiment.spec";
4+
5+
main();
6+
function main() {
7+
testCase()
8+
.then(() => process.stdout.write("\nsuccess\n\n"))
9+
.catch((e) => {
10+
process.stderr.write("\nfailure: " + e + "\n\n");
11+
process.exit(1);
12+
});
13+
}

0 commit comments

Comments
 (0)