@harryisfish/gitt 1.3.0 → 1.4.0
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/README.md +60 -1
- package/dist/commands/clean.js +65 -6
- package/dist/commands/config.js +33 -0
- package/dist/index.js +83 -2
- package/dist/utils/config.js +83 -0
- package/dist/utils/git.js +76 -0
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -19,12 +19,71 @@ pnpm add -g @harryisfish/gitt
|
|
|
19
19
|
## Usage
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
+
# Default behavior (auto-clean)
|
|
22
23
|
gitt
|
|
24
|
+
|
|
25
|
+
# Interactive mode (select branches to delete)
|
|
26
|
+
gitt -i
|
|
27
|
+
# or
|
|
28
|
+
gitt --interactive
|
|
29
|
+
|
|
30
|
+
# Dry run (preview what would be deleted)
|
|
31
|
+
gitt -d
|
|
32
|
+
# or
|
|
33
|
+
gitt --dry-run
|
|
23
34
|
```
|
|
24
35
|
|
|
25
36
|
## Configuration
|
|
26
37
|
|
|
27
|
-
|
|
38
|
+
### Main Branch Detection
|
|
39
|
+
|
|
40
|
+
Gitt automatically detects your main branch in the following order:
|
|
41
|
+
1. **`.gitt` configuration file** (Project level)
|
|
42
|
+
2. **Git config** `gitt.mainBranch` (User/System level)
|
|
43
|
+
3. **Remote HEAD** (e.g., `origin/HEAD`)
|
|
44
|
+
4. **Common names** (`main`, `master`)
|
|
45
|
+
|
|
46
|
+
### Setting the Main Branch
|
|
47
|
+
|
|
48
|
+
You can explicitly set the main branch for your project using the command:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
gitt set-main <branch-name>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Example:
|
|
55
|
+
```bash
|
|
56
|
+
gitt set-main master
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
gitt set-main master
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
This will create a `.gitt` file in your project root with your preference.
|
|
63
|
+
|
|
64
|
+
### Branch Protection
|
|
65
|
+
|
|
66
|
+
You can prevent specific branches from being deleted by adding them to the ignore list.
|
|
67
|
+
|
|
68
|
+
**Using command (Recommended):**
|
|
69
|
+
```bash
|
|
70
|
+
gitt ignore "release/*"
|
|
71
|
+
gitt ignore "test-branch"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Manual Configuration:**
|
|
75
|
+
You can also manually edit the `.gitt` configuration file:
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"mainBranch": "main",
|
|
79
|
+
"ignoreBranches": [
|
|
80
|
+
"release/*",
|
|
81
|
+
"test-branch",
|
|
82
|
+
"feature/important-*"
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
Supports glob patterns (e.g., `*`).
|
|
28
87
|
|
|
29
88
|
## Documentation
|
|
30
89
|
|
package/dist/commands/clean.js
CHANGED
|
@@ -3,26 +3,32 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.cleanDeletedBranches = cleanDeletedBranches;
|
|
4
4
|
const simple_git_1 = require("simple-git");
|
|
5
5
|
const listr2_1 = require("listr2");
|
|
6
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
7
|
+
const minimatch_1 = require("minimatch");
|
|
6
8
|
const errors_1 = require("../errors");
|
|
7
9
|
const errors_2 = require("../errors");
|
|
10
|
+
const git_1 = require("../utils/git");
|
|
11
|
+
const config_1 = require("../utils/config");
|
|
8
12
|
const git = (0, simple_git_1.simpleGit)();
|
|
9
13
|
/**
|
|
10
14
|
* Clean up local branches that have been deleted on the remote
|
|
11
15
|
* @throws {GitError} When cleaning operation fails
|
|
12
16
|
*/
|
|
13
|
-
async function cleanDeletedBranches() {
|
|
17
|
+
async function cleanDeletedBranches(options = {}) {
|
|
14
18
|
try {
|
|
15
19
|
const tasks = new listr2_1.Listr([
|
|
16
20
|
{
|
|
17
21
|
title: 'Fetch main from remote',
|
|
18
|
-
task: async () => {
|
|
19
|
-
await
|
|
22
|
+
task: async (ctx) => {
|
|
23
|
+
const mainBranch = await (0, git_1.getMainBranch)();
|
|
24
|
+
ctx.mainBranch = mainBranch;
|
|
25
|
+
await git.fetch(['origin', mainBranch]);
|
|
20
26
|
}
|
|
21
27
|
},
|
|
22
28
|
{
|
|
23
29
|
title: 'Switch to main branch',
|
|
24
|
-
task: async () => {
|
|
25
|
-
await git.checkout(
|
|
30
|
+
task: async (ctx) => {
|
|
31
|
+
await git.checkout(ctx.mainBranch);
|
|
26
32
|
}
|
|
27
33
|
},
|
|
28
34
|
{
|
|
@@ -36,13 +42,66 @@ async function cleanDeletedBranches() {
|
|
|
36
42
|
task: async (ctx) => {
|
|
37
43
|
await git.fetch(['--prune']);
|
|
38
44
|
const branchSummary = await git.branch(['-vv']);
|
|
39
|
-
const
|
|
45
|
+
const config = await (0, config_1.readConfigFile)();
|
|
46
|
+
const ignorePatterns = config.ignoreBranches || [];
|
|
47
|
+
let deletedBranches = branchSummary.all.filter(branch => {
|
|
40
48
|
const branchInfo = branchSummary.branches[branch];
|
|
41
49
|
return branchInfo.label && branchInfo.label.includes(': gone]');
|
|
42
50
|
});
|
|
51
|
+
// Filter out ignored branches
|
|
52
|
+
if (ignorePatterns.length > 0) {
|
|
53
|
+
deletedBranches = deletedBranches.filter(branch => {
|
|
54
|
+
const isIgnored = ignorePatterns.some(pattern => (0, minimatch_1.minimatch)(branch, pattern));
|
|
55
|
+
return !isIgnored;
|
|
56
|
+
});
|
|
57
|
+
}
|
|
43
58
|
ctx.deletedBranches = deletedBranches;
|
|
44
59
|
}
|
|
45
60
|
},
|
|
61
|
+
{
|
|
62
|
+
title: 'Select branches to delete',
|
|
63
|
+
enabled: (ctx) => !!options.interactive && Array.isArray(ctx.deletedBranches) && ctx.deletedBranches.length > 0,
|
|
64
|
+
task: async (ctx, task) => {
|
|
65
|
+
const branches = ctx.deletedBranches;
|
|
66
|
+
// Pause the listr task to show prompt
|
|
67
|
+
// We need to stop the spinner to show the prompt clearly
|
|
68
|
+
// But listr2 doesn't make it easy to pause/resume in the middle of a task flow easily without custom renderers
|
|
69
|
+
// So we will do the prompt and then update the context
|
|
70
|
+
// Actually, listr2 supports prompts but it's complex.
|
|
71
|
+
// Let's try to use the prompt directly.
|
|
72
|
+
// We might need to handle the UI interference.
|
|
73
|
+
// For now, let's just run the prompt.
|
|
74
|
+
// In a real TTY, listr2 might fight for control.
|
|
75
|
+
// A common pattern is to run the prompt outside listr or use listr's prompt adapter.
|
|
76
|
+
// But here we are inside a task.
|
|
77
|
+
// Let's try to use the prompt.
|
|
78
|
+
try {
|
|
79
|
+
const selected = await (0, prompts_1.checkbox)({
|
|
80
|
+
message: 'Select branches to delete:',
|
|
81
|
+
choices: branches.map(b => ({ name: b, value: b, checked: true })),
|
|
82
|
+
});
|
|
83
|
+
ctx.deletedBranches = selected;
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
// If user cancels, we might want to abort or just delete nothing
|
|
87
|
+
ctx.deletedBranches = [];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
title: 'Dry Run: List branches to be deleted',
|
|
93
|
+
enabled: (ctx) => !!options.dryRun && Array.isArray(ctx.deletedBranches) && ctx.deletedBranches.length > 0,
|
|
94
|
+
task: (ctx, task) => {
|
|
95
|
+
const branches = ctx.deletedBranches;
|
|
96
|
+
task.output = `The following branches would be deleted:\n${branches.map(b => ` - ${b}`).join('\n')}`;
|
|
97
|
+
// We don't want to fail, just show info.
|
|
98
|
+
// But listr output is transient.
|
|
99
|
+
// We will print it at the end or use a separate log.
|
|
100
|
+
console.log('\nDry Run: The following branches would be deleted:');
|
|
101
|
+
branches.forEach(b => console.log(` - ${b}`));
|
|
102
|
+
ctx.deletedBranches = []; // Clear so next step does nothing
|
|
103
|
+
}
|
|
104
|
+
},
|
|
46
105
|
{
|
|
47
106
|
title: 'Delete branches removed on remote',
|
|
48
107
|
enabled: (ctx) => Array.isArray(ctx.deletedBranches),
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.configMainBranch = configMainBranch;
|
|
4
|
+
exports.configIgnoreBranch = configIgnoreBranch;
|
|
5
|
+
const git_1 = require("../utils/git");
|
|
6
|
+
const errors_1 = require("../errors");
|
|
7
|
+
const config_1 = require("../utils/config");
|
|
8
|
+
async function configMainBranch(branch) {
|
|
9
|
+
try {
|
|
10
|
+
await (0, git_1.setMainBranch)(branch);
|
|
11
|
+
(0, errors_1.printSuccess)(`Successfully set main branch to '${branch}'`);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
(0, errors_1.handleError)(error);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async function configIgnoreBranch(pattern) {
|
|
18
|
+
try {
|
|
19
|
+
const config = await (0, config_1.readConfigFile)();
|
|
20
|
+
const ignoreBranches = config.ignoreBranches || [];
|
|
21
|
+
if (!ignoreBranches.includes(pattern)) {
|
|
22
|
+
ignoreBranches.push(pattern);
|
|
23
|
+
await (0, config_1.writeConfigFile)({ ignoreBranches });
|
|
24
|
+
(0, errors_1.printSuccess)(`Successfully added '${pattern}' to ignore list`);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
(0, errors_1.printSuccess)(`'${pattern}' is already in the ignore list`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
(0, errors_1.handleError)(error);
|
|
32
|
+
}
|
|
33
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
3
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
37
|
const simple_git_1 = require("simple-git");
|
|
5
38
|
const errors_1 = require("./errors");
|
|
@@ -41,12 +74,60 @@ async function checkGitRepo() {
|
|
|
41
74
|
throw new errors_1.GitError('Cannot access remote repository, please check network connection or repository permissions');
|
|
42
75
|
}
|
|
43
76
|
}
|
|
77
|
+
function printHelp() {
|
|
78
|
+
console.log(`
|
|
79
|
+
Usage: gitt [command] [options]
|
|
80
|
+
|
|
81
|
+
Commands:
|
|
82
|
+
(default) Clean up local branches that have been deleted on remote
|
|
83
|
+
set-main <branch> Set the main branch for the current project
|
|
84
|
+
ignore <pattern> Add a branch pattern to the ignore list (e.g., "release/*")
|
|
85
|
+
|
|
86
|
+
Options:
|
|
87
|
+
-i, --interactive Interactive mode: Select branches to delete
|
|
88
|
+
-d, --dry-run Dry run: Show what would be deleted without deleting
|
|
89
|
+
-h, --help Show this help message
|
|
90
|
+
|
|
91
|
+
Examples:
|
|
92
|
+
gitt # Auto-clean deleted branches
|
|
93
|
+
gitt -i # Select branches to delete interactively
|
|
94
|
+
gitt -d # Preview deletion
|
|
95
|
+
gitt ignore "temp/*" # Ignore branches matching "temp/*"
|
|
96
|
+
gitt set-main master # Set main branch to 'master'
|
|
97
|
+
`);
|
|
98
|
+
}
|
|
44
99
|
async function main() {
|
|
45
100
|
try {
|
|
101
|
+
const args = process.argv.slice(2);
|
|
102
|
+
const command = args[0];
|
|
103
|
+
// Check for help command
|
|
104
|
+
if (args.includes('-h') || args.includes('--help')) {
|
|
105
|
+
printHelp();
|
|
106
|
+
process.exit(0);
|
|
107
|
+
}
|
|
46
108
|
// 检查 Git 仓库
|
|
47
109
|
await checkGitRepo();
|
|
48
|
-
|
|
49
|
-
|
|
110
|
+
if (command === 'set-main') {
|
|
111
|
+
const branch = args[1];
|
|
112
|
+
if (!branch) {
|
|
113
|
+
throw new Error('Please specify a branch name');
|
|
114
|
+
}
|
|
115
|
+
await Promise.resolve().then(() => __importStar(require('./commands/config'))).then(m => m.configMainBranch(branch));
|
|
116
|
+
}
|
|
117
|
+
else if (command === 'ignore') {
|
|
118
|
+
const pattern = args[1];
|
|
119
|
+
if (!pattern) {
|
|
120
|
+
throw new Error('Please specify a branch pattern to ignore');
|
|
121
|
+
}
|
|
122
|
+
await Promise.resolve().then(() => __importStar(require('./commands/config'))).then(m => m.configIgnoreBranch(pattern));
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// Parse options
|
|
126
|
+
const isInteractive = args.includes('-i') || args.includes('--interactive');
|
|
127
|
+
const isDryRun = args.includes('-d') || args.includes('--dry-run');
|
|
128
|
+
// 默认执行清理操作
|
|
129
|
+
await (0, clean_1.cleanDeletedBranches)({ interactive: isInteractive, dryRun: isDryRun });
|
|
130
|
+
}
|
|
50
131
|
// 退出程序
|
|
51
132
|
process.exit(0);
|
|
52
133
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getProjectRoot = getProjectRoot;
|
|
37
|
+
exports.readConfigFile = readConfigFile;
|
|
38
|
+
exports.writeConfigFile = writeConfigFile;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const simple_git_1 = require("simple-git");
|
|
42
|
+
const CONFIG_FILE_NAME = '.gitt';
|
|
43
|
+
/**
|
|
44
|
+
* Get the project root directory (where .git is located).
|
|
45
|
+
*/
|
|
46
|
+
async function getProjectRoot() {
|
|
47
|
+
const git = (0, simple_git_1.simpleGit)();
|
|
48
|
+
try {
|
|
49
|
+
const root = await git.revparse(['--show-toplevel']);
|
|
50
|
+
return root.trim();
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
return process.cwd();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Read the .gitt configuration file.
|
|
58
|
+
*/
|
|
59
|
+
async function readConfigFile() {
|
|
60
|
+
try {
|
|
61
|
+
const root = await getProjectRoot();
|
|
62
|
+
const configPath = path.join(root, CONFIG_FILE_NAME);
|
|
63
|
+
if (!fs.existsSync(configPath)) {
|
|
64
|
+
return {};
|
|
65
|
+
}
|
|
66
|
+
const content = fs.readFileSync(configPath, 'utf-8');
|
|
67
|
+
return JSON.parse(content);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Write to the .gitt configuration file.
|
|
75
|
+
*/
|
|
76
|
+
async function writeConfigFile(config) {
|
|
77
|
+
const root = await getProjectRoot();
|
|
78
|
+
const configPath = path.join(root, CONFIG_FILE_NAME);
|
|
79
|
+
// Read existing config to merge
|
|
80
|
+
const currentConfig = await readConfigFile();
|
|
81
|
+
const newConfig = { ...currentConfig, ...config };
|
|
82
|
+
fs.writeFileSync(configPath, JSON.stringify(newConfig, null, 2));
|
|
83
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getMainBranch = getMainBranch;
|
|
4
|
+
exports.setMainBranch = setMainBranch;
|
|
5
|
+
const simple_git_1 = require("simple-git");
|
|
6
|
+
const errors_1 = require("../errors");
|
|
7
|
+
const config_1 = require("./config");
|
|
8
|
+
const git = (0, simple_git_1.simpleGit)();
|
|
9
|
+
/**
|
|
10
|
+
* Get the main branch name for the current repository.
|
|
11
|
+
* Priority:
|
|
12
|
+
* 1. User config (gitt.mainBranch)
|
|
13
|
+
* 2. Remote HEAD (origin/HEAD)
|
|
14
|
+
* 3. Common names (main, master)
|
|
15
|
+
*/
|
|
16
|
+
async function getMainBranch() {
|
|
17
|
+
try {
|
|
18
|
+
// 1. Check .gitt config file
|
|
19
|
+
const fileConfig = await (0, config_1.readConfigFile)();
|
|
20
|
+
if (fileConfig.mainBranch) {
|
|
21
|
+
return fileConfig.mainBranch;
|
|
22
|
+
}
|
|
23
|
+
// 2. Check user config (legacy/fallback)
|
|
24
|
+
const configMain = await git.getConfig('gitt.mainBranch');
|
|
25
|
+
if (configMain.value) {
|
|
26
|
+
return configMain.value;
|
|
27
|
+
}
|
|
28
|
+
// 3. Try to detect from remote HEAD
|
|
29
|
+
try {
|
|
30
|
+
const remotes = await git.listRemote(['--symref', 'origin', 'HEAD']);
|
|
31
|
+
// Output format example: "ref: refs/heads/main\tHEAD"
|
|
32
|
+
const match = remotes.match(/ref: refs\/heads\/([^\s]+)\s+HEAD/);
|
|
33
|
+
if (match && match[1]) {
|
|
34
|
+
return match[1];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
// Ignore network/remote errors during detection
|
|
39
|
+
}
|
|
40
|
+
// 4. Check for common local branches
|
|
41
|
+
const localBranches = await git.branchLocal();
|
|
42
|
+
if (localBranches.all.includes('main'))
|
|
43
|
+
return 'main';
|
|
44
|
+
if (localBranches.all.includes('master'))
|
|
45
|
+
return 'master';
|
|
46
|
+
// Default fallback
|
|
47
|
+
return 'main';
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
throw new errors_1.GitError('Failed to detect main branch');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Set the preferred main branch for the current repository.
|
|
55
|
+
*/
|
|
56
|
+
async function setMainBranch(branch) {
|
|
57
|
+
try {
|
|
58
|
+
// Verify branch exists locally or remotely
|
|
59
|
+
const localBranches = await git.branchLocal();
|
|
60
|
+
if (!localBranches.all.includes(branch)) {
|
|
61
|
+
// If not local, check if we can fetch it
|
|
62
|
+
try {
|
|
63
|
+
await git.fetch(['origin', branch]);
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
throw new errors_1.GitError(`Branch '${branch}' does not exist locally or on remote`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
await (0, config_1.writeConfigFile)({ mainBranch: branch });
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (error instanceof errors_1.GitError)
|
|
73
|
+
throw error;
|
|
74
|
+
throw new errors_1.GitError('Failed to set main branch configuration');
|
|
75
|
+
}
|
|
76
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@harryisfish/gitt",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "A command-line tool to help you manage Git repositories and remote repositories, such as keeping in sync, pushing, pulling, etc.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -38,8 +38,10 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@inquirer/prompts": "^3.3.0",
|
|
41
|
-
"
|
|
42
|
-
"listr2": "^8.0.0"
|
|
41
|
+
"@types/minimatch": "^6.0.0",
|
|
42
|
+
"listr2": "^8.0.0",
|
|
43
|
+
"minimatch": "^10.1.1",
|
|
44
|
+
"simple-git": "^3.22.0"
|
|
43
45
|
},
|
|
44
46
|
"engines": {
|
|
45
47
|
"node": ">=14.0.0"
|
|
@@ -50,4 +52,4 @@
|
|
|
50
52
|
"README.md",
|
|
51
53
|
"LICENSE"
|
|
52
54
|
]
|
|
53
|
-
}
|
|
55
|
+
}
|