0
# Git Operations
1
2
Comprehensive git integration for branch validation, tagging, history management, and pushing during the npm publishing workflow.
3
4
## Capabilities
5
6
### Git Information Functions
7
8
Functions for retrieving git repository information and status.
9
10
```javascript { .api }
11
/**
12
* Get the latest git tag in the repository
13
* @returns Promise resolving to the latest tag string
14
*/
15
function latestTag(): Promise<string>;
16
17
/**
18
* Get the git repository root directory
19
* @returns Promise resolving to absolute path of git root
20
*/
21
function root(): Promise<string>;
22
23
/**
24
* Get the name of the current git branch
25
* @returns Promise resolving to current branch name
26
*/
27
function getCurrentBranch(): Promise<string>;
28
29
/**
30
* Get the default branch name (main/master)
31
* @returns Promise resolving to default branch name
32
*/
33
function defaultBranch(): Promise<string>;
34
35
/**
36
* Check if current branch has an upstream
37
* @returns Promise resolving to true if upstream exists
38
*/
39
function hasUpstream(): Promise<boolean>;
40
41
/**
42
* Check if HEAD is in detached state
43
* @returns Promise resolving to true if HEAD is detached
44
*/
45
function isHeadDetached(): Promise<boolean>;
46
```
47
48
**Usage Examples:**
49
50
```javascript
51
import * as git from "np/source/git-util.js";
52
53
// Get repository information
54
const currentBranch = await git.getCurrentBranch();
55
const latestTag = await git.latestTag();
56
const gitRoot = await git.root();
57
58
console.log(`On branch: ${currentBranch}`);
59
console.log(`Latest tag: ${latestTag}`);
60
console.log(`Git root: ${gitRoot}`);
61
62
// Check repository state
63
const hasUpstream = await git.hasUpstream();
64
const isDetached = await git.isHeadDetached();
65
66
if (isDetached) {
67
throw new Error("Cannot publish from detached HEAD");
68
}
69
```
70
71
### Git Validation Functions
72
73
Functions for validating git repository state before publishing.
74
75
```javascript { .api }
76
/**
77
* Verify current branch is the designated release branch
78
* @param releaseBranch - Name of the release branch (e.g., 'main', 'master')
79
* @throws Error if not on release branch
80
*/
81
function verifyCurrentBranchIsReleaseBranch(releaseBranch: string): Promise<void>;
82
83
/**
84
* Verify working tree has no uncommitted changes
85
* @throws Error if working tree is dirty
86
*/
87
function verifyWorkingTreeIsClean(): Promise<void>;
88
89
/**
90
* Verify remote history is clean (no unpushed/unpulled commits)
91
* @throws Error if remote is out of sync
92
*/
93
function verifyRemoteHistoryIsClean(): Promise<void>;
94
95
/**
96
* Verify remote repository is valid and accessible
97
* @throws Error if remote is invalid
98
*/
99
function verifyRemoteIsValid(): Promise<void>;
100
101
/**
102
* Verify git version meets minimum requirements
103
* @throws Error if git version is too old
104
*/
105
function verifyRecentGitVersion(): Promise<void>;
106
107
/**
108
* Verify tag does not exist on remote repository
109
* @param tagName - Name of the tag to check
110
* @throws Error if tag already exists on remote
111
*/
112
function verifyTagDoesNotExistOnRemote(tagName: string): Promise<void>;
113
```
114
115
**Usage Examples:**
116
117
```javascript
118
import * as git from "np/source/git-util.js";
119
120
// Validate repository state before publishing
121
async function validateGitState(releaseBranch = "main") {
122
try {
123
// Check branch
124
await git.verifyCurrentBranchIsReleaseBranch(releaseBranch);
125
126
// Check working tree
127
await git.verifyWorkingTreeIsClean();
128
129
// Check remote sync
130
await git.verifyRemoteHistoryIsClean();
131
132
// Check git version
133
await git.verifyRecentGitVersion();
134
135
console.log("Git validation passed");
136
} catch (error) {
137
console.error(`Git validation failed: ${error.message}`);
138
throw error;
139
}
140
}
141
142
// Check if tag already exists
143
async function checkTagAvailability(version) {
144
const tagName = `v${version}`;
145
try {
146
await git.verifyTagDoesNotExistOnRemote(tagName);
147
console.log(`Tag ${tagName} is available`);
148
} catch (error) {
149
throw new Error(`Tag ${tagName} already exists on remote`);
150
}
151
}
152
```
153
154
### Git Operations Functions
155
156
Functions for performing git operations during the publishing workflow.
157
158
```javascript { .api }
159
/**
160
* Fetch latest changes from remote repository
161
* @returns Promise that resolves when fetch completes
162
*/
163
function fetch(): Promise<void>;
164
165
/**
166
* Push commits and tags to remote with graceful error handling
167
* @param remoteIsOnGitHub - True if remote is hosted on GitHub
168
* @returns Promise resolving to push result object
169
*/
170
function pushGraceful(remoteIsOnGitHub: boolean): Promise<GitPushResult>;
171
172
/**
173
* Delete a git tag locally and remotely
174
* @param tagName - Name of the tag to delete
175
* @returns Promise that resolves when tag is deleted
176
*/
177
function deleteTag(tagName: string): Promise<void>;
178
179
/**
180
* Remove the last commit from current branch
181
* @returns Promise that resolves when commit is removed
182
*/
183
function removeLastCommit(): Promise<void>;
184
```
185
186
**Usage Examples:**
187
188
```javascript
189
import * as git from "np/source/git-util.js";
190
191
// Fetch before validation
192
await git.fetch();
193
194
// Push with error handling
195
const pushResult = await git.pushGraceful(true); // GitHub remote
196
if (pushResult.reason) {
197
console.warn(`Push warning: ${pushResult.reason}`);
198
}
199
200
// Rollback operations
201
async function rollbackRelease(tagName) {
202
try {
203
await git.deleteTag(tagName);
204
await git.removeLastCommit();
205
console.log("Successfully rolled back release");
206
} catch (error) {
207
console.error(`Rollback failed: ${error.message}`);
208
}
209
}
210
```
211
212
### Git History Functions
213
214
Functions for analyzing git history and file changes.
215
216
```javascript { .api }
217
/**
218
* Get files that have changed since the last release
219
* @param rootDirectory - Root directory of the project
220
* @returns Promise resolving to array of changed file paths
221
*/
222
function newFilesSinceLastRelease(rootDirectory: string): Promise<string[]>;
223
224
/**
225
* Read file content from the last release
226
* @param file - Path to the file
227
* @returns Promise resolving to file content from last release
228
*/
229
function readFileFromLastRelease(file: string): Promise<string>;
230
231
/**
232
* Get the previous tag or first commit if no tags exist
233
* @returns Promise resolving to previous tag or commit hash
234
*/
235
function previousTagOrFirstCommit(): Promise<string>;
236
237
/**
238
* Get the latest tag or first commit if no tags exist
239
* @returns Promise resolving to latest tag or commit hash
240
*/
241
function latestTagOrFirstCommit(): Promise<string>;
242
243
/**
244
* Get commit log from a specific revision to HEAD
245
* @param revision - Git revision (tag, commit hash, branch)
246
* @returns Promise resolving to formatted commit log string
247
*/
248
function commitLogFromRevision(revision: string): Promise<string>;
249
```
250
251
**Usage Examples:**
252
253
```javascript
254
import * as git from "np/source/git-util.js";
255
256
// Analyze changes since last release
257
async function analyzeChanges(rootDirectory) {
258
const changedFiles = await git.newFilesSinceLastRelease(rootDirectory);
259
const previousTag = await git.previousTagOrFirstCommit();
260
261
console.log(`Changed files since ${previousTag}:`);
262
changedFiles.forEach(file => console.log(` ${file}`));
263
264
// Get commit history
265
const commits = await git.commitLogFromRevision(previousTag);
266
console.log(`Commits since last release: ${commits.length}`);
267
}
268
269
// Compare file versions
270
async function compareFileVersion(filePath) {
271
try {
272
const lastReleaseContent = await git.readFileFromLastRelease(filePath);
273
const currentContent = require('fs').readFileSync(filePath, 'utf8');
274
275
if (lastReleaseContent !== currentContent) {
276
console.log(`${filePath} has changed since last release`);
277
}
278
} catch (error) {
279
console.log(`${filePath} is a new file`);
280
}
281
}
282
```
283
284
## Git Workflow Integration
285
286
### Pre-publish Validation
287
288
Git validation steps performed before publishing:
289
290
1. **Git Version Check**: Ensure git version ≥ 2.11.0
291
2. **Remote Validation**: Verify remote repository is accessible
292
3. **Branch Validation**: Confirm on correct release branch
293
4. **Working Tree**: Ensure no uncommitted changes
294
5. **Remote Sync**: Verify local and remote are in sync
295
6. **Tag Availability**: Check version tag doesn't exist
296
297
### During Publishing
298
299
Git operations during the publishing workflow:
300
301
1. **Fetch**: Update remote tracking information
302
2. **Version Bump**: Package manager creates version commit and tag
303
3. **Validation**: Verify tag was created successfully
304
4. **Push**: Push commits and tags to remote
305
306
### Post-publish Operations
307
308
Git operations after successful publishing:
309
310
1. **Push Verification**: Confirm push was successful
311
2. **Release Notes**: Generate release notes from commits
312
3. **GitHub Integration**: Create release draft if configured
313
314
### Error Recovery
315
316
Git rollback operations on publishing failure:
317
318
1. **Tag Deletion**: Remove version tag if created
319
2. **Commit Removal**: Remove version bump commit
320
3. **State Restoration**: Return to pre-publish state
321
322
## Configuration
323
324
### Branch Configuration
325
326
Configure release branch behavior:
327
328
```javascript
329
// Default branch detection
330
const branch = await git.defaultBranch(); // 'main' or 'master'
331
332
// Custom branch
333
const options = {
334
branch: 'release',
335
anyBranch: false // Strict branch enforcement
336
};
337
338
// Allow any branch
339
const options = {
340
anyBranch: true // Skip branch validation
341
};
342
```
343
344
### Remote Configuration
345
346
Handle different remote configurations:
347
348
```javascript
349
// GitHub integration
350
const isGitHub = hostedGitInfo.fromUrl(repoUrl)?.type === 'github';
351
await git.pushGraceful(isGitHub);
352
353
// Custom remote handling
354
if (!await git.hasUpstream()) {
355
console.warn('No upstream configured, skipping push');
356
}
357
```
358
359
### Tag Configuration
360
361
Configure git tag behavior:
362
363
```javascript
364
// Tag prefix from package manager
365
const tagPrefix = await getTagVersionPrefix(packageManager);
366
const tagName = `${tagPrefix}${version}`; // e.g., 'v1.2.3'
367
368
// Custom tag message
369
const tagMessage = options.message?.replace('%s', version) || version;
370
```
371
372
## Error Conditions
373
374
### Common Git Errors
375
376
**Branch Errors:**
377
- Not on release branch
378
- Detached HEAD state
379
- Branch doesn't exist
380
381
**Working Tree Errors:**
382
- Uncommitted changes
383
- Untracked files in publish directory
384
- Merge conflicts
385
386
**Remote Errors:**
387
- No remote configured
388
- Remote unreachable
389
- Authentication failures
390
- Push/pull conflicts
391
392
**Tag Errors:**
393
- Tag already exists locally
394
- Tag already exists on remote
395
- Invalid tag format
396
397
### Error Messages
398
399
Git utilities provide detailed error messages:
400
401
```javascript
402
// Branch validation error
403
"Not on release branch. Currently on 'feature-branch', expected 'main'"
404
405
// Working tree error
406
"Working tree is not clean. Uncommitted changes in: package.json, src/index.js"
407
408
// Remote sync error
409
"Remote history has diverged. Run 'git pull' to sync with remote"
410
411
// Tag conflict error
412
"Tag 'v1.2.3' already exists on remote. Use a different version or delete the existing tag"
413
```