@vibe-validate/git 0.18.4 → 0.19.0-rc.10
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/branch-cleanup.d.ts.map +1 -1
- package/dist/branch-cleanup.js +3 -2
- package/dist/branch-cleanup.js.map +1 -1
- package/dist/branch-sync.d.ts.map +1 -1
- package/dist/branch-sync.js +1 -0
- package/dist/branch-sync.js.map +1 -1
- package/dist/git-executor.d.ts +3 -1
- package/dist/git-executor.d.ts.map +1 -1
- package/dist/git-executor.js +8 -4
- package/dist/git-executor.js.map +1 -1
- package/dist/git-notes.d.ts +8 -7
- package/dist/git-notes.d.ts.map +1 -1
- package/dist/git-notes.js +25 -27
- package/dist/git-notes.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/post-merge-cleanup.js +4 -4
- package/dist/post-merge-cleanup.js.map +1 -1
- package/dist/staging.d.ts +18 -0
- package/dist/staging.d.ts.map +1 -1
- package/dist/staging.js +45 -2
- package/dist/staging.js.map +1 -1
- package/dist/test-helpers.d.ts +144 -0
- package/dist/test-helpers.d.ts.map +1 -0
- package/dist/test-helpers.js +195 -0
- package/dist/test-helpers.js.map +1 -0
- package/dist/tree-hash.d.ts +86 -4
- package/dist/tree-hash.d.ts.map +1 -1
- package/dist/tree-hash.js +180 -8
- package/dist/tree-hash.js.map +1 -1
- package/dist/types.d.ts +23 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -3
package/dist/staging.js
CHANGED
|
@@ -20,6 +20,49 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import { executeGitCommand } from './git-executor.js';
|
|
22
22
|
const GIT_TIMEOUT = 30000;
|
|
23
|
+
const GIT_NAME_ONLY_FLAG = '--name-only';
|
|
24
|
+
/**
|
|
25
|
+
* Get list of staged files (files in git index)
|
|
26
|
+
*
|
|
27
|
+
* Returns file paths relative to repository root for all files
|
|
28
|
+
* currently staged (Added, Copied, Modified, or Renamed).
|
|
29
|
+
*
|
|
30
|
+
* @param cwd - Working directory (defaults to process.cwd())
|
|
31
|
+
* @returns Array of staged file paths, empty if none or not a git repo
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* const files = getStagedFiles();
|
|
36
|
+
* if (files.length > 0) {
|
|
37
|
+
* console.log('Staged files:', files);
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export function getStagedFiles(cwd = process.cwd()) {
|
|
42
|
+
try {
|
|
43
|
+
const result = executeGitCommand([
|
|
44
|
+
'diff',
|
|
45
|
+
'--cached',
|
|
46
|
+
GIT_NAME_ONLY_FLAG,
|
|
47
|
+
'--diff-filter=ACMR', // Added, Copied, Modified, Renamed (not Deleted)
|
|
48
|
+
], {
|
|
49
|
+
cwd,
|
|
50
|
+
timeout: GIT_TIMEOUT,
|
|
51
|
+
ignoreErrors: true,
|
|
52
|
+
});
|
|
53
|
+
if (!result.success || !result.stdout) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
return result.stdout
|
|
57
|
+
.split('\n')
|
|
58
|
+
.map((f) => f.trim())
|
|
59
|
+
.filter((f) => f.length > 0);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Not a git repository, or git command failed
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
23
66
|
/**
|
|
24
67
|
* Get list of files with partially staged changes
|
|
25
68
|
*
|
|
@@ -44,7 +87,7 @@ const GIT_TIMEOUT = 30000;
|
|
|
44
87
|
export function getPartiallyStagedFiles() {
|
|
45
88
|
try {
|
|
46
89
|
// Get list of files with staged changes
|
|
47
|
-
const stagedResult = executeGitCommand(['diff',
|
|
90
|
+
const stagedResult = executeGitCommand(['diff', GIT_NAME_ONLY_FLAG, '--cached'], {
|
|
48
91
|
timeout: GIT_TIMEOUT,
|
|
49
92
|
ignoreErrors: true
|
|
50
93
|
});
|
|
@@ -60,7 +103,7 @@ export function getPartiallyStagedFiles() {
|
|
|
60
103
|
return [];
|
|
61
104
|
}
|
|
62
105
|
// Get list of files with unstaged changes
|
|
63
|
-
const unstagedResult = executeGitCommand(['diff',
|
|
106
|
+
const unstagedResult = executeGitCommand(['diff', GIT_NAME_ONLY_FLAG], {
|
|
64
107
|
timeout: GIT_TIMEOUT,
|
|
65
108
|
ignoreErrors: true
|
|
66
109
|
});
|
package/dist/staging.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"staging.js","sourceRoot":"","sources":["../src/staging.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,WAAW,GAAG,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"staging.js","sourceRoot":"","sources":["../src/staging.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,WAAW,GAAG,KAAK,CAAC;AAC1B,MAAM,kBAAkB,GAAG,aAAa,CAAC;AAEzC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IACxD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,iBAAiB,CAC9B;YACE,MAAM;YACN,UAAU;YACV,kBAAkB;YAClB,oBAAoB,EAAE,iDAAiD;SACxE,EACD;YACE,GAAG;YACH,OAAO,EAAE,WAAW;YACpB,YAAY,EAAE,IAAI;SACnB,CACF,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACtC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,OAAO,MAAM,CAAC,MAAM;aACjB,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,8CAA8C;QAC9C,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,uBAAuB;IACrC,IAAI,CAAC;QACH,wCAAwC;QACxC,MAAM,YAAY,GAAG,iBAAiB,CAAC,CAAC,MAAM,EAAE,kBAAkB,EAAE,UAAU,CAAC,EAAE;YAC/E,OAAO,EAAE,WAAW;YACpB,YAAY,EAAE,IAAI;SACnB,CAAC,CAAC;QAEH,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;YAC1B,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM;aACpC,IAAI,EAAE;aACN,KAAK,CAAC,IAAI,CAAC;aACX,MAAM,CAAC,OAAO,CAAC,CAAC;QAEnB,8CAA8C;QAC9C,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,0CAA0C;QAC1C,MAAM,cAAc,GAAG,iBAAiB,CAAC,CAAC,MAAM,EAAE,kBAAkB,CAAC,EAAE;YACrE,OAAO,EAAE,WAAW;YACpB,YAAY,EAAE,IAAI;SACnB,CAAC,CAAC;QAEH,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;YAC5B,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,aAAa,GAAG,IAAI,GAAG,CAC3B,cAAc,CAAC,MAAM;aAClB,IAAI,EAAE;aACN,KAAK,CAAC,IAAI,CAAC;aACX,MAAM,CAAC,OAAO,CAAC,CACnB,CAAC;QAEF,mEAAmE;QACnE,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CACjC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CACxB,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,8CAA8C;QAC9C,4EAA4E;QAC5E,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git Test Helpers
|
|
3
|
+
*
|
|
4
|
+
* Centralized git operations for test setup and verification.
|
|
5
|
+
* Single source of truth for git commands in tests.
|
|
6
|
+
*
|
|
7
|
+
* Security: All git command execution happens here, not scattered across tests.
|
|
8
|
+
* Auditability: One place to review for security vulnerabilities.
|
|
9
|
+
* Consistency: All tests use same patterns for git operations.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Initialize a git repository for testing
|
|
13
|
+
*
|
|
14
|
+
* @param repoPath - Path to initialize as git repo
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* const testDir = mkdtempSync(join(tmpdir(), 'test-'));
|
|
18
|
+
* initTestRepo(testDir);
|
|
19
|
+
*/
|
|
20
|
+
export declare function initTestRepo(repoPath: string): void;
|
|
21
|
+
/**
|
|
22
|
+
* Configure git user for test repository
|
|
23
|
+
*
|
|
24
|
+
* @param repoPath - Path to git repository
|
|
25
|
+
* @param email - User email (default: test@example.com)
|
|
26
|
+
* @param name - User name (default: Test User)
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* configTestUser(testDir);
|
|
30
|
+
* configTestUser(testDir, 'custom@example.com', 'Custom User');
|
|
31
|
+
*/
|
|
32
|
+
export declare function configTestUser(repoPath: string, email?: string, name?: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* Stage files in test repository
|
|
35
|
+
*
|
|
36
|
+
* @param repoPath - Path to git repository
|
|
37
|
+
* @param files - Files to stage (default: all files)
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* stageTestFiles(testDir);
|
|
41
|
+
* stageTestFiles(testDir, ['file1.txt', 'file2.txt']);
|
|
42
|
+
*/
|
|
43
|
+
export declare function stageTestFiles(repoPath: string, files?: string[]): void;
|
|
44
|
+
/**
|
|
45
|
+
* Create a commit in test repository
|
|
46
|
+
*
|
|
47
|
+
* @param repoPath - Path to git repository
|
|
48
|
+
* @param message - Commit message
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* commitTestChanges(testDir, 'Initial commit');
|
|
52
|
+
*/
|
|
53
|
+
export declare function commitTestChanges(repoPath: string, message: string): void;
|
|
54
|
+
/**
|
|
55
|
+
* Get tree hash for current HEAD
|
|
56
|
+
*
|
|
57
|
+
* @param repoPath - Path to git repository
|
|
58
|
+
* @returns Tree hash
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* const treeHash = getTestTreeHash(testDir);
|
|
62
|
+
*/
|
|
63
|
+
export declare function getTestTreeHash(repoPath: string): string;
|
|
64
|
+
/**
|
|
65
|
+
* Read git note for object
|
|
66
|
+
*
|
|
67
|
+
* @param repoPath - Path to git repository
|
|
68
|
+
* @param notesRef - Notes ref (e.g., 'refs/notes/vibe-validate/validate')
|
|
69
|
+
* @param object - Object hash to read note for
|
|
70
|
+
* @returns Note content or null if not found
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* const note = readTestNote(testDir, 'refs/notes/vibe-validate/validate', treeHash);
|
|
74
|
+
*/
|
|
75
|
+
export declare function readTestNote(repoPath: string, notesRef: string, object: string): string | null;
|
|
76
|
+
/**
|
|
77
|
+
* Setup a complete test repository with initial commit
|
|
78
|
+
*
|
|
79
|
+
* Combines: init, config user, create file, stage, commit
|
|
80
|
+
*
|
|
81
|
+
* @param repoPath - Path to initialize as git repo
|
|
82
|
+
* @param initialFile - Initial file to create (default: README.md)
|
|
83
|
+
* @param initialContent - Content for initial file (default: "# Test")
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* const testDir = mkdtempSync(join(tmpdir(), 'test-'));
|
|
87
|
+
* setupTestRepoWithCommit(testDir);
|
|
88
|
+
*/
|
|
89
|
+
export declare function setupTestRepoWithCommit(repoPath: string, initialFile?: string, initialContent?: string): void;
|
|
90
|
+
/**
|
|
91
|
+
* Configure git submodule protocol settings for local file URLs
|
|
92
|
+
*
|
|
93
|
+
* Required for testing submodules with file:// URLs
|
|
94
|
+
*
|
|
95
|
+
* @param repoPath - Path to git repository
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* configTestSubmoduleProtocol(testDir);
|
|
99
|
+
*/
|
|
100
|
+
export declare function configTestSubmoduleProtocol(repoPath: string): void;
|
|
101
|
+
/**
|
|
102
|
+
* Get current HEAD commit hash
|
|
103
|
+
*
|
|
104
|
+
* @param repoPath - Path to git repository
|
|
105
|
+
* @returns Commit SHA
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* const commitHash = getTestCommitHash(testDir);
|
|
109
|
+
*/
|
|
110
|
+
export declare function getTestCommitHash(repoPath: string): string;
|
|
111
|
+
/**
|
|
112
|
+
* Register a submodule in git index
|
|
113
|
+
*
|
|
114
|
+
* Low-level operation to add submodule entry without cloning
|
|
115
|
+
*
|
|
116
|
+
* @param repoPath - Path to git repository
|
|
117
|
+
* @param commitHash - Submodule commit hash
|
|
118
|
+
* @param submodulePath - Relative path for submodule
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* registerTestSubmodule(testDir, 'abc123...', 'libs/auth');
|
|
122
|
+
*/
|
|
123
|
+
export declare function registerTestSubmodule(repoPath: string, commitHash: string, submodulePath: string): void;
|
|
124
|
+
/**
|
|
125
|
+
* Initialize git submodules (register in .git/config)
|
|
126
|
+
*
|
|
127
|
+
* @param repoPath - Path to git repository
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* initTestSubmodules(testDir);
|
|
131
|
+
*/
|
|
132
|
+
export declare function initTestSubmodules(repoPath: string): void;
|
|
133
|
+
/**
|
|
134
|
+
* Add a submodule to the repository
|
|
135
|
+
*
|
|
136
|
+
* @param repoPath - Path to git repository
|
|
137
|
+
* @param submoduleUrl - URL of submodule repository
|
|
138
|
+
* @param submodulePath - Relative path where submodule should be placed
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* addTestSubmodule(testDir, '/tmp/sub-repo', 'libs/auth');
|
|
142
|
+
*/
|
|
143
|
+
export declare function addTestSubmodule(repoPath: string, submoduleUrl: string, submodulePath: string): void;
|
|
144
|
+
//# sourceMappingURL=test-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-helpers.d.ts","sourceRoot":"","sources":["../src/test-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAEnD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,KAAK,SAAqB,EAC1B,IAAI,SAAc,GACjB,IAAI,CAGN;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,EAAU,GAAG,IAAI,CAE9E;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAEzE;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOxD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAY9F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAc,EACzB,cAAc,SAAa,GAC1B,IAAI,CASN;AAED;;;;;;;;;GASG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAElE;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAO1D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,GACpB,IAAI,CAMN;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAEzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,GACpB,IAAI,CAMN"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git Test Helpers
|
|
3
|
+
*
|
|
4
|
+
* Centralized git operations for test setup and verification.
|
|
5
|
+
* Single source of truth for git commands in tests.
|
|
6
|
+
*
|
|
7
|
+
* Security: All git command execution happens here, not scattered across tests.
|
|
8
|
+
* Auditability: One place to review for security vulnerabilities.
|
|
9
|
+
* Consistency: All tests use same patterns for git operations.
|
|
10
|
+
*/
|
|
11
|
+
import { writeFileSync } from 'node:fs';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
import { safeExecSync, safeExecResult } from '@vibe-validate/utils';
|
|
14
|
+
/**
|
|
15
|
+
* Initialize a git repository for testing
|
|
16
|
+
*
|
|
17
|
+
* @param repoPath - Path to initialize as git repo
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* const testDir = mkdtempSync(join(tmpdir(), 'test-'));
|
|
21
|
+
* initTestRepo(testDir);
|
|
22
|
+
*/
|
|
23
|
+
export function initTestRepo(repoPath) {
|
|
24
|
+
safeExecSync('git', ['init'], { cwd: repoPath, stdio: 'pipe' });
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Configure git user for test repository
|
|
28
|
+
*
|
|
29
|
+
* @param repoPath - Path to git repository
|
|
30
|
+
* @param email - User email (default: test@example.com)
|
|
31
|
+
* @param name - User name (default: Test User)
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* configTestUser(testDir);
|
|
35
|
+
* configTestUser(testDir, 'custom@example.com', 'Custom User');
|
|
36
|
+
*/
|
|
37
|
+
export function configTestUser(repoPath, email = 'test@example.com', name = 'Test User') {
|
|
38
|
+
safeExecSync('git', ['config', 'user.email', email], { cwd: repoPath, stdio: 'pipe' });
|
|
39
|
+
safeExecSync('git', ['config', 'user.name', name], { cwd: repoPath, stdio: 'pipe' });
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Stage files in test repository
|
|
43
|
+
*
|
|
44
|
+
* @param repoPath - Path to git repository
|
|
45
|
+
* @param files - Files to stage (default: all files)
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* stageTestFiles(testDir);
|
|
49
|
+
* stageTestFiles(testDir, ['file1.txt', 'file2.txt']);
|
|
50
|
+
*/
|
|
51
|
+
export function stageTestFiles(repoPath, files = ['.']) {
|
|
52
|
+
safeExecSync('git', ['add', ...files], { cwd: repoPath, stdio: 'pipe' });
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Create a commit in test repository
|
|
56
|
+
*
|
|
57
|
+
* @param repoPath - Path to git repository
|
|
58
|
+
* @param message - Commit message
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* commitTestChanges(testDir, 'Initial commit');
|
|
62
|
+
*/
|
|
63
|
+
export function commitTestChanges(repoPath, message) {
|
|
64
|
+
safeExecSync('git', ['commit', '-m', message], { cwd: repoPath, stdio: 'pipe' });
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get tree hash for current HEAD
|
|
68
|
+
*
|
|
69
|
+
* @param repoPath - Path to git repository
|
|
70
|
+
* @returns Tree hash
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* const treeHash = getTestTreeHash(testDir);
|
|
74
|
+
*/
|
|
75
|
+
export function getTestTreeHash(repoPath) {
|
|
76
|
+
const result = safeExecSync('git', ['rev-parse', 'HEAD:'], {
|
|
77
|
+
cwd: repoPath,
|
|
78
|
+
encoding: 'utf-8',
|
|
79
|
+
stdio: 'pipe',
|
|
80
|
+
});
|
|
81
|
+
return result.trim();
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Read git note for object
|
|
85
|
+
*
|
|
86
|
+
* @param repoPath - Path to git repository
|
|
87
|
+
* @param notesRef - Notes ref (e.g., 'refs/notes/vibe-validate/validate')
|
|
88
|
+
* @param object - Object hash to read note for
|
|
89
|
+
* @returns Note content or null if not found
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* const note = readTestNote(testDir, 'refs/notes/vibe-validate/validate', treeHash);
|
|
93
|
+
*/
|
|
94
|
+
export function readTestNote(repoPath, notesRef, object) {
|
|
95
|
+
const result = safeExecResult('git', ['notes', '--ref', notesRef, 'show', object], {
|
|
96
|
+
cwd: repoPath,
|
|
97
|
+
encoding: 'utf-8',
|
|
98
|
+
stdio: 'pipe',
|
|
99
|
+
});
|
|
100
|
+
if (result.status !== 0) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
return result.stdout.trim();
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Setup a complete test repository with initial commit
|
|
107
|
+
*
|
|
108
|
+
* Combines: init, config user, create file, stage, commit
|
|
109
|
+
*
|
|
110
|
+
* @param repoPath - Path to initialize as git repo
|
|
111
|
+
* @param initialFile - Initial file to create (default: README.md)
|
|
112
|
+
* @param initialContent - Content for initial file (default: "# Test")
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* const testDir = mkdtempSync(join(tmpdir(), 'test-'));
|
|
116
|
+
* setupTestRepoWithCommit(testDir);
|
|
117
|
+
*/
|
|
118
|
+
export function setupTestRepoWithCommit(repoPath, initialFile = 'README.md', initialContent = '# Test\n') {
|
|
119
|
+
initTestRepo(repoPath);
|
|
120
|
+
configTestUser(repoPath);
|
|
121
|
+
// Create initial file
|
|
122
|
+
writeFileSync(join(repoPath, initialFile), initialContent);
|
|
123
|
+
stageTestFiles(repoPath);
|
|
124
|
+
commitTestChanges(repoPath, 'Initial commit');
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Configure git submodule protocol settings for local file URLs
|
|
128
|
+
*
|
|
129
|
+
* Required for testing submodules with file:// URLs
|
|
130
|
+
*
|
|
131
|
+
* @param repoPath - Path to git repository
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* configTestSubmoduleProtocol(testDir);
|
|
135
|
+
*/
|
|
136
|
+
export function configTestSubmoduleProtocol(repoPath) {
|
|
137
|
+
safeExecSync('git', ['config', 'protocol.file.allow', 'always'], { cwd: repoPath, stdio: 'pipe' });
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Get current HEAD commit hash
|
|
141
|
+
*
|
|
142
|
+
* @param repoPath - Path to git repository
|
|
143
|
+
* @returns Commit SHA
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* const commitHash = getTestCommitHash(testDir);
|
|
147
|
+
*/
|
|
148
|
+
export function getTestCommitHash(repoPath) {
|
|
149
|
+
const result = safeExecSync('git', ['rev-parse', 'HEAD'], {
|
|
150
|
+
cwd: repoPath,
|
|
151
|
+
encoding: 'utf-8',
|
|
152
|
+
stdio: 'pipe',
|
|
153
|
+
});
|
|
154
|
+
return result.trim();
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Register a submodule in git index
|
|
158
|
+
*
|
|
159
|
+
* Low-level operation to add submodule entry without cloning
|
|
160
|
+
*
|
|
161
|
+
* @param repoPath - Path to git repository
|
|
162
|
+
* @param commitHash - Submodule commit hash
|
|
163
|
+
* @param submodulePath - Relative path for submodule
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* registerTestSubmodule(testDir, 'abc123...', 'libs/auth');
|
|
167
|
+
*/
|
|
168
|
+
export function registerTestSubmodule(repoPath, commitHash, submodulePath) {
|
|
169
|
+
safeExecSync('git', ['update-index', '--add', '--cacheinfo', `160000,${commitHash},${submodulePath}`], { cwd: repoPath, stdio: 'pipe' });
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Initialize git submodules (register in .git/config)
|
|
173
|
+
*
|
|
174
|
+
* @param repoPath - Path to git repository
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* initTestSubmodules(testDir);
|
|
178
|
+
*/
|
|
179
|
+
export function initTestSubmodules(repoPath) {
|
|
180
|
+
safeExecSync('git', ['submodule', 'init'], { cwd: repoPath, stdio: 'pipe' });
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Add a submodule to the repository
|
|
184
|
+
*
|
|
185
|
+
* @param repoPath - Path to git repository
|
|
186
|
+
* @param submoduleUrl - URL of submodule repository
|
|
187
|
+
* @param submodulePath - Relative path where submodule should be placed
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* addTestSubmodule(testDir, '/tmp/sub-repo', 'libs/auth');
|
|
191
|
+
*/
|
|
192
|
+
export function addTestSubmodule(repoPath, submoduleUrl, submodulePath) {
|
|
193
|
+
safeExecSync('git', ['-c', 'protocol.file.allow=always', 'submodule', 'add', submoduleUrl, submodulePath], { cwd: repoPath, stdio: 'pipe' });
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=test-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-helpers.js","sourceRoot":"","sources":["../src/test-helpers.ts"],"names":[],"mappings":"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"}
|
package/dist/tree-hash.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* git stash create includes timestamps, making hashes non-deterministic.
|
|
11
11
|
* git write-tree produces content-based hashes only (no timestamps).
|
|
12
12
|
*/
|
|
13
|
-
import type { TreeHash } from './types.js';
|
|
13
|
+
import type { TreeHash, TreeHashResult } from './types.js';
|
|
14
14
|
/**
|
|
15
15
|
* Get deterministic git tree hash representing current working tree state
|
|
16
16
|
*
|
|
@@ -19,18 +19,55 @@ import type { TreeHash } from './types.js';
|
|
|
19
19
|
* 2. Copy current index to temporary index
|
|
20
20
|
* 3. Mark untracked files with --intent-to-add in temp index
|
|
21
21
|
* 4. Calculate tree hash with git write-tree using temp index
|
|
22
|
-
* 5.
|
|
22
|
+
* 5. Detect and process git submodules (recursive)
|
|
23
|
+
* 6. Return parent hash + optional submodule hashes
|
|
24
|
+
* 7. Clean up temp index file
|
|
23
25
|
*
|
|
24
26
|
* Why this is better than git stash create:
|
|
25
27
|
* - git stash create: includes timestamps in commit → different hash each time
|
|
26
28
|
* - git write-tree: content-based only → same content = same hash (deterministic)
|
|
27
29
|
*
|
|
30
|
+
* Submodule Support (Issue #120):
|
|
31
|
+
* - Detects submodules via `git submodule status`
|
|
32
|
+
* - Recursively calculates tree hash for each submodule
|
|
33
|
+
* - Returns TreeHashResult with parent hash + submodule hashes
|
|
34
|
+
* - Working tree changes in submodules invalidate cache
|
|
35
|
+
* - Git notes store full result for state reconstruction
|
|
36
|
+
*
|
|
37
|
+
* IMPORTANT: This function returns a structured result object, NOT a composite hash.
|
|
38
|
+
* Git notes store the TreeHashResult as-is. The hash field is the parent repo's
|
|
39
|
+
* standard Git SHA-1 hash (40 hex characters). The optional submoduleHashes field
|
|
40
|
+
* records each submodule's tree hash separately.
|
|
41
|
+
*
|
|
42
|
+
* Cache key format in git notes (v0.19.0+):
|
|
43
|
+
* - Parent-only repos: Use parent hash directly (backward compatible)
|
|
44
|
+
* - Repos with submodules: Use parent hash + submodule metadata
|
|
45
|
+
* - Result structure stored in git notes for state reconstruction
|
|
46
|
+
*
|
|
28
47
|
* CRITICAL: Uses GIT_INDEX_FILE to avoid corrupting real index during git commit hooks
|
|
29
48
|
*
|
|
30
|
-
* @returns
|
|
49
|
+
* @returns TreeHashResult containing:
|
|
50
|
+
* - hash: Parent repository tree hash (Git SHA-1, 40 hex chars)
|
|
51
|
+
* - submoduleHashes: Optional record of submodule paths to tree hashes
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // Repository without submodules (0.18.x compatible)
|
|
55
|
+
* const result = await getGitTreeHash();
|
|
56
|
+
* // { hash: 'abc123...' }
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* // Repository with submodules (v0.19.0+)
|
|
60
|
+
* const result = await getGitTreeHash();
|
|
61
|
+
* // {
|
|
62
|
+
* // hash: 'abc123...', // Parent repo hash
|
|
63
|
+
* // submoduleHashes: {
|
|
64
|
+
* // 'libs/auth': 'xyz789...'
|
|
65
|
+
* // }
|
|
66
|
+
* // }
|
|
67
|
+
*
|
|
31
68
|
* @throws Error if not in a git repository or git command fails
|
|
32
69
|
*/
|
|
33
|
-
export declare function getGitTreeHash(): Promise<
|
|
70
|
+
export declare function getGitTreeHash(): Promise<TreeHashResult>;
|
|
34
71
|
/**
|
|
35
72
|
* Get tree hash for HEAD commit (committed state only, no working tree changes)
|
|
36
73
|
*
|
|
@@ -46,4 +83,49 @@ export declare function getHeadTreeHash(): Promise<TreeHash>;
|
|
|
46
83
|
* @returns true if working tree differs from HEAD, false if clean
|
|
47
84
|
*/
|
|
48
85
|
export declare function hasWorkingTreeChanges(): Promise<boolean>;
|
|
86
|
+
/**
|
|
87
|
+
* Submodule information from git submodule status
|
|
88
|
+
* @internal Exported for testing
|
|
89
|
+
*/
|
|
90
|
+
export interface SubmoduleInfo {
|
|
91
|
+
/** Submodule path relative to repo root */
|
|
92
|
+
path: string;
|
|
93
|
+
/** Status character (' '=clean, '+'=modified, '-'=uninitialized, 'U'=conflict) */
|
|
94
|
+
status: string;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get list of git submodules in current repository
|
|
98
|
+
*
|
|
99
|
+
* Parses output of `git submodule status` to detect submodules.
|
|
100
|
+
* Returns empty array if no submodules or command fails.
|
|
101
|
+
*
|
|
102
|
+
* Output format: " abc123 libs/auth (heads/main)"
|
|
103
|
+
* ^^^^^^ ^^^^^^^^^ (description)
|
|
104
|
+
* hash path
|
|
105
|
+
*
|
|
106
|
+
* @returns Array of submodule information
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* const submodules = getSubmodules();
|
|
110
|
+
* // [{ path: 'libs/auth', status: ' ' }, { path: 'vendor/foo', status: '+' }]
|
|
111
|
+
*
|
|
112
|
+
* @internal Exported for testing
|
|
113
|
+
*/
|
|
114
|
+
export declare function getSubmodules(): SubmoduleInfo[];
|
|
115
|
+
/**
|
|
116
|
+
* Calculate tree hash for a git submodule (recursive)
|
|
117
|
+
*
|
|
118
|
+
* Changes to submodule directory, calculates tree hash, then returns to original directory.
|
|
119
|
+
* This is recursive - if the submodule has its own submodules, they will be included.
|
|
120
|
+
*
|
|
121
|
+
* @param submodulePath - Path to submodule relative to current directory
|
|
122
|
+
* @returns Tree hash result for the submodule
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* const result = await getSubmoduleTreeHash('libs/auth');
|
|
126
|
+
* // Returns TreeHashResult for libs/auth submodule
|
|
127
|
+
*
|
|
128
|
+
* @internal Exported for testing
|
|
129
|
+
*/
|
|
130
|
+
export declare function getSubmoduleTreeHash(submodulePath: string): Promise<TreeHashResult>;
|
|
49
131
|
//# sourceMappingURL=tree-hash.d.ts.map
|
package/dist/tree-hash.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree-hash.d.ts","sourceRoot":"","sources":["../src/tree-hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAQH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AA+
|
|
1
|
+
{"version":3,"file":"tree-hash.d.ts","sourceRoot":"","sources":["../src/tree-hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAQH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AA+G3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAEH,wBAAsB,cAAc,IAAI,OAAO,CAAC,cAAc,CAAC,CA2I9D;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,QAAQ,CAAC,CAUzD;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAS9D;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,IAAI,aAAa,EAAE,CAgD/C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CASzF"}
|