codeowners-git 2.0.2 → 2.2.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 +362 -3
- package/dist/cli.js +630 -46
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5,15 +5,29 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
function __accessProp(key) {
|
|
9
|
+
return this[key];
|
|
10
|
+
}
|
|
11
|
+
var __toESMCache_node;
|
|
12
|
+
var __toESMCache_esm;
|
|
8
13
|
var __toESM = (mod, isNodeMode, target) => {
|
|
14
|
+
var canCache = mod != null && typeof mod === "object";
|
|
15
|
+
if (canCache) {
|
|
16
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
17
|
+
var cached = cache.get(mod);
|
|
18
|
+
if (cached)
|
|
19
|
+
return cached;
|
|
20
|
+
}
|
|
9
21
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
22
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
23
|
for (let key of __getOwnPropNames(mod))
|
|
12
24
|
if (!__hasOwnProp.call(to, key))
|
|
13
25
|
__defProp(to, key, {
|
|
14
|
-
get: (
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
15
27
|
enumerable: true
|
|
16
28
|
});
|
|
29
|
+
if (canCache)
|
|
30
|
+
cache.set(mod, to);
|
|
17
31
|
return to;
|
|
18
32
|
};
|
|
19
33
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
@@ -2287,7 +2301,7 @@ var require_ignore = __commonJS((exports, module) => {
|
|
|
2287
2301
|
}
|
|
2288
2302
|
}, {
|
|
2289
2303
|
key: "filter",
|
|
2290
|
-
value: function
|
|
2304
|
+
value: function filter2(paths) {
|
|
2291
2305
|
var _this = this;
|
|
2292
2306
|
return make_array(paths).filter(function(path) {
|
|
2293
2307
|
return _this._filter(path);
|
|
@@ -2438,7 +2452,7 @@ var require_ignore = __commonJS((exports, module) => {
|
|
|
2438
2452
|
}
|
|
2439
2453
|
if (typeof process !== "undefined" && (process.env && process.env.IGNORE_TEST_WIN32 || process.platform === "win32")) {
|
|
2440
2454
|
filter = IgnoreBase.prototype._filter;
|
|
2441
|
-
make_posix = function
|
|
2455
|
+
make_posix = function make_posix2(str) {
|
|
2442
2456
|
return /^\\\\\?\\/.test(str) || /[^\x00-\x80]+/.test(str) ? str : str.replace(/\\/g, "/");
|
|
2443
2457
|
};
|
|
2444
2458
|
IgnoreBase.prototype._filter = function(path, slices) {
|
|
@@ -3039,25 +3053,25 @@ var require_minimatch = __commonJS((exports, module) => {
|
|
|
3039
3053
|
return minimatch;
|
|
3040
3054
|
}
|
|
3041
3055
|
var orig = minimatch;
|
|
3042
|
-
var m = function
|
|
3056
|
+
var m = function minimatch2(p, pattern, options) {
|
|
3043
3057
|
return orig(p, pattern, ext(def, options));
|
|
3044
3058
|
};
|
|
3045
|
-
m.Minimatch = function
|
|
3059
|
+
m.Minimatch = function Minimatch2(pattern, options) {
|
|
3046
3060
|
return new orig.Minimatch(pattern, ext(def, options));
|
|
3047
3061
|
};
|
|
3048
3062
|
m.Minimatch.defaults = function defaults(options) {
|
|
3049
3063
|
return orig.defaults(ext(def, options)).Minimatch;
|
|
3050
3064
|
};
|
|
3051
|
-
m.filter = function
|
|
3065
|
+
m.filter = function filter2(pattern, options) {
|
|
3052
3066
|
return orig.filter(pattern, ext(def, options));
|
|
3053
3067
|
};
|
|
3054
3068
|
m.defaults = function defaults(options) {
|
|
3055
3069
|
return orig.defaults(ext(def, options));
|
|
3056
3070
|
};
|
|
3057
|
-
m.makeRe = function
|
|
3071
|
+
m.makeRe = function makeRe2(pattern, options) {
|
|
3058
3072
|
return orig.makeRe(pattern, ext(def, options));
|
|
3059
3073
|
};
|
|
3060
|
-
m.braceExpand = function
|
|
3074
|
+
m.braceExpand = function braceExpand2(pattern, options) {
|
|
3061
3075
|
return orig.braceExpand(pattern, ext(def, options));
|
|
3062
3076
|
};
|
|
3063
3077
|
m.match = function(list, pattern, options) {
|
|
@@ -6584,7 +6598,7 @@ var require_colors = __commonJS((exports, module) => {
|
|
|
6584
6598
|
colors.stripColors = colors.strip = function(str) {
|
|
6585
6599
|
return ("" + str).replace(/\x1B\[\d+m/g, "");
|
|
6586
6600
|
};
|
|
6587
|
-
var stylize = colors.stylize = function
|
|
6601
|
+
var stylize = colors.stylize = function stylize2(str, style) {
|
|
6588
6602
|
if (!colors.enabled) {
|
|
6589
6603
|
return str + "";
|
|
6590
6604
|
}
|
|
@@ -6602,8 +6616,8 @@ var require_colors = __commonJS((exports, module) => {
|
|
|
6602
6616
|
return str.replace(matchOperatorsRe, "\\$&");
|
|
6603
6617
|
};
|
|
6604
6618
|
function build(_styles) {
|
|
6605
|
-
var builder = function
|
|
6606
|
-
return applyStyle2.apply(
|
|
6619
|
+
var builder = function builder2() {
|
|
6620
|
+
return applyStyle2.apply(builder2, arguments);
|
|
6607
6621
|
};
|
|
6608
6622
|
builder._styles = _styles;
|
|
6609
6623
|
builder.__proto__ = proto2;
|
|
@@ -6622,7 +6636,7 @@ var require_colors = __commonJS((exports, module) => {
|
|
|
6622
6636
|
});
|
|
6623
6637
|
return ret;
|
|
6624
6638
|
}();
|
|
6625
|
-
var proto2 = defineProps(function
|
|
6639
|
+
var proto2 = defineProps(function colors2() {}, styles3);
|
|
6626
6640
|
function applyStyle2() {
|
|
6627
6641
|
var args = Array.prototype.slice.call(arguments);
|
|
6628
6642
|
var str = args.map(function(arg) {
|
|
@@ -6681,7 +6695,7 @@ var require_colors = __commonJS((exports, module) => {
|
|
|
6681
6695
|
});
|
|
6682
6696
|
return ret;
|
|
6683
6697
|
}
|
|
6684
|
-
var sequencer = function
|
|
6698
|
+
var sequencer = function sequencer2(map2, str) {
|
|
6685
6699
|
var exploded = str.split("");
|
|
6686
6700
|
exploded = exploded.map(map2);
|
|
6687
6701
|
return exploded.join("");
|
|
@@ -11612,7 +11626,7 @@ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
|
11612
11626
|
var __esm = (fn, res) => function __init() {
|
|
11613
11627
|
return fn && (res = (0, fn[__getOwnPropNames2(fn)[0]])(fn = 0)), res;
|
|
11614
11628
|
};
|
|
11615
|
-
var __commonJS2 = (cb, mod) => function
|
|
11629
|
+
var __commonJS2 = (cb, mod) => function __require2() {
|
|
11616
11630
|
return mod || (0, cb[__getOwnPropNames2(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
11617
11631
|
};
|
|
11618
11632
|
var __export = (target, all) => {
|
|
@@ -16080,6 +16094,26 @@ var import_cli_table3 = __toESM(require_table(), 1);
|
|
|
16080
16094
|
var DEFAULT_COLUMN_WIDTH = 50;
|
|
16081
16095
|
var MAX_LINE_LENGTH = 80;
|
|
16082
16096
|
var MAX_PATH_LENGTH = 60;
|
|
16097
|
+
var _silent = false;
|
|
16098
|
+
var _origConsoleLog = console.log;
|
|
16099
|
+
var _origConsoleWarn = console.warn;
|
|
16100
|
+
var _origConsoleError = console.error;
|
|
16101
|
+
var noop = () => {};
|
|
16102
|
+
var setSilent = (silent) => {
|
|
16103
|
+
_silent = silent;
|
|
16104
|
+
if (silent) {
|
|
16105
|
+
console.log = noop;
|
|
16106
|
+
console.warn = noop;
|
|
16107
|
+
console.error = noop;
|
|
16108
|
+
} else {
|
|
16109
|
+
console.log = _origConsoleLog;
|
|
16110
|
+
console.warn = _origConsoleWarn;
|
|
16111
|
+
console.error = _origConsoleError;
|
|
16112
|
+
}
|
|
16113
|
+
};
|
|
16114
|
+
var outputJson = (data) => {
|
|
16115
|
+
_origConsoleLog(JSON.stringify(data, null, 2));
|
|
16116
|
+
};
|
|
16083
16117
|
var log = {
|
|
16084
16118
|
success: (message) => console.log(source_default.green(`✓ ${message}`)),
|
|
16085
16119
|
error: (message) => console.error(source_default.red(`✗ ${message}`)),
|
|
@@ -16154,6 +16188,14 @@ var hasUnstagedChanges = async () => {
|
|
|
16154
16188
|
const unstagedFiles = await getUnstagedFiles();
|
|
16155
16189
|
return unstagedFiles.length > 0;
|
|
16156
16190
|
};
|
|
16191
|
+
var getStagedFiles = async () => {
|
|
16192
|
+
const status = await git.status();
|
|
16193
|
+
return status.files.filter((file) => file.index !== " " && file.index !== "?").map((file) => file.path);
|
|
16194
|
+
};
|
|
16195
|
+
var hasStagedChanges = async () => {
|
|
16196
|
+
const stagedFiles = await getStagedFiles();
|
|
16197
|
+
return stagedFiles.length > 0;
|
|
16198
|
+
};
|
|
16157
16199
|
var branchExists = async (branchName) => {
|
|
16158
16200
|
try {
|
|
16159
16201
|
const branches = await git.branch();
|
|
@@ -16240,7 +16282,8 @@ var pushBranch = async (branchName, {
|
|
|
16240
16282
|
remote = "origin",
|
|
16241
16283
|
upstream,
|
|
16242
16284
|
force = false,
|
|
16243
|
-
noVerify = false
|
|
16285
|
+
noVerify = false,
|
|
16286
|
+
silent = false
|
|
16244
16287
|
} = {}) => {
|
|
16245
16288
|
const targetUpstream = upstream || branchName;
|
|
16246
16289
|
log.info(`Pushing branch "${branchName}" to ${remote}/${targetUpstream}...`);
|
|
@@ -16253,8 +16296,9 @@ var pushBranch = async (branchName, {
|
|
|
16253
16296
|
if (noVerify) {
|
|
16254
16297
|
pushArgs.push("--no-verify");
|
|
16255
16298
|
}
|
|
16299
|
+
const stdioOption = silent ? ["pipe", "pipe", "pipe"] : ["inherit", "inherit", "inherit"];
|
|
16256
16300
|
const gitProcess = spawn2("git", ["push", ...pushArgs], {
|
|
16257
|
-
stdio:
|
|
16301
|
+
stdio: stdioOption
|
|
16258
16302
|
});
|
|
16259
16303
|
return new Promise((resolve, reject) => {
|
|
16260
16304
|
gitProcess.on("close", (code) => {
|
|
@@ -16328,6 +16372,16 @@ var getChangedFilesBetween = async (source, target) => {
|
|
|
16328
16372
|
throw new Error(`Failed to get changed files between ${source} and ${target || "base"}: ${error}`);
|
|
16329
16373
|
}
|
|
16330
16374
|
};
|
|
16375
|
+
var stageFiles = async (files) => {
|
|
16376
|
+
if (files.length === 0)
|
|
16377
|
+
return;
|
|
16378
|
+
try {
|
|
16379
|
+
await git.add(files);
|
|
16380
|
+
log.info(`Staged ${files.length} file${files.length !== 1 ? "s" : ""}`);
|
|
16381
|
+
} catch (error) {
|
|
16382
|
+
throw new Error(`Failed to stage files: ${error}`);
|
|
16383
|
+
}
|
|
16384
|
+
};
|
|
16331
16385
|
var extractFilesFromRef = async (ref, files) => {
|
|
16332
16386
|
try {
|
|
16333
16387
|
for (const file of files) {
|
|
@@ -16430,6 +16484,9 @@ var getOwnerFiles = async (ownerPattern, includeUnowned = false, pathPattern, ex
|
|
|
16430
16484
|
// src/commands/list.ts
|
|
16431
16485
|
var listCodeowners = async (options) => {
|
|
16432
16486
|
try {
|
|
16487
|
+
if (options.json) {
|
|
16488
|
+
setSilent(true);
|
|
16489
|
+
}
|
|
16433
16490
|
if (await hasUnstagedChanges()) {
|
|
16434
16491
|
const unstagedFiles = await getUnstagedFiles();
|
|
16435
16492
|
log.warn("Warning: Unstaged changes detected (these will be ignored):");
|
|
@@ -16462,6 +16519,34 @@ Only staged files will be processed.`);
|
|
|
16462
16519
|
return matchFn(owners, patterns);
|
|
16463
16520
|
});
|
|
16464
16521
|
}
|
|
16522
|
+
if (options.json) {
|
|
16523
|
+
let grouped;
|
|
16524
|
+
if (options.group) {
|
|
16525
|
+
grouped = {};
|
|
16526
|
+
for (const { file, owners } of filteredFiles) {
|
|
16527
|
+
if (owners.length === 0) {
|
|
16528
|
+
grouped["(unowned)"] = grouped["(unowned)"] || [];
|
|
16529
|
+
grouped["(unowned)"].push(file);
|
|
16530
|
+
} else {
|
|
16531
|
+
for (const owner of owners) {
|
|
16532
|
+
grouped[owner] = grouped[owner] || [];
|
|
16533
|
+
grouped[owner].push(file);
|
|
16534
|
+
}
|
|
16535
|
+
}
|
|
16536
|
+
}
|
|
16537
|
+
}
|
|
16538
|
+
outputJson({
|
|
16539
|
+
command: "list",
|
|
16540
|
+
...grouped ? { grouped } : { files: filteredFiles.map(({ file, owners }) => ({ file, owners })) },
|
|
16541
|
+
filters: {
|
|
16542
|
+
include: options.include || null,
|
|
16543
|
+
pathPattern: options.pathPattern || null,
|
|
16544
|
+
exclusive: options.exclusive || false,
|
|
16545
|
+
coOwned: options.coOwned || false
|
|
16546
|
+
}
|
|
16547
|
+
});
|
|
16548
|
+
return;
|
|
16549
|
+
}
|
|
16465
16550
|
if (options.group) {
|
|
16466
16551
|
const ownerGroups = new Map;
|
|
16467
16552
|
for (const { file, owners } of filteredFiles) {
|
|
@@ -16528,6 +16613,10 @@ Only staged files will be processed.`);
|
|
|
16528
16613
|
]);
|
|
16529
16614
|
}
|
|
16530
16615
|
} catch (err) {
|
|
16616
|
+
if (options.json) {
|
|
16617
|
+
outputJson({ command: "list", error: String(err) });
|
|
16618
|
+
process.exit(1);
|
|
16619
|
+
}
|
|
16531
16620
|
log.error(err);
|
|
16532
16621
|
process.exit(1);
|
|
16533
16622
|
}
|
|
@@ -16617,11 +16706,16 @@ var createPullRequest = async (options) => {
|
|
|
16617
16706
|
});
|
|
16618
16707
|
};
|
|
16619
16708
|
var createPRWithTemplate = async (title, branchName, options = {}) => {
|
|
16620
|
-
const template = await findPRTemplate();
|
|
16621
16709
|
let body = "";
|
|
16622
|
-
if (
|
|
16623
|
-
body =
|
|
16624
|
-
log.info("Using PR
|
|
16710
|
+
if (options.prBody) {
|
|
16711
|
+
body = options.prBody;
|
|
16712
|
+
log.info("Using provided PR body");
|
|
16713
|
+
} else {
|
|
16714
|
+
const template = await findPRTemplate();
|
|
16715
|
+
if (template) {
|
|
16716
|
+
body = template.content;
|
|
16717
|
+
log.info("Using PR template for pull request body");
|
|
16718
|
+
}
|
|
16625
16719
|
}
|
|
16626
16720
|
return createPullRequest({
|
|
16627
16721
|
title,
|
|
@@ -16780,6 +16874,11 @@ var branch = async (options) => {
|
|
|
16780
16874
|
let operationState = options.operationState || null;
|
|
16781
16875
|
const isSubOperation = !!options.operationState;
|
|
16782
16876
|
let autoRecoverySucceeded = false;
|
|
16877
|
+
let tempBranch = null;
|
|
16878
|
+
let sourceOriginalBranch = null;
|
|
16879
|
+
if (options.json && !isSubOperation) {
|
|
16880
|
+
setSilent(true);
|
|
16881
|
+
}
|
|
16783
16882
|
try {
|
|
16784
16883
|
if (!options.branch || !options.message || !options.include) {
|
|
16785
16884
|
throw new Error("Missing required options for branch creation");
|
|
@@ -16790,7 +16889,37 @@ var branch = async (options) => {
|
|
|
16790
16889
|
if (options.pr && options.draftPr) {
|
|
16791
16890
|
throw new Error("Cannot use both --pr and --draft-pr options");
|
|
16792
16891
|
}
|
|
16793
|
-
if (
|
|
16892
|
+
if (options.source && !isSubOperation) {
|
|
16893
|
+
if (await hasStagedChanges()) {
|
|
16894
|
+
throw new Error("Cannot use --source when there are staged changes. " + "Either commit/unstage your changes first, or omit --source to use staged files.");
|
|
16895
|
+
}
|
|
16896
|
+
sourceOriginalBranch = await getCurrentBranch();
|
|
16897
|
+
log.info(`Extracting changes from source: ${options.source}`);
|
|
16898
|
+
const defaultBranch = await getDefaultBranch();
|
|
16899
|
+
const compareTarget = options.compareMain ? defaultBranch : undefined;
|
|
16900
|
+
if (compareTarget) {
|
|
16901
|
+
log.info(`Comparing ${options.source} against ${compareTarget}...`);
|
|
16902
|
+
}
|
|
16903
|
+
let sourceFiles = await getChangedFilesBetween(options.source, compareTarget);
|
|
16904
|
+
if (sourceFiles.length === 0) {
|
|
16905
|
+
throw new Error(`No changed files found in ${options.source}`);
|
|
16906
|
+
}
|
|
16907
|
+
sourceFiles = filterByPathPatterns(sourceFiles, options.pathPattern);
|
|
16908
|
+
if (sourceFiles.length === 0) {
|
|
16909
|
+
throw new Error(options.pathPattern ? `No changed files found matching pattern: ${options.pathPattern}` : `No changed files found in ${options.source}`);
|
|
16910
|
+
}
|
|
16911
|
+
log.info(`Found ${sourceFiles.length} changed file${sourceFiles.length !== 1 ? "s" : ""} in source`);
|
|
16912
|
+
tempBranch = `cg-temp-${Date.now()}`;
|
|
16913
|
+
log.info(`Creating temporary branch "${tempBranch}" from "${defaultBranch}"...`);
|
|
16914
|
+
await checkout(defaultBranch);
|
|
16915
|
+
await createBranch(tempBranch);
|
|
16916
|
+
log.info("Extracting files from source...");
|
|
16917
|
+
await extractFilesFromRef(options.source, sourceFiles);
|
|
16918
|
+
await stageFiles(sourceFiles);
|
|
16919
|
+
log.info(`Files extracted and staged on temporary branch. Proceeding with branch creation...
|
|
16920
|
+
`);
|
|
16921
|
+
}
|
|
16922
|
+
if (!(options.source && !isSubOperation) && await hasUnstagedChanges()) {
|
|
16794
16923
|
const unstagedFiles = await getUnstagedFiles();
|
|
16795
16924
|
log.warn("Warning: Unstaged changes detected (these will be ignored):");
|
|
16796
16925
|
unstagedFiles.forEach((file) => log.warn(` - ${file}`));
|
|
@@ -16829,6 +16958,93 @@ Only staged files will be processed.`);
|
|
|
16829
16958
|
log.file(`Files to be committed:
|
|
16830
16959
|
${filesToCommit.join(`
|
|
16831
16960
|
`)}`);
|
|
16961
|
+
if (options.dryRun) {
|
|
16962
|
+
const allStagedFiles = await getChangedFiles();
|
|
16963
|
+
const excludedFiles = allStagedFiles.filter((f) => !filesToCommit.includes(f));
|
|
16964
|
+
const branchAlreadyExistsDry = await branchExists(options.branch);
|
|
16965
|
+
if (options.json && !isSubOperation) {
|
|
16966
|
+
outputJson({
|
|
16967
|
+
command: "branch",
|
|
16968
|
+
dryRun: true,
|
|
16969
|
+
owner: options.include,
|
|
16970
|
+
branch: options.branch,
|
|
16971
|
+
branchExists: branchAlreadyExistsDry,
|
|
16972
|
+
message: options.message,
|
|
16973
|
+
files: filesToCommit,
|
|
16974
|
+
excludedFiles,
|
|
16975
|
+
options: {
|
|
16976
|
+
push: options.push || false,
|
|
16977
|
+
remote: options.remote || "origin",
|
|
16978
|
+
force: options.force || false,
|
|
16979
|
+
pr: options.pr || false,
|
|
16980
|
+
draftPr: options.draftPr || false,
|
|
16981
|
+
noVerify: !options.verify,
|
|
16982
|
+
append: options.append || false,
|
|
16983
|
+
exclusive: options.exclusive || false,
|
|
16984
|
+
coOwned: options.coOwned || false,
|
|
16985
|
+
pathPattern: options.pathPattern || null
|
|
16986
|
+
}
|
|
16987
|
+
});
|
|
16988
|
+
return {
|
|
16989
|
+
success: true,
|
|
16990
|
+
branchName: options.branch,
|
|
16991
|
+
owner: options.include,
|
|
16992
|
+
files: filesToCommit,
|
|
16993
|
+
pushed: false
|
|
16994
|
+
};
|
|
16995
|
+
}
|
|
16996
|
+
if (!isSubOperation) {
|
|
16997
|
+
log.header("Dry Run Preview — branch");
|
|
16998
|
+
console.log("");
|
|
16999
|
+
}
|
|
17000
|
+
const detailsTable = new import_cli_table32.default({
|
|
17001
|
+
style: { head: ["cyan"] },
|
|
17002
|
+
wordWrap: true
|
|
17003
|
+
});
|
|
17004
|
+
detailsTable.push({ [source_default.bold("Owner pattern")]: options.include }, { [source_default.bold("Branch name")]: options.branch }, {
|
|
17005
|
+
[source_default.bold("Branch exists")]: branchAlreadyExistsDry ? options.append ? "Yes (--append: will add commit)" : "Yes (will fail without --append)" : "No (will be created)"
|
|
17006
|
+
}, { [source_default.bold("Commit message")]: options.message }, {
|
|
17007
|
+
[source_default.bold("Files matched")]: `${filesToCommit.length} file${filesToCommit.length !== 1 ? "s" : ""}`
|
|
17008
|
+
}, {
|
|
17009
|
+
[source_default.bold("Files excluded")]: `${excludedFiles.length} staged file${excludedFiles.length !== 1 ? "s" : ""} not matching`
|
|
17010
|
+
}, { [source_default.bold("No-verify")]: !options.verify ? "Yes" : "No" }, {
|
|
17011
|
+
[source_default.bold("Push")]: options.push ? `Yes → ${options.remote || "origin"}${options.force ? " (force)" : ""}` : "No"
|
|
17012
|
+
}, {
|
|
17013
|
+
[source_default.bold("Pull request")]: options.pr ? "Yes" : options.draftPr ? "Yes (draft)" : "No"
|
|
17014
|
+
});
|
|
17015
|
+
if (options.pathPattern) {
|
|
17016
|
+
detailsTable.push({
|
|
17017
|
+
[source_default.bold("Path filter")]: options.pathPattern
|
|
17018
|
+
});
|
|
17019
|
+
}
|
|
17020
|
+
if (options.exclusive) {
|
|
17021
|
+
detailsTable.push({
|
|
17022
|
+
[source_default.bold("Exclusive mode")]: "Yes (only files solely owned by this owner)"
|
|
17023
|
+
});
|
|
17024
|
+
}
|
|
17025
|
+
if (options.coOwned) {
|
|
17026
|
+
detailsTable.push({
|
|
17027
|
+
[source_default.bold("Co-owned mode")]: "Yes (only files with multiple owners)"
|
|
17028
|
+
});
|
|
17029
|
+
}
|
|
17030
|
+
console.log(detailsTable.toString());
|
|
17031
|
+
console.log(source_default.bold.green(`
|
|
17032
|
+
Files to be committed (${filesToCommit.length}):`));
|
|
17033
|
+
filesToCommit.forEach((file) => console.log(` ${source_default.green("+")} ${file}`));
|
|
17034
|
+
if (excludedFiles.length > 0) {
|
|
17035
|
+
console.log(source_default.bold.dim(`
|
|
17036
|
+
Excluded staged files (${excludedFiles.length}):`));
|
|
17037
|
+
excludedFiles.forEach((file) => console.log(` ${source_default.dim("-")} ${source_default.dim(file)}`));
|
|
17038
|
+
}
|
|
17039
|
+
console.log("");
|
|
17040
|
+
return {
|
|
17041
|
+
success: true,
|
|
17042
|
+
branchName: options.branch,
|
|
17043
|
+
owner: options.include,
|
|
17044
|
+
files: filesToCommit,
|
|
17045
|
+
pushed: false
|
|
17046
|
+
};
|
|
17047
|
+
}
|
|
16832
17048
|
const branchAlreadyExists = await branchExists(options.branch);
|
|
16833
17049
|
if (branchAlreadyExists && !options.append) {
|
|
16834
17050
|
throw new Error(`Branch "${options.branch}" already exists. Use --append to add commits to it, or use a different name.`);
|
|
@@ -16883,7 +17099,8 @@ Only staged files will be processed.`);
|
|
|
16883
17099
|
remote: options.remote,
|
|
16884
17100
|
upstream: options.upstream,
|
|
16885
17101
|
force: options.force,
|
|
16886
|
-
noVerify: !options.verify
|
|
17102
|
+
noVerify: !options.verify,
|
|
17103
|
+
silent: !!options.json
|
|
16887
17104
|
});
|
|
16888
17105
|
pushed = true;
|
|
16889
17106
|
if (operationState) {
|
|
@@ -16902,7 +17119,8 @@ Only staged files will be processed.`);
|
|
|
16902
17119
|
const defaultBranch = await getDefaultBranch();
|
|
16903
17120
|
const prResult = await createPRWithTemplate(options.message, options.branch, {
|
|
16904
17121
|
draft: options.draftPr,
|
|
16905
|
-
base: defaultBranch
|
|
17122
|
+
base: defaultBranch,
|
|
17123
|
+
prBody: options.prBody
|
|
16906
17124
|
});
|
|
16907
17125
|
if (prResult) {
|
|
16908
17126
|
prUrl = prResult.url;
|
|
@@ -16949,7 +17167,7 @@ Only staged files will be processed.`);
|
|
|
16949
17167
|
Files committed:`);
|
|
16950
17168
|
filesToCommit.forEach((file) => console.log(` - ${file}`));
|
|
16951
17169
|
}
|
|
16952
|
-
|
|
17170
|
+
const result = {
|
|
16953
17171
|
success: true,
|
|
16954
17172
|
branchName: options.branch,
|
|
16955
17173
|
owner: options.include,
|
|
@@ -16958,6 +17176,15 @@ Files committed:`);
|
|
|
16958
17176
|
prUrl,
|
|
16959
17177
|
prNumber
|
|
16960
17178
|
};
|
|
17179
|
+
if (options.json && !isSubOperation) {
|
|
17180
|
+
outputJson({
|
|
17181
|
+
command: "branch",
|
|
17182
|
+
dryRun: false,
|
|
17183
|
+
...result,
|
|
17184
|
+
error: null
|
|
17185
|
+
});
|
|
17186
|
+
}
|
|
17187
|
+
return result;
|
|
16961
17188
|
} catch (operationError) {
|
|
16962
17189
|
log.error(`Operation failed: ${operationError}`);
|
|
16963
17190
|
if (operationState && !isSubOperation) {
|
|
@@ -17027,6 +17254,21 @@ Files committed:`);
|
|
|
17027
17254
|
error: String(err)
|
|
17028
17255
|
};
|
|
17029
17256
|
}
|
|
17257
|
+
if (options.json && !isSubOperation) {
|
|
17258
|
+
outputJson({
|
|
17259
|
+
command: "branch",
|
|
17260
|
+
dryRun: false,
|
|
17261
|
+
success: false,
|
|
17262
|
+
branchName: options.branch ?? "",
|
|
17263
|
+
owner: options.include ?? "",
|
|
17264
|
+
files: filesToCommit,
|
|
17265
|
+
pushed,
|
|
17266
|
+
prUrl: prUrl || null,
|
|
17267
|
+
prNumber: prNumber || null,
|
|
17268
|
+
error: String(err)
|
|
17269
|
+
});
|
|
17270
|
+
process.exit(1);
|
|
17271
|
+
}
|
|
17030
17272
|
if (operationState && !autoRecoverySucceeded) {
|
|
17031
17273
|
log.info(`
|
|
17032
17274
|
Auto-recovery failed. Manual recovery options:`);
|
|
@@ -17037,19 +17279,33 @@ Auto-recovery failed. Manual recovery options:`);
|
|
|
17037
17279
|
process.exit(1);
|
|
17038
17280
|
} finally {
|
|
17039
17281
|
try {
|
|
17040
|
-
if (
|
|
17041
|
-
|
|
17042
|
-
|
|
17043
|
-
|
|
17282
|
+
if (tempBranch) {
|
|
17283
|
+
if (sourceOriginalBranch) {
|
|
17284
|
+
const currentBranch = await getCurrentBranch();
|
|
17285
|
+
if (currentBranch !== sourceOriginalBranch) {
|
|
17286
|
+
log.info(`Returning to original branch "${sourceOriginalBranch}"...`);
|
|
17287
|
+
await checkout(sourceOriginalBranch);
|
|
17288
|
+
}
|
|
17289
|
+
}
|
|
17290
|
+
log.info(`Cleaning up temporary branch "${tempBranch}"...`);
|
|
17291
|
+
await deleteBranch(tempBranch, true);
|
|
17292
|
+
} else {
|
|
17293
|
+
if (originalBranch) {
|
|
17294
|
+
const currentBranch = await getCurrentBranch();
|
|
17295
|
+
if (currentBranch !== originalBranch) {
|
|
17296
|
+
await checkout(originalBranch);
|
|
17297
|
+
}
|
|
17044
17298
|
}
|
|
17045
17299
|
}
|
|
17046
17300
|
} catch (finalError) {
|
|
17047
17301
|
log.error(`Error during final cleanup: ${finalError}`);
|
|
17048
17302
|
log.info("Some manual cleanup may be required.");
|
|
17303
|
+
if (tempBranch) {
|
|
17304
|
+
log.info(`You can manually delete the temp branch with: git branch -D ${tempBranch}`);
|
|
17305
|
+
}
|
|
17049
17306
|
}
|
|
17050
17307
|
}
|
|
17051
17308
|
};
|
|
17052
|
-
|
|
17053
17309
|
// node_modules/@inquirer/core/dist/esm/lib/key.js
|
|
17054
17310
|
var isUpKey = (key) => key.name === "up" || key.name === "k" || key.ctrl && key.name === "p";
|
|
17055
17311
|
var isDownKey = (key) => key.name === "down" || key.name === "j" || key.ctrl && key.name === "n";
|
|
@@ -18337,9 +18593,21 @@ Operation ID: ${op.id}`);
|
|
|
18337
18593
|
log.info(formatBranchState(op));
|
|
18338
18594
|
}
|
|
18339
18595
|
};
|
|
18340
|
-
var performRecovery = async (state, keepBranches) => {
|
|
18596
|
+
var performRecovery = async (state, keepBranches, options) => {
|
|
18341
18597
|
log.header(`Recovering from operation ${state.id}`);
|
|
18342
18598
|
let hadWarnings = false;
|
|
18599
|
+
if (!options?.skipDirtyCheck) {
|
|
18600
|
+
const hasUnstaged = await hasUnstagedChanges();
|
|
18601
|
+
const hasStaged = await hasStagedChanges();
|
|
18602
|
+
if (hasUnstaged || hasStaged) {
|
|
18603
|
+
log.warn("Working directory has uncommitted changes.");
|
|
18604
|
+
log.warn("Recovery may overwrite files in your working directory.");
|
|
18605
|
+
log.info("Consider committing or stashing your changes first:");
|
|
18606
|
+
log.info(" git stash push -m 'before recovery'");
|
|
18607
|
+
throw new Error("Working directory is not clean. Commit or stash changes before recovering.");
|
|
18608
|
+
}
|
|
18609
|
+
}
|
|
18610
|
+
const branchesWithRestoreFailure = new Set;
|
|
18343
18611
|
const currentBranch = await getCurrentBranch();
|
|
18344
18612
|
if (currentBranch !== state.originalBranch) {
|
|
18345
18613
|
try {
|
|
@@ -18370,7 +18638,8 @@ Restoring files from branches...`);
|
|
|
18370
18638
|
}
|
|
18371
18639
|
} catch (error) {
|
|
18372
18640
|
log.error(`Failed to restore files from ${branch2.name}: ${error}`);
|
|
18373
|
-
log.
|
|
18641
|
+
log.warn(`Branch ${branch2.name} will be kept to prevent data loss`);
|
|
18642
|
+
branchesWithRestoreFailure.add(branch2.name);
|
|
18374
18643
|
hadWarnings = true;
|
|
18375
18644
|
}
|
|
18376
18645
|
}
|
|
@@ -18381,6 +18650,12 @@ Restoring files from branches...`);
|
|
|
18381
18650
|
Cleaning up created branches...`);
|
|
18382
18651
|
for (const branch2 of state.branches) {
|
|
18383
18652
|
if (branch2.created) {
|
|
18653
|
+
if (branchesWithRestoreFailure.has(branch2.name)) {
|
|
18654
|
+
log.warn(`Skipping deletion of ${branch2.name} — file restoration failed, branch preserved to prevent data loss`);
|
|
18655
|
+
log.info(` To recover files manually: git checkout ${branch2.name} -- <file>`);
|
|
18656
|
+
log.info(` To delete manually when done: git branch -D ${branch2.name}`);
|
|
18657
|
+
continue;
|
|
18658
|
+
}
|
|
18384
18659
|
try {
|
|
18385
18660
|
const exists2 = await branchExists(branch2.name);
|
|
18386
18661
|
if (exists2) {
|
|
@@ -18478,6 +18753,11 @@ Operation details:`);
|
|
|
18478
18753
|
var import_cli_table33 = __toESM(require_table(), 1);
|
|
18479
18754
|
var multiBranch = async (options) => {
|
|
18480
18755
|
let operationState = null;
|
|
18756
|
+
let tempBranch = null;
|
|
18757
|
+
let sourceOriginalBranch = null;
|
|
18758
|
+
if (options.json) {
|
|
18759
|
+
setSilent(true);
|
|
18760
|
+
}
|
|
18481
18761
|
try {
|
|
18482
18762
|
if (!options.branch || !options.message) {
|
|
18483
18763
|
throw new Error("Missing required options for multi-branch creation");
|
|
@@ -18491,7 +18771,37 @@ var multiBranch = async (options) => {
|
|
|
18491
18771
|
if (options.pr && options.draftPr) {
|
|
18492
18772
|
throw new Error("Cannot use both --pr and --draft-pr options");
|
|
18493
18773
|
}
|
|
18494
|
-
if (
|
|
18774
|
+
if (options.source) {
|
|
18775
|
+
if (await hasStagedChanges()) {
|
|
18776
|
+
throw new Error("Cannot use --source when there are staged changes. " + "Either commit/unstage your changes first, or omit --source to use staged files.");
|
|
18777
|
+
}
|
|
18778
|
+
sourceOriginalBranch = await getCurrentBranch();
|
|
18779
|
+
log.info(`Extracting changes from source: ${options.source}`);
|
|
18780
|
+
const defaultBranch = await getDefaultBranch();
|
|
18781
|
+
const compareTarget = options.compareMain ? defaultBranch : undefined;
|
|
18782
|
+
if (compareTarget) {
|
|
18783
|
+
log.info(`Comparing ${options.source} against ${compareTarget}...`);
|
|
18784
|
+
}
|
|
18785
|
+
let sourceFiles = await getChangedFilesBetween(options.source, compareTarget);
|
|
18786
|
+
if (sourceFiles.length === 0) {
|
|
18787
|
+
throw new Error(`No changed files found in ${options.source}`);
|
|
18788
|
+
}
|
|
18789
|
+
sourceFiles = filterByPathPatterns(sourceFiles, options.pathPattern);
|
|
18790
|
+
if (sourceFiles.length === 0) {
|
|
18791
|
+
throw new Error(options.pathPattern ? `No changed files found matching pattern: ${options.pathPattern}` : `No changed files found in ${options.source}`);
|
|
18792
|
+
}
|
|
18793
|
+
log.info(`Found ${sourceFiles.length} changed file${sourceFiles.length !== 1 ? "s" : ""} in source`);
|
|
18794
|
+
tempBranch = `cg-temp-${Date.now()}`;
|
|
18795
|
+
log.info(`Creating temporary branch "${tempBranch}" from "${defaultBranch}"...`);
|
|
18796
|
+
await checkout(defaultBranch);
|
|
18797
|
+
await createBranch(tempBranch);
|
|
18798
|
+
log.info("Extracting files from source...");
|
|
18799
|
+
await extractFilesFromRef(options.source, sourceFiles);
|
|
18800
|
+
await stageFiles(sourceFiles);
|
|
18801
|
+
log.info(`Files extracted and staged on temporary branch. Proceeding with multi-branch split...
|
|
18802
|
+
`);
|
|
18803
|
+
}
|
|
18804
|
+
if (!options.source && await hasUnstagedChanges()) {
|
|
18495
18805
|
const unstagedFiles = await getUnstagedFiles();
|
|
18496
18806
|
log.warn("Warning: Unstaged changes detected (these will be ignored):");
|
|
18497
18807
|
unstagedFiles.forEach((file) => log.warn(` - ${file}`));
|
|
@@ -18556,6 +18866,137 @@ Only staged files will be processed.`);
|
|
|
18556
18866
|
}
|
|
18557
18867
|
log.info(`Processing ${codeowners2.length} codeowners after filtering: ${codeowners2.join(", ")}`);
|
|
18558
18868
|
}
|
|
18869
|
+
if (options.dryRun) {
|
|
18870
|
+
const previews = [];
|
|
18871
|
+
const allCoveredFiles = new Set;
|
|
18872
|
+
for (const owner of codeowners2) {
|
|
18873
|
+
const sanitizedOwner = owner.replace(/[^a-zA-Z0-9-_@]/g, "-").replace(/^@/, "");
|
|
18874
|
+
const branchName = `${options.branch}/${sanitizedOwner}`;
|
|
18875
|
+
const commitMessage = `${options.message} - ${owner}`;
|
|
18876
|
+
const ownerFiles = await getOwnerFiles(owner, owner === options.defaultOwner, options.pathPattern, options.exclusive || false, options.coOwned || false);
|
|
18877
|
+
for (const f of ownerFiles)
|
|
18878
|
+
allCoveredFiles.add(f);
|
|
18879
|
+
previews.push({
|
|
18880
|
+
owner,
|
|
18881
|
+
branchName,
|
|
18882
|
+
commitMessage,
|
|
18883
|
+
files: ownerFiles
|
|
18884
|
+
});
|
|
18885
|
+
}
|
|
18886
|
+
const uncoveredFiles = changedFiles.filter((f) => !allCoveredFiles.has(f));
|
|
18887
|
+
if (options.json) {
|
|
18888
|
+
outputJson({
|
|
18889
|
+
command: "multi-branch",
|
|
18890
|
+
dryRun: true,
|
|
18891
|
+
owners: previews.map((p) => ({
|
|
18892
|
+
owner: p.owner,
|
|
18893
|
+
branch: p.branchName,
|
|
18894
|
+
message: p.commitMessage,
|
|
18895
|
+
files: p.files
|
|
18896
|
+
})),
|
|
18897
|
+
uncoveredFiles,
|
|
18898
|
+
filesWithoutOwners: options.defaultOwner ? [] : filesWithoutOwners,
|
|
18899
|
+
totalFiles: changedFiles.length,
|
|
18900
|
+
coveredFiles: allCoveredFiles.size,
|
|
18901
|
+
options: {
|
|
18902
|
+
baseBranch: options.branch,
|
|
18903
|
+
baseMessage: options.message,
|
|
18904
|
+
push: options.push || false,
|
|
18905
|
+
remote: options.remote || "origin",
|
|
18906
|
+
force: options.force || false,
|
|
18907
|
+
pr: options.pr || false,
|
|
18908
|
+
draftPr: options.draftPr || false,
|
|
18909
|
+
noVerify: !options.verify,
|
|
18910
|
+
append: options.append || false,
|
|
18911
|
+
exclusive: options.exclusive || false,
|
|
18912
|
+
coOwned: options.coOwned || false,
|
|
18913
|
+
pathPattern: options.pathPattern || null,
|
|
18914
|
+
defaultOwner: options.defaultOwner || null
|
|
18915
|
+
}
|
|
18916
|
+
});
|
|
18917
|
+
return;
|
|
18918
|
+
}
|
|
18919
|
+
log.header("Dry Run Preview — multi-branch");
|
|
18920
|
+
console.log("");
|
|
18921
|
+
const settingsTable = new import_cli_table33.default({
|
|
18922
|
+
style: { head: ["cyan"] },
|
|
18923
|
+
wordWrap: true
|
|
18924
|
+
});
|
|
18925
|
+
settingsTable.push({ [source_default.bold("Base branch name")]: options.branch }, { [source_default.bold("Base commit message")]: options.message }, { [source_default.bold("Total codeowners")]: `${codeowners2.length}` }, { [source_default.bold("No-verify")]: !options.verify ? "Yes" : "No" }, {
|
|
18926
|
+
[source_default.bold("Push")]: options.push ? `Yes → ${options.remote || "origin"}${options.force ? " (force)" : ""}` : "No"
|
|
18927
|
+
}, {
|
|
18928
|
+
[source_default.bold("Pull request")]: options.pr ? "Yes" : options.draftPr ? "Yes (draft)" : "No"
|
|
18929
|
+
}, { [source_default.bold("Append mode")]: options.append ? "Yes" : "No" });
|
|
18930
|
+
if (options.pathPattern) {
|
|
18931
|
+
settingsTable.push({
|
|
18932
|
+
[source_default.bold("Path filter")]: options.pathPattern
|
|
18933
|
+
});
|
|
18934
|
+
}
|
|
18935
|
+
if (options.exclusive) {
|
|
18936
|
+
settingsTable.push({
|
|
18937
|
+
[source_default.bold("Exclusive mode")]: "Yes (only files solely owned by each owner)"
|
|
18938
|
+
});
|
|
18939
|
+
}
|
|
18940
|
+
if (options.coOwned) {
|
|
18941
|
+
settingsTable.push({
|
|
18942
|
+
[source_default.bold("Co-owned mode")]: "Yes (only files with multiple owners)"
|
|
18943
|
+
});
|
|
18944
|
+
}
|
|
18945
|
+
if (options.defaultOwner) {
|
|
18946
|
+
settingsTable.push({
|
|
18947
|
+
[source_default.bold("Default owner")]: options.defaultOwner
|
|
18948
|
+
});
|
|
18949
|
+
}
|
|
18950
|
+
console.log(settingsTable.toString());
|
|
18951
|
+
console.log("");
|
|
18952
|
+
const summaryTable = new import_cli_table33.default({
|
|
18953
|
+
head: ["Owner", "Branch", "Files", "Commit Message"],
|
|
18954
|
+
colWidths: [22, 35, 8, 45],
|
|
18955
|
+
wordWrap: true,
|
|
18956
|
+
style: { head: ["cyan"] }
|
|
18957
|
+
});
|
|
18958
|
+
for (const p of previews) {
|
|
18959
|
+
summaryTable.push([
|
|
18960
|
+
p.owner,
|
|
18961
|
+
p.branchName,
|
|
18962
|
+
`${p.files.length}`,
|
|
18963
|
+
p.commitMessage
|
|
18964
|
+
]);
|
|
18965
|
+
}
|
|
18966
|
+
console.log(summaryTable.toString());
|
|
18967
|
+
console.log(source_default.bold.cyan(`
|
|
18968
|
+
Files by branch:`));
|
|
18969
|
+
for (const p of previews) {
|
|
18970
|
+
if (p.files.length > 0) {
|
|
18971
|
+
console.log(`
|
|
18972
|
+
${source_default.bold(p.branchName)} ${source_default.dim(`(${p.owner})`)} — ${p.files.length} file${p.files.length !== 1 ? "s" : ""}:`);
|
|
18973
|
+
p.files.forEach((file) => console.log(` ${source_default.green("+")} ${file}`));
|
|
18974
|
+
} else {
|
|
18975
|
+
console.log(`
|
|
18976
|
+
${source_default.bold(p.branchName)} ${source_default.dim(`(${p.owner})`)} — ${source_default.yellow("0 files (branch will be skipped)")}`);
|
|
18977
|
+
}
|
|
18978
|
+
}
|
|
18979
|
+
if (uncoveredFiles.length > 0) {
|
|
18980
|
+
console.log(source_default.bold.yellow(`
|
|
18981
|
+
Uncovered staged files (${uncoveredFiles.length}) — not included in any branch:`));
|
|
18982
|
+
uncoveredFiles.forEach((file) => console.log(` ${source_default.yellow("!")} ${file}`));
|
|
18983
|
+
}
|
|
18984
|
+
if (filesWithoutOwners.length > 0 && !options.defaultOwner) {
|
|
18985
|
+
console.log(source_default.bold.yellow(`
|
|
18986
|
+
Files without CODEOWNERS (${filesWithoutOwners.length}):`));
|
|
18987
|
+
filesWithoutOwners.forEach((file) => console.log(` ${source_default.yellow("?")} ${file}`));
|
|
18988
|
+
console.log(source_default.dim(" Tip: Use --default-owner <owner> to assign these files"));
|
|
18989
|
+
}
|
|
18990
|
+
console.log(source_default.bold.cyan(`
|
|
18991
|
+
Summary:`));
|
|
18992
|
+
console.log(` Branches to create: ${source_default.bold(`${previews.length}`)}`);
|
|
18993
|
+
console.log(` Total files covered: ${source_default.bold(`${allCoveredFiles.size}`)} of ${changedFiles.length} staged`);
|
|
18994
|
+
if (uncoveredFiles.length > 0) {
|
|
18995
|
+
console.log(` Uncovered files: ${source_default.yellow(`${uncoveredFiles.length}`)}`);
|
|
18996
|
+
}
|
|
18997
|
+
console.log("");
|
|
18998
|
+
return;
|
|
18999
|
+
}
|
|
18559
19000
|
const results = [];
|
|
18560
19001
|
for (const owner of codeowners2) {
|
|
18561
19002
|
const sanitizedOwner = owner.replace(/[^a-zA-Z0-9-_@]/g, "-").replace(/^@/, "");
|
|
@@ -18579,7 +19020,9 @@ Only staged files will be processed.`);
|
|
|
18579
19020
|
operationState: operationState || undefined,
|
|
18580
19021
|
pathPattern: options.pathPattern,
|
|
18581
19022
|
exclusive: options.exclusive,
|
|
18582
|
-
coOwned: options.coOwned
|
|
19023
|
+
coOwned: options.coOwned,
|
|
19024
|
+
json: options.json,
|
|
19025
|
+
prBody: options.prBody
|
|
18583
19026
|
});
|
|
18584
19027
|
results.push(result);
|
|
18585
19028
|
}
|
|
@@ -18642,7 +19085,36 @@ Note: ${failureCount} branch(es) failed. Files were auto-restored to working dir
|
|
|
18642
19085
|
log.info(`State preserved for reference. Run 'codeowners-git recover --id ${operationState.id}' if needed.`);
|
|
18643
19086
|
}
|
|
18644
19087
|
}
|
|
19088
|
+
if (options.json) {
|
|
19089
|
+
outputJson({
|
|
19090
|
+
command: "multi-branch",
|
|
19091
|
+
dryRun: false,
|
|
19092
|
+
success: failureCount === 0,
|
|
19093
|
+
totalOwners: codeowners2.length,
|
|
19094
|
+
successCount,
|
|
19095
|
+
failureCount,
|
|
19096
|
+
results: results.map((r) => ({
|
|
19097
|
+
owner: r.owner,
|
|
19098
|
+
branch: r.branchName,
|
|
19099
|
+
success: r.success,
|
|
19100
|
+
files: r.files,
|
|
19101
|
+
pushed: r.pushed,
|
|
19102
|
+
prUrl: r.prUrl || null,
|
|
19103
|
+
prNumber: r.prNumber || null,
|
|
19104
|
+
error: r.error || null
|
|
19105
|
+
}))
|
|
19106
|
+
});
|
|
19107
|
+
}
|
|
18645
19108
|
} catch (err) {
|
|
19109
|
+
if (options.json) {
|
|
19110
|
+
outputJson({
|
|
19111
|
+
command: "multi-branch",
|
|
19112
|
+
dryRun: false,
|
|
19113
|
+
success: false,
|
|
19114
|
+
error: String(err)
|
|
19115
|
+
});
|
|
19116
|
+
process.exit(1);
|
|
19117
|
+
}
|
|
18646
19118
|
log.error(`Multi-branch operation failed: ${err}`);
|
|
18647
19119
|
if (operationState) {
|
|
18648
19120
|
log.info(`
|
|
@@ -18650,7 +19122,7 @@ Attempting auto-recovery...`);
|
|
|
18650
19122
|
const currentState = loadOperationState(operationState.id);
|
|
18651
19123
|
if (currentState) {
|
|
18652
19124
|
try {
|
|
18653
|
-
const recovered = await performRecovery(currentState, false);
|
|
19125
|
+
const recovered = await performRecovery(currentState, false, { skipDirtyCheck: true });
|
|
18654
19126
|
if (recovered) {
|
|
18655
19127
|
log.success("Auto-recovery completed successfully");
|
|
18656
19128
|
} else {
|
|
@@ -18666,11 +19138,32 @@ Manual recovery options:`);
|
|
|
18666
19138
|
}
|
|
18667
19139
|
}
|
|
18668
19140
|
process.exit(1);
|
|
19141
|
+
} finally {
|
|
19142
|
+
if (tempBranch) {
|
|
19143
|
+
try {
|
|
19144
|
+
if (sourceOriginalBranch) {
|
|
19145
|
+
const currentBranch = await getCurrentBranch();
|
|
19146
|
+
if (currentBranch !== sourceOriginalBranch) {
|
|
19147
|
+
log.info(`Returning to original branch "${sourceOriginalBranch}"...`);
|
|
19148
|
+
await checkout(sourceOriginalBranch);
|
|
19149
|
+
}
|
|
19150
|
+
}
|
|
19151
|
+
log.info(`Cleaning up temporary branch "${tempBranch}"...`);
|
|
19152
|
+
await deleteBranch(tempBranch, true);
|
|
19153
|
+
} catch (cleanupError) {
|
|
19154
|
+
log.warn(`Failed to clean up temporary branch "${tempBranch}": ${cleanupError}`);
|
|
19155
|
+
log.info(`You can manually delete it with: git branch -D ${tempBranch}`);
|
|
19156
|
+
}
|
|
19157
|
+
}
|
|
18669
19158
|
}
|
|
18670
19159
|
};
|
|
18671
19160
|
|
|
18672
19161
|
// src/commands/extract.ts
|
|
19162
|
+
var import_cli_table34 = __toESM(require_table(), 1);
|
|
18673
19163
|
var extract = async (options) => {
|
|
19164
|
+
if (options.json) {
|
|
19165
|
+
setSilent(true);
|
|
19166
|
+
}
|
|
18674
19167
|
try {
|
|
18675
19168
|
if (!options.source) {
|
|
18676
19169
|
log.error("Missing required option: --source");
|
|
@@ -18758,8 +19251,82 @@ Only staged files will be processed.`);
|
|
|
18758
19251
|
}
|
|
18759
19252
|
log.info(`Filtered to ${filesToExtract.length} file${filesToExtract.length !== 1 ? "s" : ""}`);
|
|
18760
19253
|
}
|
|
19254
|
+
if (options.dryRun) {
|
|
19255
|
+
const excludedFiles = changedFiles.filter((f) => !filesToExtract.includes(f));
|
|
19256
|
+
if (options.json) {
|
|
19257
|
+
outputJson({
|
|
19258
|
+
command: "extract",
|
|
19259
|
+
dryRun: true,
|
|
19260
|
+
source: options.source,
|
|
19261
|
+
compareTarget: compareTarget || null,
|
|
19262
|
+
files: filesToExtract,
|
|
19263
|
+
excludedFiles,
|
|
19264
|
+
totalChanged: changedFiles.length,
|
|
19265
|
+
options: {
|
|
19266
|
+
include: options.include || null,
|
|
19267
|
+
pathPattern: options.pathPattern || null,
|
|
19268
|
+
exclusive: options.exclusive || false,
|
|
19269
|
+
coOwned: options.coOwned || false,
|
|
19270
|
+
compareMain: options.compareMain || false
|
|
19271
|
+
}
|
|
19272
|
+
});
|
|
19273
|
+
return;
|
|
19274
|
+
}
|
|
19275
|
+
log.header("Dry Run Preview — extract");
|
|
19276
|
+
console.log("");
|
|
19277
|
+
const detailsTable = new import_cli_table34.default({
|
|
19278
|
+
style: { head: ["cyan"] },
|
|
19279
|
+
wordWrap: true
|
|
19280
|
+
});
|
|
19281
|
+
detailsTable.push({ [source_default.bold("Source")]: options.source }, {
|
|
19282
|
+
[source_default.bold("Compare target")]: compareTarget || "auto-detected"
|
|
19283
|
+
}, {
|
|
19284
|
+
[source_default.bold("Files in source")]: `${changedFiles.length} changed file${changedFiles.length !== 1 ? "s" : ""}`
|
|
19285
|
+
}, {
|
|
19286
|
+
[source_default.bold("Files to extract")]: `${filesToExtract.length} file${filesToExtract.length !== 1 ? "s" : ""}`
|
|
19287
|
+
}, {
|
|
19288
|
+
[source_default.bold("Files excluded")]: `${excludedFiles.length} file${excludedFiles.length !== 1 ? "s" : ""} (filtered out)`
|
|
19289
|
+
});
|
|
19290
|
+
if (options.include) {
|
|
19291
|
+
detailsTable.push({
|
|
19292
|
+
[source_default.bold("Owner filter")]: `${options.include}${options.exclusive ? " (exclusive)" : ""}`
|
|
19293
|
+
});
|
|
19294
|
+
}
|
|
19295
|
+
if (options.pathPattern) {
|
|
19296
|
+
detailsTable.push({
|
|
19297
|
+
[source_default.bold("Path filter")]: options.pathPattern
|
|
19298
|
+
});
|
|
19299
|
+
}
|
|
19300
|
+
if (options.coOwned) {
|
|
19301
|
+
detailsTable.push({
|
|
19302
|
+
[source_default.bold("Co-owned mode")]: "Yes (only files with multiple owners)"
|
|
19303
|
+
});
|
|
19304
|
+
}
|
|
19305
|
+
console.log(detailsTable.toString());
|
|
19306
|
+
console.log(source_default.bold.green(`
|
|
19307
|
+
Files to be extracted (${filesToExtract.length}):`));
|
|
19308
|
+
filesToExtract.forEach((file) => console.log(` ${source_default.green("+")} ${file}`));
|
|
19309
|
+
if (excludedFiles.length > 0) {
|
|
19310
|
+
console.log(source_default.bold.dim(`
|
|
19311
|
+
Excluded files (${excludedFiles.length}):`));
|
|
19312
|
+
excludedFiles.forEach((file) => console.log(` ${source_default.dim("-")} ${source_default.dim(file)}`));
|
|
19313
|
+
}
|
|
19314
|
+
console.log("");
|
|
19315
|
+
return;
|
|
19316
|
+
}
|
|
18761
19317
|
log.info("Extracting files to working directory...");
|
|
18762
19318
|
await extractFilesFromRef(options.source, filesToExtract);
|
|
19319
|
+
if (options.json) {
|
|
19320
|
+
outputJson({
|
|
19321
|
+
command: "extract",
|
|
19322
|
+
dryRun: false,
|
|
19323
|
+
source: options.source,
|
|
19324
|
+
compareTarget: compareTarget || null,
|
|
19325
|
+
files: filesToExtract,
|
|
19326
|
+
totalChanged: changedFiles.length
|
|
19327
|
+
});
|
|
19328
|
+
return;
|
|
19329
|
+
}
|
|
18763
19330
|
log.success(`
|
|
18764
19331
|
✓ Extracted ${filesToExtract.length} file${filesToExtract.length !== 1 ? "s" : ""} to working directory (unstaged)`);
|
|
18765
19332
|
log.info(`
|
|
@@ -18771,13 +19338,17 @@ Next steps:`);
|
|
|
18771
19338
|
log.info(" - Use 'cg branch' command to create a branch and commit");
|
|
18772
19339
|
log.info(" - Example: cg branch -i @my-team -b my-branch -m 'Commit message' -p");
|
|
18773
19340
|
} catch (err) {
|
|
19341
|
+
if (options.json) {
|
|
19342
|
+
outputJson({ command: "extract", error: String(err) });
|
|
19343
|
+
process.exit(1);
|
|
19344
|
+
}
|
|
18774
19345
|
log.error(`
|
|
18775
19346
|
✗ Extraction failed: ${err}`);
|
|
18776
19347
|
process.exit(1);
|
|
18777
19348
|
}
|
|
18778
19349
|
};
|
|
18779
19350
|
// package.json
|
|
18780
|
-
var version = "2.0
|
|
19351
|
+
var version = "2.2.0";
|
|
18781
19352
|
|
|
18782
19353
|
// src/commands/version.ts
|
|
18783
19354
|
function getVersion() {
|
|
@@ -18798,15 +19369,28 @@ Force exiting...`);
|
|
|
18798
19369
|
|
|
18799
19370
|
Received ${signal}. Gracefully shutting down...`);
|
|
18800
19371
|
const incompleteOps = getIncompleteOperations();
|
|
18801
|
-
if (incompleteOps.length
|
|
18802
|
-
|
|
19372
|
+
if (incompleteOps.length === 0) {
|
|
19373
|
+
process.exit(130);
|
|
19374
|
+
return;
|
|
19375
|
+
}
|
|
19376
|
+
const mostRecent = incompleteOps[0];
|
|
19377
|
+
log.warn(`Found ${incompleteOps.length} incomplete operation(s).`);
|
|
19378
|
+
getCurrentBranch().then((currentBranch) => {
|
|
19379
|
+
if (currentBranch !== mostRecent.originalBranch) {
|
|
19380
|
+
log.info(`Returning to original branch: ${mostRecent.originalBranch}...`);
|
|
19381
|
+
return checkout(mostRecent.originalBranch).then(() => {
|
|
19382
|
+
log.success(`Returned to ${mostRecent.originalBranch}`);
|
|
19383
|
+
});
|
|
19384
|
+
}
|
|
19385
|
+
}).catch(() => {
|
|
19386
|
+
log.warn("Could not return to original branch automatically.");
|
|
19387
|
+
}).finally(() => {
|
|
18803
19388
|
log.info(`
|
|
18804
|
-
To recover from incomplete operations, run:`);
|
|
18805
|
-
log.info(" codeowners-git recover --list # List all incomplete operations");
|
|
19389
|
+
To fully recover from incomplete operations, run:`);
|
|
18806
19390
|
log.info(" codeowners-git recover --auto # Auto-recover most recent operation");
|
|
18807
|
-
log.info(" codeowners-git recover --
|
|
18808
|
-
|
|
18809
|
-
|
|
19391
|
+
log.info(" codeowners-git recover --list # List all incomplete operations");
|
|
19392
|
+
process.exit(130);
|
|
19393
|
+
});
|
|
18810
19394
|
};
|
|
18811
19395
|
process.on("SIGINT", () => handleShutdown("SIGINT"));
|
|
18812
19396
|
process.on("SIGTERM", () => handleShutdown("SIGTERM"));
|
|
@@ -18816,7 +19400,7 @@ To recover from incomplete operations, run:`);
|
|
|
18816
19400
|
setupSignalHandlers();
|
|
18817
19401
|
var program2 = new Command;
|
|
18818
19402
|
program2.name("codeowners-git (cg)").description("CLI tool for grouping and managing staged files by CODEOWNERS").version(getVersion());
|
|
18819
|
-
program2.command("list").description("Lists all git changed files by CODEOWNER").argument("[pattern]", "Path pattern to filter files (micromatch syntax, comma-separated)").option("-i, --include <patterns>", "Filter by owner patterns").option("-g, --group", "Group files by code owner").option("-e, --exclusive", "Only include files where the owner is the sole owner (no co-owners)").option("-c, --co-owned", "Only include files with multiple owners (co-owned files)").action((pattern, options) => {
|
|
19403
|
+
program2.command("list").description("Lists all git changed files by CODEOWNER").argument("[pattern]", "Path pattern to filter files (micromatch syntax, comma-separated)").option("-i, --include <patterns>", "Filter by owner patterns").option("-g, --group", "Group files by code owner").option("-e, --exclusive", "Only include files where the owner is the sole owner (no co-owners)").option("-c, --co-owned", "Only include files with multiple owners (co-owned files)").option("--json", "Output results as JSON (suppresses all other output)").action((pattern, options) => {
|
|
18820
19404
|
if (options.exclusive && options.coOwned) {
|
|
18821
19405
|
console.error("Error: Cannot use both --exclusive and --co-owned options");
|
|
18822
19406
|
process.exit(1);
|
|
@@ -18826,7 +19410,7 @@ program2.command("list").description("Lists all git changed files by CODEOWNER")
|
|
|
18826
19410
|
pathPattern: pattern
|
|
18827
19411
|
});
|
|
18828
19412
|
});
|
|
18829
|
-
program2.command("branch").description("Create new branch with codeowner changes").argument("[pattern]", "Path pattern to filter files (micromatch syntax, comma-separated)").requiredOption("-i, --include <patterns>", "Code owner pattern to filter files").requiredOption("-b, --branch <branch>", "Branch name").requiredOption("-m, --message <message>", "Commit message").option("-n, --no-verify", "Skip lint-staged or any other ci checks").option("-p, --push", "Push branch to remote after commit").option("-r, --remote <remote>", "Remote name to push to", "origin").option("-u, --upstream <upstream>", "Upstream branch name (defaults to local branch name)").option("-f, --force", "Force push to remote").option("-k, --keep-branch-on-failure", "Keep the created branch even if operation fails").option("--append", "Add commits to existing branch instead of creating a new one").option("--pr", "Create a pull request after pushing (requires --push)").option("--draft-pr", "Create a draft pull request after pushing (requires --push)").option("-e, --exclusive", "Only include files where the owner is the sole owner (no co-owners)").option("-c, --co-owned", "Only include files with multiple owners (co-owned files)").action((pattern, options) => {
|
|
19413
|
+
program2.command("branch").description("Create new branch with codeowner changes").argument("[pattern]", "Path pattern to filter files (micromatch syntax, comma-separated)").requiredOption("-i, --include <patterns>", "Code owner pattern to filter files").requiredOption("-b, --branch <branch>", "Branch name").requiredOption("-m, --message <message>", "Commit message").option("-n, --no-verify", "Skip lint-staged or any other ci checks").option("-p, --push", "Push branch to remote after commit").option("-r, --remote <remote>", "Remote name to push to", "origin").option("-u, --upstream <upstream>", "Upstream branch name (defaults to local branch name)").option("-f, --force", "Force push to remote").option("-k, --keep-branch-on-failure", "Keep the created branch even if operation fails").option("--append", "Add commits to existing branch instead of creating a new one").option("--pr", "Create a pull request after pushing (requires --push)").option("--draft-pr", "Create a draft pull request after pushing (requires --push)").option("--pr-body <body>", "Custom PR body text (overrides repo PR template, requires --pr or --draft-pr)").option("-e, --exclusive", "Only include files where the owner is the sole owner (no co-owners)").option("-c, --co-owned", "Only include files with multiple owners (co-owned files)").option("--dry-run", "Preview the operation without making any changes").option("--json", "Output results as JSON (suppresses all other output)").option("-s, --source <source>", "Source branch or commit to extract changes from (creates a temp branch from the default branch)").option("--compare-main", "Compare source against main branch instead of detecting merge-base (use with --source)").action((pattern, options) => {
|
|
18830
19414
|
if (options.exclusive && options.coOwned) {
|
|
18831
19415
|
console.error("Error: Cannot use both --exclusive and --co-owned options");
|
|
18832
19416
|
process.exit(1);
|
|
@@ -18836,7 +19420,7 @@ program2.command("branch").description("Create new branch with codeowner changes
|
|
|
18836
19420
|
pathPattern: pattern
|
|
18837
19421
|
});
|
|
18838
19422
|
});
|
|
18839
|
-
program2.command("multi-branch").description("Create branches for all codeowners").argument("[pattern]", "Path pattern to filter files (micromatch syntax, comma-separated)").requiredOption("-b, --branch <branch>", "Base branch name (will be suffixed with codeowner name)").requiredOption("-m, --message <message>", "Base commit message (will be suffixed with codeowner name)").option("-n, --no-verify", "Skip lint-staged or any other ci checks").option("-p, --push", "Push branches to remote after commit").option("-r, --remote <remote>", "Remote name to push to", "origin").option("-u, --upstream <upstream>", "Upstream branch name pattern (defaults to local branch name)").option("-f, --force", "Force push to remote").option("-k, --keep-branch-on-failure", "Keep created branches even if operation fails").option("-d, --default-owner <defaultOwner>", "Default owner to use when no codeowners are found for changed files").option("--ignore <patterns>", "Comma-separated patterns to exclude codeowners (e.g., 'team-a,team-b')").option("--include <patterns>", "Comma-separated patterns to include codeowners (e.g., 'team-*,@org/*')").option("--append", "Add commits to existing branches instead of creating new ones").option("--pr", "Create pull requests after pushing (requires --push)").option("--draft-pr", "Create draft pull requests after pushing (requires --push)").option("-e, --exclusive", "Only include files where each owner is the sole owner (no co-owners)").option("-c, --co-owned", "Only include files with multiple owners (co-owned files)").action((pattern, options) => {
|
|
19423
|
+
program2.command("multi-branch").description("Create branches for all codeowners").argument("[pattern]", "Path pattern to filter files (micromatch syntax, comma-separated)").requiredOption("-b, --branch <branch>", "Base branch name (will be suffixed with codeowner name)").requiredOption("-m, --message <message>", "Base commit message (will be suffixed with codeowner name)").option("-n, --no-verify", "Skip lint-staged or any other ci checks").option("-p, --push", "Push branches to remote after commit").option("-r, --remote <remote>", "Remote name to push to", "origin").option("-u, --upstream <upstream>", "Upstream branch name pattern (defaults to local branch name)").option("-f, --force", "Force push to remote").option("-k, --keep-branch-on-failure", "Keep created branches even if operation fails").option("-d, --default-owner <defaultOwner>", "Default owner to use when no codeowners are found for changed files").option("--ignore <patterns>", "Comma-separated patterns to exclude codeowners (e.g., 'team-a,team-b')").option("--include <patterns>", "Comma-separated patterns to include codeowners (e.g., 'team-*,@org/*')").option("--append", "Add commits to existing branches instead of creating new ones").option("--pr", "Create pull requests after pushing (requires --push)").option("--draft-pr", "Create draft pull requests after pushing (requires --push)").option("--pr-body <body>", "Custom PR body text (overrides repo PR template, requires --pr or --draft-pr)").option("-e, --exclusive", "Only include files where each owner is the sole owner (no co-owners)").option("-c, --co-owned", "Only include files with multiple owners (co-owned files)").option("--dry-run", "Preview the operation without making any changes").option("--json", "Output results as JSON (suppresses all other output)").option("-s, --source <source>", "Source branch or commit to split (extracts changes onto a temp branch from the default branch)").option("--compare-main", "Compare source against main branch instead of detecting merge-base (use with --source)").action((pattern, options) => {
|
|
18840
19424
|
if (options.exclusive && options.coOwned) {
|
|
18841
19425
|
console.error("Error: Cannot use both --exclusive and --co-owned options");
|
|
18842
19426
|
process.exit(1);
|
|
@@ -18846,7 +19430,7 @@ program2.command("multi-branch").description("Create branches for all codeowners
|
|
|
18846
19430
|
pathPattern: pattern
|
|
18847
19431
|
});
|
|
18848
19432
|
});
|
|
18849
|
-
program2.command("extract").description("Extract file changes from a branch or commit to working directory").argument("[pattern]", "Path pattern to filter files (micromatch syntax, comma-separated)").requiredOption("-s, --source <source>", "Source branch or commit to extract from").option("-i, --include <patterns>", "Filter extracted files by code owner pattern").option("--compare-main", "Compare source against main branch instead of detecting merge-base").option("-e, --exclusive", "Only include files where the owner is the sole owner (no co-owners)").option("-c, --co-owned", "Only include files with multiple owners (co-owned files)").action((pattern, options) => {
|
|
19433
|
+
program2.command("extract").description("Extract file changes from a branch or commit to working directory").argument("[pattern]", "Path pattern to filter files (micromatch syntax, comma-separated)").requiredOption("-s, --source <source>", "Source branch or commit to extract from").option("-i, --include <patterns>", "Filter extracted files by code owner pattern").option("--compare-main", "Compare source against main branch instead of detecting merge-base").option("-e, --exclusive", "Only include files where the owner is the sole owner (no co-owners)").option("-c, --co-owned", "Only include files with multiple owners (co-owned files)").option("--dry-run", "Preview the operation without making any changes").option("--json", "Output results as JSON (suppresses all other output)").action((pattern, options) => {
|
|
18850
19434
|
if (options.exclusive && options.coOwned) {
|
|
18851
19435
|
console.error("Error: Cannot use both --exclusive and --co-owned options");
|
|
18852
19436
|
process.exit(1);
|