Skip to content

Commit b38439c

Browse files
committed
CIHelper: optionally clone the Git mailing list mirror
A couple of GitGitGadget's common operations require a local clone of the Git mailing list mirror. For obvious reasons, the idea here is to initialize a partial, shallow clone, so that missing objects (if any) will be retrieved on demand, and no time is wasted on a full clone every time GitGitGadget wants to look at mails. The not-so-obvious idea is to un-shallow the clone using the same strategy (and in fact, the same function) as for the Git "worktree": When I tried to simply delete the `shallow` file during the development of this patch, assuming that Git's partial clone functionality would gracefully fetch any missing commit e.g. when computing a merge base, reality taught me the harsh lesson that it would fail hard in such scenarios. Instead, the `git fetch --unshallow` command is called with `--filter=tree:0` so that _only_ the missing commits (and all of them) are fetched. This still seems to strike a good compromise between saving time on the clone and saving on network round-trips when fetching missing Git objects. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
1 parent 7979f53 commit b38439c

File tree

4 files changed

+68
-2
lines changed

4 files changed

+68
-2
lines changed

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
"CIsmtphost",
9797
"CIsmtppass",
9898
"CIsmtpopts",
99+
"mailrepo",
99100
"parsePRURLInput",
100101
"users\\.noreply\\.github\\.com",
101102
],

lib/ci-helper.ts

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export class CIHelper {
4646
private notesPushToken: string | undefined;
4747
private smtpOptions?: ISMTPOptions;
4848
protected maxCommitsExceptions: string[];
49+
protected mailingListMirror: string | undefined;
4950

5051
public static async getConfig(configFile?: string): Promise<IConfig> {
5152
return configFile ? await getExternalConfig(configFile) : getConfig();
@@ -66,7 +67,7 @@ export class CIHelper {
6667
this.urlRepo = `${this.urlBase}${this.config.repo.name}/`;
6768
}
6869

69-
public async setupGitHubAction(): Promise<void> {
70+
public async setupGitHubAction(setupOptions?: { needsMailingListMirror?: boolean }): Promise<void> {
7071
// help dugite realize where `git` is...
7172
const gitExecutable = os.type() === "Windows_NT" ? "git.exe" : "git";
7273
const stripSuffix = `bin${path.sep}${gitExecutable}`;
@@ -150,6 +151,58 @@ export class CIHelper {
150151
console.timeEnd(`Making ${workDir} non-shallow`);
151152
};
152153
await unshallow(this.workDir);
154+
155+
if (setupOptions?.needsMailingListMirror) {
156+
this.mailingListMirror = "mailing-list-mirror.git";
157+
const epoch = this.config.mailrepo.public_inbox_epoch ?? 1;
158+
159+
// eslint-disable-next-line security/detect-non-literal-fs-filename
160+
if (!fs.existsSync(this.mailingListMirror)) {
161+
await git(["init", "--bare", "--initial-branch", this.config.mailrepo.branch, this.mailingListMirror]);
162+
}
163+
164+
// First fetch from GitGitGadget's mirror, which supports partial clones
165+
for (const [key, value] of [
166+
["remote.mirror.url", this.config.mailrepo.mirrorURL || this.config.mailrepo.url],
167+
["remote.mirror.promisor", "true"],
168+
["remote.mirror.partialCloneFilter", "blob:none"],
169+
]) {
170+
await git(["config", key, value], { workDir: this.mailingListMirror });
171+
}
172+
console.time("fetch mailing list mirror");
173+
await git(
174+
[
175+
"-c",
176+
"remote.mirror.promisor=false", // let's fetch the mails with all of their contents
177+
"fetch",
178+
"mirror",
179+
"--depth=50",
180+
"+REF:REF".replace("REF", this.config.mailrepo.mirrorRef || `refs/heads/lore-${epoch}`),
181+
],
182+
{
183+
workDir: this.mailingListMirror,
184+
},
185+
);
186+
console.timeEnd("fetch mailing list mirror");
187+
188+
// Now update the head branch from the authoritative repository
189+
console.time(`update from ${this.config.mailrepo.url}`);
190+
await git(["config", "remote.origin.url", `${this.config.mailrepo.url.replace(/\/*$/, "")}/${epoch}`], {
191+
workDir: this.mailingListMirror,
192+
});
193+
await git(
194+
[
195+
"fetch",
196+
"origin",
197+
`+refs/heads/${this.config.mailrepo.branch}:refs/heads/${this.config.mailrepo.branch}`,
198+
],
199+
{
200+
workDir: this.mailingListMirror,
201+
},
202+
);
203+
console.timeEnd(`update from ${this.config.mailrepo.url}`);
204+
await unshallow(this.mailingListMirror);
205+
}
153206
}
154207

155208
public parsePRCommentURLInput(): { owner: string; repo: string; prNumber: number; commentId: number } {
@@ -938,7 +991,13 @@ export class CIHelper {
938991
}
939992
}
940993

941-
public async handleNewMails(mailArchiveGitDir: string, onlyPRs?: Set<number>): Promise<boolean> {
994+
public async handleNewMails(mailArchiveGitDir?: string, onlyPRs?: Set<number>): Promise<boolean> {
995+
if (!mailArchiveGitDir) {
996+
mailArchiveGitDir = this.mailingListMirror;
997+
if (!mailArchiveGitDir) {
998+
throw new Error("No mail archive directory specified (forgot to run `setupGitHubAction()`?)");
999+
}
1000+
}
9421001
await git(["fetch"], { workDir: mailArchiveGitDir });
9431002
const prFilter = !onlyPRs
9441003
? undefined

lib/gitgitgadget-config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ const defaultConfig: IConfig = {
1919
branch: "master",
2020
host: "lore.kernel.org",
2121
url: "https://lore.kernel.org/git/",
22+
public_inbox_epoch: 1,
23+
mirrorURL: "https://github.com/gitgitgadget/git-mailing-list-mirror",
24+
mirrorRef: "refs/heads/lore-1",
2225
descriptiveName: "lore.kernel/git",
2326
},
2427
mail: {

lib/project-config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ export interface IConfig {
2727
branch: string;
2828
host: string;
2929
url: string;
30+
public_inbox_epoch?: number;
31+
mirrorURL?: string;
32+
mirrorRef?: string;
3033
descriptiveName: string;
3134
};
3235
mail: {

0 commit comments

Comments
 (0)