hillclimb 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +123 -21
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -13515,7 +13515,7 @@ var EXEC_OPTS = {
|
|
|
13515
13515
|
maxBuffer: 50 * 1024 * 1024
|
|
13516
13516
|
};
|
|
13517
13517
|
var MAX_ERROR_OUTPUT_CHARS = 2e3;
|
|
13518
|
-
var
|
|
13518
|
+
var MAX_SNAPSHOT_FILE_BYTES = 10 * 1024 * 1024;
|
|
13519
13519
|
var EMPTY_TREE_SHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
13520
13520
|
function quoteGitArg(arg) {
|
|
13521
13521
|
if (/^[A-Za-z0-9_./:=@%+,-]+$/.test(arg)) return arg;
|
|
@@ -13619,7 +13619,81 @@ function captureSnapshotSha(repoRoot) {
|
|
|
13619
13619
|
}
|
|
13620
13620
|
return git(repoRoot, ["rev-parse", "HEAD"]);
|
|
13621
13621
|
}
|
|
13622
|
-
function
|
|
13622
|
+
function recordOmittedSnapshotFile(omittedFiles, file, options = {}) {
|
|
13623
|
+
omittedFiles?.push(file);
|
|
13624
|
+
if (options.log === false) return;
|
|
13625
|
+
const action = file.tracked ? "omitting tracked" : "skipping untracked";
|
|
13626
|
+
appendLog(
|
|
13627
|
+
"warn",
|
|
13628
|
+
`git-traces: ${action} ${file.path} (${file.sizeBytes} bytes > ${MAX_SNAPSHOT_FILE_BYTES} limit)`
|
|
13629
|
+
);
|
|
13630
|
+
}
|
|
13631
|
+
function parseLsTreeLongZ(output) {
|
|
13632
|
+
const entries = [];
|
|
13633
|
+
for (const record of output.toString("utf-8").split("\0")) {
|
|
13634
|
+
if (!record) continue;
|
|
13635
|
+
const tab = record.indexOf(" ");
|
|
13636
|
+
if (tab === -1) continue;
|
|
13637
|
+
const meta = record.slice(0, tab);
|
|
13638
|
+
const filePath = record.slice(tab + 1);
|
|
13639
|
+
const [mode, type, sha, sizeRaw] = meta.trim().split(/\s+/);
|
|
13640
|
+
if (type !== "blob" || !mode || !sha || !sizeRaw) continue;
|
|
13641
|
+
const sizeBytes = Number.parseInt(sizeRaw, 10);
|
|
13642
|
+
if (!Number.isFinite(sizeBytes)) continue;
|
|
13643
|
+
entries.push({ mode, sha, sizeBytes, path: filePath });
|
|
13644
|
+
}
|
|
13645
|
+
return entries;
|
|
13646
|
+
}
|
|
13647
|
+
function listOversizedTreeFiles(repoRoot, treeSha, options = {}) {
|
|
13648
|
+
const output = gitBuffer(repoRoot, ["ls-tree", "-r", "-l", "-z", treeSha]);
|
|
13649
|
+
const oversized = [];
|
|
13650
|
+
for (const entry of parseLsTreeLongZ(output)) {
|
|
13651
|
+
if (entry.sizeBytes <= MAX_SNAPSHOT_FILE_BYTES) continue;
|
|
13652
|
+
const file = {
|
|
13653
|
+
path: entry.path,
|
|
13654
|
+
sizeBytes: entry.sizeBytes,
|
|
13655
|
+
tracked: true,
|
|
13656
|
+
reason: "file-over-limit",
|
|
13657
|
+
gitObjectSha: entry.sha
|
|
13658
|
+
};
|
|
13659
|
+
oversized.push(file);
|
|
13660
|
+
recordOmittedSnapshotFile(options.omittedFiles, file, {
|
|
13661
|
+
log: options.log
|
|
13662
|
+
});
|
|
13663
|
+
}
|
|
13664
|
+
return oversized;
|
|
13665
|
+
}
|
|
13666
|
+
function removePathsFromIndex(repoRoot, env, paths) {
|
|
13667
|
+
if (paths.length === 0) return;
|
|
13668
|
+
gitBuffer(repoRoot, ["update-index", "--force-remove", "-z", "--stdin"], {
|
|
13669
|
+
input: `${paths.join("\0")}\0`,
|
|
13670
|
+
env
|
|
13671
|
+
});
|
|
13672
|
+
}
|
|
13673
|
+
function filterOversizedFilesFromTree(repoRoot, treeSha, options = {}) {
|
|
13674
|
+
const oversizedFiles = listOversizedTreeFiles(repoRoot, treeSha, options);
|
|
13675
|
+
if (oversizedFiles.length === 0) return treeSha;
|
|
13676
|
+
const tmpIndex = path14.join(
|
|
13677
|
+
os6.tmpdir(),
|
|
13678
|
+
`hillclimb-filter-${Date.now()}-${process.pid}`
|
|
13679
|
+
);
|
|
13680
|
+
const env = { ...process.env, GIT_INDEX_FILE: tmpIndex };
|
|
13681
|
+
try {
|
|
13682
|
+
gitWithEnv(repoRoot, ["read-tree", treeSha], env);
|
|
13683
|
+
removePathsFromIndex(
|
|
13684
|
+
repoRoot,
|
|
13685
|
+
env,
|
|
13686
|
+
oversizedFiles.map((file) => file.path)
|
|
13687
|
+
);
|
|
13688
|
+
return gitWithEnv(repoRoot, ["write-tree"], env);
|
|
13689
|
+
} finally {
|
|
13690
|
+
try {
|
|
13691
|
+
fs11.unlinkSync(tmpIndex);
|
|
13692
|
+
} catch {
|
|
13693
|
+
}
|
|
13694
|
+
}
|
|
13695
|
+
}
|
|
13696
|
+
function buildUntrackedTree(repoRoot, omittedFiles) {
|
|
13623
13697
|
const list = git(repoRoot, [
|
|
13624
13698
|
"ls-files",
|
|
13625
13699
|
"--others",
|
|
@@ -13632,11 +13706,13 @@ function buildUntrackedTree(repoRoot) {
|
|
|
13632
13706
|
if (!relPath) continue;
|
|
13633
13707
|
try {
|
|
13634
13708
|
const stat = fs11.lstatSync(path14.join(repoRoot, relPath));
|
|
13635
|
-
if (stat.size >
|
|
13636
|
-
|
|
13637
|
-
|
|
13638
|
-
|
|
13639
|
-
|
|
13709
|
+
if (stat.size > MAX_SNAPSHOT_FILE_BYTES) {
|
|
13710
|
+
recordOmittedSnapshotFile(omittedFiles, {
|
|
13711
|
+
path: relPath,
|
|
13712
|
+
sizeBytes: stat.size,
|
|
13713
|
+
tracked: false,
|
|
13714
|
+
reason: "file-over-limit"
|
|
13715
|
+
});
|
|
13640
13716
|
continue;
|
|
13641
13717
|
}
|
|
13642
13718
|
kept.push(relPath);
|
|
@@ -13662,19 +13738,25 @@ function buildUntrackedTree(repoRoot) {
|
|
|
13662
13738
|
}
|
|
13663
13739
|
}
|
|
13664
13740
|
}
|
|
13665
|
-
function buildSnapshotTree(repoRoot, stashSha) {
|
|
13741
|
+
function buildSnapshotTree(repoRoot, stashSha, options = {}) {
|
|
13666
13742
|
const trackedTree = git(repoRoot, ["rev-parse", `${stashSha}^{tree}`]);
|
|
13667
|
-
const
|
|
13668
|
-
|
|
13669
|
-
|
|
13670
|
-
|
|
13743
|
+
const filteredTrackedTree = filterOversizedFilesFromTree(
|
|
13744
|
+
repoRoot,
|
|
13745
|
+
trackedTree,
|
|
13746
|
+
{
|
|
13747
|
+
omittedFiles: options.omittedFiles
|
|
13748
|
+
}
|
|
13749
|
+
);
|
|
13750
|
+
const untrackedTree = buildUntrackedTree(repoRoot, options.omittedFiles);
|
|
13751
|
+
if (!untrackedTree || untrackedTree === EMPTY_TREE_SHA)
|
|
13752
|
+
return filteredTrackedTree;
|
|
13671
13753
|
const tmpIndex = path14.join(
|
|
13672
13754
|
os6.tmpdir(),
|
|
13673
13755
|
`hillclimb-index-${Date.now()}-${process.pid}`
|
|
13674
13756
|
);
|
|
13675
13757
|
const env = { ...process.env, GIT_INDEX_FILE: tmpIndex };
|
|
13676
13758
|
try {
|
|
13677
|
-
gitWithEnv(repoRoot, ["read-tree",
|
|
13759
|
+
gitWithEnv(repoRoot, ["read-tree", filteredTrackedTree], env);
|
|
13678
13760
|
const untrackedList = gitBuffer(
|
|
13679
13761
|
repoRoot,
|
|
13680
13762
|
["ls-tree", "-r", untrackedTree],
|
|
@@ -13727,11 +13809,22 @@ function createBundleFromTree(repoRoot, treeSha, sessionId, label) {
|
|
|
13727
13809
|
}
|
|
13728
13810
|
function createTreeDiffPatchGz(repoRoot, fromTreeSha, toTreeSha) {
|
|
13729
13811
|
if (fromTreeSha === toTreeSha) return null;
|
|
13812
|
+
const filteredFromTreeSha = filterOversizedFilesFromTree(
|
|
13813
|
+
repoRoot,
|
|
13814
|
+
fromTreeSha,
|
|
13815
|
+
{
|
|
13816
|
+
log: false
|
|
13817
|
+
}
|
|
13818
|
+
);
|
|
13819
|
+
const filteredToTreeSha = filterOversizedFilesFromTree(repoRoot, toTreeSha, {
|
|
13820
|
+
log: false
|
|
13821
|
+
});
|
|
13822
|
+
if (filteredFromTreeSha === filteredToTreeSha) return null;
|
|
13730
13823
|
const diff = gitBuffer(repoRoot, [
|
|
13731
13824
|
"diff",
|
|
13732
13825
|
"--binary",
|
|
13733
|
-
|
|
13734
|
-
|
|
13826
|
+
filteredFromTreeSha,
|
|
13827
|
+
filteredToTreeSha
|
|
13735
13828
|
]);
|
|
13736
13829
|
if (diff.length === 0) return null;
|
|
13737
13830
|
return Buffer.from(gzipSync(diff));
|
|
@@ -13778,7 +13871,7 @@ function parseDirtyFilesFromStatus(status) {
|
|
|
13778
13871
|
return pathPart;
|
|
13779
13872
|
}).filter(Boolean);
|
|
13780
13873
|
}
|
|
13781
|
-
function buildBaselineMetadata(repoRoot, sessionId, tool, baselineSha, cliVersion, epoch, prevHeadSha, transitionKind, startedAt) {
|
|
13874
|
+
function buildBaselineMetadata(repoRoot, sessionId, tool, baselineSha, cliVersion, epoch, prevHeadSha, transitionKind, startedAt, omittedFiles = []) {
|
|
13782
13875
|
const headSha = safeGit(repoRoot, ["rev-parse", "HEAD"]) ?? "unknown";
|
|
13783
13876
|
const branch = safeGit(repoRoot, ["rev-parse", "--abbrev-ref", "HEAD"]) ?? null;
|
|
13784
13877
|
const remoteUrl = safeGit(repoRoot, ["config", "--get", "remote.origin.url"]) ?? null;
|
|
@@ -13802,7 +13895,8 @@ function buildBaselineMetadata(repoRoot, sessionId, tool, baselineSha, cliVersio
|
|
|
13802
13895
|
branch,
|
|
13803
13896
|
remoteUrl,
|
|
13804
13897
|
isDirty: dirtyFiles.length > 0,
|
|
13805
|
-
dirtyFiles
|
|
13898
|
+
dirtyFiles,
|
|
13899
|
+
...omittedFiles.length > 0 ? { omittedFiles } : {}
|
|
13806
13900
|
},
|
|
13807
13901
|
author: { name: authorName, email: authorEmail },
|
|
13808
13902
|
hostname: os6.hostname(),
|
|
@@ -14246,6 +14340,10 @@ function freezeEpochBaseline(params) {
|
|
|
14246
14340
|
sessionId,
|
|
14247
14341
|
epoch
|
|
14248
14342
|
);
|
|
14343
|
+
const omittedFiles = [];
|
|
14344
|
+
const baselineTreeSha = buildSnapshotTree(repoRoot, baselineSha, {
|
|
14345
|
+
omittedFiles
|
|
14346
|
+
});
|
|
14249
14347
|
const baselineMetadata = buildBaselineMetadata(
|
|
14250
14348
|
repoRoot,
|
|
14251
14349
|
sessionId,
|
|
@@ -14255,9 +14353,9 @@ function freezeEpochBaseline(params) {
|
|
|
14255
14353
|
epoch,
|
|
14256
14354
|
prevHeadSha,
|
|
14257
14355
|
transitionKind,
|
|
14258
|
-
startedAt
|
|
14356
|
+
startedAt,
|
|
14357
|
+
omittedFiles
|
|
14259
14358
|
);
|
|
14260
|
-
const baselineTreeSha = buildSnapshotTree(repoRoot, baselineSha);
|
|
14261
14359
|
pinFrozenBaselineTree(repoRoot, sessionId, epoch, baselineTreeSha);
|
|
14262
14360
|
return { baselineSha, baselineTreeSha, baselineMetadata, headSha };
|
|
14263
14361
|
} catch (err) {
|
|
@@ -14303,6 +14401,10 @@ function buildEpochBaselineArtifacts(params) {
|
|
|
14303
14401
|
} = params;
|
|
14304
14402
|
const prefix = epochPrefix(epoch);
|
|
14305
14403
|
try {
|
|
14404
|
+
const omittedFiles = [];
|
|
14405
|
+
const baselineTreeSha = buildSnapshotTree(repoRoot, baselineSha, {
|
|
14406
|
+
omittedFiles
|
|
14407
|
+
});
|
|
14306
14408
|
const metadata = buildBaselineMetadata(
|
|
14307
14409
|
repoRoot,
|
|
14308
14410
|
sessionId,
|
|
@@ -14312,9 +14414,9 @@ function buildEpochBaselineArtifacts(params) {
|
|
|
14312
14414
|
epoch,
|
|
14313
14415
|
prevHeadSha,
|
|
14314
14416
|
transitionKind,
|
|
14315
|
-
startedAt
|
|
14417
|
+
startedAt,
|
|
14418
|
+
omittedFiles
|
|
14316
14419
|
);
|
|
14317
|
-
const baselineTreeSha = buildSnapshotTree(repoRoot, baselineSha);
|
|
14318
14420
|
return buildFrozenEpochBaselineArtifacts({
|
|
14319
14421
|
repoRoot,
|
|
14320
14422
|
sessionId,
|