Skip to content

Commit 8d47c54

Browse files
committed
compare-screenshots: support checking out a specific commit
Allow specifying a commit to checkout before building by appending @commit to the worktree path, e.g. .@Head~2 or /path/to/worktree@main. The original branch or detached HEAD state is restored after taking the screenshot. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
1 parent d5a84b6 commit 8d47c54

1 file changed

Lines changed: 52 additions & 4 deletions

File tree

script/compare-screenshots.js

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ Usage:
88
99
Arguments can be URLs or paths to git-scm.com worktrees. When a worktree
1010
path is given, Hugo is run to build the site and a local server is started.
11+
Use worktree@commit to checkout a specific commit before building.
1112
Use worktree:/path/to/page to navigate to a specific page.
13+
Both can be combined: worktree@commit:/path/to/page
1214
1315
Options:
1416
--dark Emulate dark mode (prefers-color-scheme: dark)
@@ -18,6 +20,7 @@ Options:
1820
Examples:
1921
node script/compare-screenshots.js https://git-scm.com http://localhost:5000
2022
node script/compare-screenshots.js https://git-scm.com /path/to/worktree
23+
node script/compare-screenshots.js https://git-scm.com .@HEAD~2
2124
node script/compare-screenshots.js https://git-scm.com/docs/git-config /path/to/worktree:/docs/git-config
2225
node script/compare-screenshots.js --dark https://git-scm.com http://localhost:5000
2326
node script/compare-screenshots.js --clip=1280x720+0+0 https://git-scm.com http://localhost:5000`;
@@ -27,21 +30,53 @@ const { spawn, execSync } = require('child_process');
2730
const fs = require('fs');
2831
const path = require('path');
2932

33+
/**
34+
* Parse a worktree argument to extract worktree path, commit, and page path.
35+
*
36+
* Format: worktree[@commit][:/page/path]
37+
*
38+
* Examples:
39+
* . -> { worktreePath: '.', commit: undefined, pagePath: '' }
40+
* .@HEAD~2 -> { worktreePath: '.', commit: 'HEAD~2', pagePath: '' }
41+
* /path/to/worktree:/docs/git -> { worktreePath: '/path/to/worktree', commit: undefined, pagePath: 'docs/git' }
42+
* .@main:/about -> { worktreePath: '.', commit: 'main', pagePath: 'about' }
43+
*
44+
* Returns false if the argument is a URL or not a valid worktree.
45+
*/
3046
function getWorktreeInfo(arg) {
3147
if (arg.startsWith('http://') || arg.startsWith('https://')) return false;
3248
const colonIndex = arg.indexOf(':');
33-
const worktreePath = colonIndex === -1 ? arg : arg.slice(0, colonIndex);
49+
const beforeColon = colonIndex === -1 ? arg : arg.slice(0, colonIndex);
3450
const pagePath = colonIndex === -1 ? '' : arg.slice(colonIndex + 1).replace(/^\/+/, '');
51+
const atIndex = beforeColon.indexOf('@');
52+
const worktreePath = atIndex === -1 ? beforeColon : beforeColon.slice(0, atIndex);
53+
const commit = atIndex === -1 ? undefined : beforeColon.slice(atIndex + 1);
3554
try {
3655
if (fs.statSync(path.join(worktreePath, 'hugo.yml')).isFile()) {
37-
return { worktreePath, pagePath };
56+
return { worktreePath, commit, pagePath };
3857
}
3958
} catch {
4059
}
4160
return false;
4261
}
4362

44-
async function startServer(worktreePath, port) {
63+
async function startServer(worktreePath, port, commit) {
64+
let restoreRef;
65+
let wasDetached = false;
66+
67+
if (commit) {
68+
// Determine if we're on a branch (symbolic ref) or detached HEAD
69+
try {
70+
restoreRef = execSync('git symbolic-ref --short HEAD', { cwd: worktreePath, encoding: 'utf-8' }).trim();
71+
} catch {
72+
// Not on a branch, save the commit SHA
73+
restoreRef = execSync('git rev-parse HEAD', { cwd: worktreePath, encoding: 'utf-8' }).trim();
74+
wasDetached = true;
75+
}
76+
console.log(`Switching to ${commit} in ${worktreePath}...`);
77+
execSync(`git switch -d ${commit}`, { cwd: worktreePath, stdio: 'inherit' });
78+
}
79+
4580
// Build Hugo site
4681
console.error(`Building Hugo site in ${worktreePath}...`);
4782
execSync('hugo', { cwd: worktreePath, stdio: 'inherit' });
@@ -54,6 +89,18 @@ async function startServer(worktreePath, port) {
5489
stdio: ['ignore', 'pipe', 'inherit'],
5590
});
5691

92+
// Attach restore function to server
93+
server.restore = () => {
94+
if (restoreRef) {
95+
console.log(`Restoring ${worktreePath} to ${restoreRef}...`);
96+
if (wasDetached) {
97+
execSync(`git switch -d ${restoreRef}`, { cwd: worktreePath, stdio: 'inherit' });
98+
} else {
99+
execSync(`git switch ${restoreRef}`, { cwd: worktreePath, stdio: 'inherit' });
100+
}
101+
}
102+
};
103+
57104
// Wait for server to be ready
58105
await new Promise((resolve, reject) => {
59106
const timeout = setTimeout(() => reject(new Error('Server startup timeout')), 30000);
@@ -137,7 +184,7 @@ async function main() {
137184

138185
const worktreeInfo = getWorktreeInfo(urlOrWorktree);
139186
if (worktreeInfo) {
140-
server = await startServer(worktreeInfo.worktreePath, 5000);
187+
server = await startServer(worktreeInfo.worktreePath, 5000, worktreeInfo.commit);
141188
url = `http://localhost:5000/${worktreeInfo.pagePath}`;
142189
}
143190

@@ -156,6 +203,7 @@ async function main() {
156203
} finally {
157204
if (server) {
158205
server.kill();
206+
server.restore();
159207
}
160208
}
161209
}

0 commit comments

Comments
 (0)