git-coco 0.21.4 → 0.22.1
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/index.d.ts +4 -0
- package/dist/index.esm.mjs +292 -199
- package/dist/index.js +292 -199
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -173,6 +173,10 @@ interface ChangelogOptions extends BaseCommandOptions {
|
|
|
173
173
|
range: string;
|
|
174
174
|
branch: string;
|
|
175
175
|
sinceLastTag: boolean;
|
|
176
|
+
withDiff?: boolean;
|
|
177
|
+
onlyDiff?: boolean;
|
|
178
|
+
additional?: string;
|
|
179
|
+
author?: boolean;
|
|
176
180
|
}
|
|
177
181
|
type ChangelogArgv = Arguments<ChangelogOptions>;
|
|
178
182
|
|
package/dist/index.esm.mjs
CHANGED
|
@@ -48,7 +48,7 @@ import { pathToFileURL } from 'url';
|
|
|
48
48
|
/**
|
|
49
49
|
* Current build version from package.json
|
|
50
50
|
*/
|
|
51
|
-
const BUILD_VERSION = "0.
|
|
51
|
+
const BUILD_VERSION = "0.22.1";
|
|
52
52
|
|
|
53
53
|
const isInteractive = (config) => {
|
|
54
54
|
return config?.mode === 'interactive' || !!config?.interactive;
|
|
@@ -1018,6 +1018,10 @@ const schema$1 = {
|
|
|
1018
1018
|
"apiKey": {
|
|
1019
1019
|
"type": "string",
|
|
1020
1020
|
"description": "API key to use when making requests to OpenAI. Defaults to the value of `OPENAI_API_KEY` environment variable."
|
|
1021
|
+
},
|
|
1022
|
+
"verbosity": {
|
|
1023
|
+
"$ref": "#/definitions/OpenAIVerbosityParam",
|
|
1024
|
+
"description": "The verbosity of the model's response."
|
|
1021
1025
|
}
|
|
1022
1026
|
}
|
|
1023
1027
|
},
|
|
@@ -1590,6 +1594,18 @@ const schema$1 = {
|
|
|
1590
1594
|
"gpt-3.5-turbo-16k-0613"
|
|
1591
1595
|
]
|
|
1592
1596
|
},
|
|
1597
|
+
"OpenAIVerbosityParam": {
|
|
1598
|
+
"type": [
|
|
1599
|
+
"string",
|
|
1600
|
+
"null"
|
|
1601
|
+
],
|
|
1602
|
+
"enum": [
|
|
1603
|
+
"low",
|
|
1604
|
+
"medium",
|
|
1605
|
+
"high",
|
|
1606
|
+
null
|
|
1607
|
+
]
|
|
1608
|
+
},
|
|
1593
1609
|
"OllamaLLMService": {
|
|
1594
1610
|
"type": "object",
|
|
1595
1611
|
"additionalProperties": false,
|
|
@@ -6054,6 +6070,26 @@ const options$4 = {
|
|
|
6054
6070
|
description: 'Generate changelog for all commits since the last tag',
|
|
6055
6071
|
default: false,
|
|
6056
6072
|
},
|
|
6073
|
+
withDiff: {
|
|
6074
|
+
type: 'boolean',
|
|
6075
|
+
description: 'Include the diff for each commit in the prompt',
|
|
6076
|
+
default: false,
|
|
6077
|
+
},
|
|
6078
|
+
onlyDiff: {
|
|
6079
|
+
type: 'boolean',
|
|
6080
|
+
description: 'Generate a changelog based only on the diff of the entire branch',
|
|
6081
|
+
default: false,
|
|
6082
|
+
},
|
|
6083
|
+
additional: {
|
|
6084
|
+
type: 'string',
|
|
6085
|
+
alias: 'a',
|
|
6086
|
+
description: 'Add extra contextual information to the prompt',
|
|
6087
|
+
},
|
|
6088
|
+
author: {
|
|
6089
|
+
type: 'boolean',
|
|
6090
|
+
description: 'Include author attribution in the changelog',
|
|
6091
|
+
default: false,
|
|
6092
|
+
},
|
|
6057
6093
|
i: {
|
|
6058
6094
|
type: 'boolean',
|
|
6059
6095
|
alias: 'interactive',
|
|
@@ -6958,45 +6994,39 @@ const getChangesSinceLastTag = async ({ git }) => {
|
|
|
6958
6994
|
};
|
|
6959
6995
|
|
|
6960
6996
|
/**
|
|
6961
|
-
* Retrieves the commit log range between two specified commits (inclusive of both commits).
|
|
6997
|
+
* Retrieves the detailed commit log range between two specified commits (inclusive of both commits).
|
|
6962
6998
|
*
|
|
6963
6999
|
* @param from - The starting commit (can be a commit hash, HEAD reference, or branch name). This commit will be included in the results.
|
|
6964
7000
|
* @param to - The ending commit (can be a commit hash, HEAD reference, or branch name). This commit will be included in the results.
|
|
6965
7001
|
* @param options - Additional options for retrieving the commit log range.
|
|
6966
|
-
* @returns A promise that resolves to an array of commit
|
|
7002
|
+
* @returns A promise that resolves to an array of commit details objects.
|
|
6967
7003
|
* @throws If there is an error retrieving the commit log range.
|
|
6968
7004
|
*/
|
|
6969
|
-
async function
|
|
7005
|
+
async function getCommitLogRangeDetails(from, to, { noMerges, git }) {
|
|
6970
7006
|
try {
|
|
6971
|
-
// Use from^..to to include the 'from' commit in the range
|
|
6972
|
-
// This works because from^..to means "commits reachable from 'to' but not from the parent of 'from'"
|
|
6973
7007
|
const logOptions = {
|
|
6974
7008
|
from: `${from}^`,
|
|
6975
7009
|
to,
|
|
6976
7010
|
'--no-merges': noMerges
|
|
6977
7011
|
};
|
|
6978
7012
|
const commitLog = await git.log(logOptions);
|
|
6979
|
-
return commitLog.all
|
|
7013
|
+
return [...commitLog.all];
|
|
6980
7014
|
}
|
|
6981
7015
|
catch (error) {
|
|
6982
|
-
// If from^ fails (e.g., 'from' is the first commit), fall back to using from..to and manually adding the 'from' commit
|
|
6983
7016
|
if (error instanceof Error && error.message.includes('unknown revision')) {
|
|
6984
7017
|
try {
|
|
6985
|
-
// Get the 'from' commit separately
|
|
6986
7018
|
const fromCommitLog = await git.log({ from: from, maxCount: 1 });
|
|
6987
7019
|
const fromCommit = fromCommitLog.latest;
|
|
6988
|
-
// Get the range from..to (excluding 'from')
|
|
6989
7020
|
const rangeLogOptions = {
|
|
6990
7021
|
from,
|
|
6991
7022
|
to,
|
|
6992
7023
|
'--no-merges': noMerges
|
|
6993
7024
|
};
|
|
6994
7025
|
const rangeCommitLog = await git.log(rangeLogOptions);
|
|
6995
|
-
// Combine the 'from' commit with the range commits
|
|
6996
7026
|
const allCommits = fromCommit
|
|
6997
7027
|
? [fromCommit, ...rangeCommitLog.all]
|
|
6998
|
-
: rangeCommitLog.all;
|
|
6999
|
-
return allCommits
|
|
7028
|
+
: [...rangeCommitLog.all];
|
|
7029
|
+
return allCommits;
|
|
7000
7030
|
}
|
|
7001
7031
|
catch (fallbackError) {
|
|
7002
7032
|
throw fallbackError;
|
|
@@ -7023,7 +7053,7 @@ async function getCurrentBranchName({ git }) {
|
|
|
7023
7053
|
* @param {SimpleGit} options.git - The SimpleGit instance.
|
|
7024
7054
|
* @param {Logger} options.logger - The logger for logging messages.
|
|
7025
7055
|
* @param {string} options.targetBranch - The target branch to compare against.
|
|
7026
|
-
* @returns {Promise<
|
|
7056
|
+
* @returns {Promise<CommitDetails[]>} The array of commit messages in the commit log.
|
|
7027
7057
|
*/
|
|
7028
7058
|
async function getCommitLogAgainstBranch({ git, logger, targetBranch, }) {
|
|
7029
7059
|
try {
|
|
@@ -7042,7 +7072,7 @@ async function getCommitLogAgainstBranch({ git, logger, targetBranch, }) {
|
|
|
7042
7072
|
return [];
|
|
7043
7073
|
}
|
|
7044
7074
|
// Retrieve commit log with messages
|
|
7045
|
-
return await
|
|
7075
|
+
return await getCommitLogRangeDetails(firstCommit, lastCommit, { git, noMerges: true });
|
|
7046
7076
|
}
|
|
7047
7077
|
catch (error) {
|
|
7048
7078
|
logger?.log('Encountered an error getting commit log between branches', { color: 'red' });
|
|
@@ -7058,7 +7088,7 @@ async function getCommitLogAgainstBranch({ git, logger, targetBranch, }) {
|
|
|
7058
7088
|
* @param {Logger} options.logger - The logger for logging messages.
|
|
7059
7089
|
* @param {string} [options.comparisonBranch='main'] - The branch to compare against.
|
|
7060
7090
|
* @param {string} [options.comparisonRemote='origin'] - The remote to compare against.
|
|
7061
|
-
* @returns {Promise<
|
|
7091
|
+
* @returns {Promise<CommitDetails[]>} The array of commit messages in the commit log.
|
|
7062
7092
|
*/
|
|
7063
7093
|
async function getCommitLogCurrentBranch({ git, logger, comparisonBranch = 'main', comparisonRemote = 'origin', }) {
|
|
7064
7094
|
try {
|
|
@@ -7094,7 +7124,7 @@ async function getCommitLogCurrentBranch({ git, logger, comparisonBranch = 'main
|
|
|
7094
7124
|
});
|
|
7095
7125
|
return [];
|
|
7096
7126
|
}
|
|
7097
|
-
return await
|
|
7127
|
+
return await getCommitLogRangeDetails(firstCommit, lastCommit, { git, noMerges: true });
|
|
7098
7128
|
}
|
|
7099
7129
|
catch (error) {
|
|
7100
7130
|
logger?.log('Encountered an error getting commit log from current branch', { color: 'red' });
|
|
@@ -7460,18 +7490,202 @@ async function handleResult({ result, mode, interactiveModeCallback }) {
|
|
|
7460
7490
|
}
|
|
7461
7491
|
}
|
|
7462
7492
|
|
|
7463
|
-
|
|
7493
|
+
/**
|
|
7494
|
+
* Fetches the diff for the given commit ID.
|
|
7495
|
+
*
|
|
7496
|
+
* @param commitId The commit ID for which the diff is to be retrieved.
|
|
7497
|
+
* @returns A promise that resolves to the diff of the commit.
|
|
7498
|
+
*/
|
|
7499
|
+
async function getDiffForCommit(commitId, { git, }) {
|
|
7500
|
+
try {
|
|
7501
|
+
return await git.diff(['-p', `${commitId}^..${commitId}`]);
|
|
7502
|
+
}
|
|
7503
|
+
catch (error) {
|
|
7504
|
+
throw new Error(`Error fetching diff for commit ${commitId}: ${error.message}`);
|
|
7505
|
+
}
|
|
7506
|
+
}
|
|
7507
|
+
|
|
7508
|
+
/**
|
|
7509
|
+
* Determines the status of a file based on its changes in the Git repository.
|
|
7510
|
+
*
|
|
7511
|
+
* @param file - The file to check the status of.
|
|
7512
|
+
* @param location - The location to check the status in ('index' or 'working_dir'). Defaults to 'index'.
|
|
7513
|
+
* @returns The status of the file ('added', 'deleted', 'modified', 'renamed', 'untracked', or 'unknown').
|
|
7514
|
+
* @throws Error if the file type is invalid.
|
|
7515
|
+
*/
|
|
7516
|
+
function getStatus(file, location = 'index') {
|
|
7517
|
+
if ('index' in file && 'working_dir' in file) {
|
|
7518
|
+
const statusCode = file[location];
|
|
7519
|
+
switch (statusCode) {
|
|
7520
|
+
case 'A':
|
|
7521
|
+
return 'added';
|
|
7522
|
+
case 'D':
|
|
7523
|
+
return 'deleted';
|
|
7524
|
+
case 'M':
|
|
7525
|
+
return 'modified';
|
|
7526
|
+
case 'R':
|
|
7527
|
+
return 'renamed';
|
|
7528
|
+
case '?':
|
|
7529
|
+
return 'untracked';
|
|
7530
|
+
default:
|
|
7531
|
+
return 'unknown';
|
|
7532
|
+
}
|
|
7533
|
+
}
|
|
7534
|
+
else if ('changes' in file && 'binary' in file) {
|
|
7535
|
+
if (file.changes === 0)
|
|
7536
|
+
return 'untracked';
|
|
7537
|
+
if (file.file.includes('=>'))
|
|
7538
|
+
return 'renamed';
|
|
7539
|
+
if (file.deletions === 0 && file.insertions > 0)
|
|
7540
|
+
return 'added';
|
|
7541
|
+
if (file.insertions === 0 && file.deletions > 0)
|
|
7542
|
+
return 'deleted';
|
|
7543
|
+
if ((file.insertions > 0 && file.deletions > 0) || file.changes > 0)
|
|
7544
|
+
return 'modified';
|
|
7545
|
+
return 'unknown';
|
|
7546
|
+
}
|
|
7547
|
+
else {
|
|
7548
|
+
throw new Error('Invalid file type');
|
|
7549
|
+
}
|
|
7550
|
+
}
|
|
7551
|
+
|
|
7552
|
+
/**
|
|
7553
|
+
* Returns the summary text for a file change.
|
|
7554
|
+
*
|
|
7555
|
+
* @param file - The file status or diff result.
|
|
7556
|
+
* @param change - The partial file change object.
|
|
7557
|
+
* @returns The summary text for the file change.
|
|
7558
|
+
* @throws Error if the file type is invalid.
|
|
7559
|
+
*/
|
|
7560
|
+
function getSummaryText(file, change) {
|
|
7561
|
+
const status = change.status || getStatus(file);
|
|
7562
|
+
let filePath;
|
|
7563
|
+
if ('path' in file) {
|
|
7564
|
+
filePath = file.path;
|
|
7565
|
+
}
|
|
7566
|
+
else if ('file' in file) {
|
|
7567
|
+
filePath = change?.filePath || file.file;
|
|
7568
|
+
}
|
|
7569
|
+
else {
|
|
7570
|
+
throw new Error('Invalid file type');
|
|
7571
|
+
}
|
|
7572
|
+
if (change.oldFilePath) {
|
|
7573
|
+
return `${status}: ${change.oldFilePath} -> ${filePath}`;
|
|
7574
|
+
}
|
|
7575
|
+
return `${status}: ${filePath}`;
|
|
7576
|
+
}
|
|
7577
|
+
|
|
7578
|
+
/**
|
|
7579
|
+
* Retrieves the diff between the current branch and a specified target branch.
|
|
7580
|
+
*
|
|
7581
|
+
* @param {Object} options - The options for retrieving the diff.
|
|
7582
|
+
* @param {SimpleGit} options.git - The SimpleGit instance.
|
|
7583
|
+
* @param {Logger} options.logger - The logger for logging messages.
|
|
7584
|
+
* @param {string} options.baseBranch - The base branch to compare against.
|
|
7585
|
+
* @param {string} options.headBranch - The head branch to compare.
|
|
7586
|
+
* @param {string[]} options.ignoredFiles - Array of specific files to ignore.
|
|
7587
|
+
* @param {string[]} options.ignoredExtensions - Array of file extensions to ignore.
|
|
7588
|
+
* @returns {Promise<GetChangesResult>} The diff between the current branch and the target branch.
|
|
7589
|
+
*/
|
|
7590
|
+
async function getDiffForBranch({ git, logger, baseBranch, headBranch, options, }) {
|
|
7591
|
+
try {
|
|
7592
|
+
logger?.verbose(`Getting diff for branches: baseBranch="${baseBranch}", headBranch="${headBranch}"`, {
|
|
7593
|
+
color: 'blue',
|
|
7594
|
+
});
|
|
7595
|
+
// Validate branch names
|
|
7596
|
+
if (!baseBranch || !headBranch) {
|
|
7597
|
+
throw new Error(`Invalid branch names: baseBranch="${baseBranch}", headBranch="${headBranch}"`);
|
|
7598
|
+
}
|
|
7599
|
+
const { ignoredFiles = [], ignoredExtensions = [] } = options || {};
|
|
7600
|
+
// Prepare ignore patterns
|
|
7601
|
+
const ignorePatterns = [
|
|
7602
|
+
...ignoredFiles.map((file) => `:!${file}`),
|
|
7603
|
+
...ignoredExtensions.map((ext) => `:!*${ext}`),
|
|
7604
|
+
];
|
|
7605
|
+
// Construct the diff command
|
|
7606
|
+
const diffArgs = [`${baseBranch}..${headBranch}`];
|
|
7607
|
+
if (ignorePatterns.length > 0) {
|
|
7608
|
+
diffArgs.push('--');
|
|
7609
|
+
diffArgs.push(...ignorePatterns);
|
|
7610
|
+
}
|
|
7611
|
+
logger?.verbose(`Running git diff with args: ${diffArgs.join(' ')}`, {
|
|
7612
|
+
color: 'blue',
|
|
7613
|
+
});
|
|
7614
|
+
// Get the diff
|
|
7615
|
+
const diff = await git.diff(diffArgs);
|
|
7616
|
+
logger?.verbose(`Generated diff between "${headBranch}" and "${baseBranch}"`, {
|
|
7617
|
+
color: 'blue',
|
|
7618
|
+
});
|
|
7619
|
+
const changes = diff.split('diff --git').slice(1).map((fileDiff) => {
|
|
7620
|
+
const lines = fileDiff.split('\n');
|
|
7621
|
+
const filePathLine = lines[0];
|
|
7622
|
+
const filePath = filePathLine.split('b/')[1]?.split(' ')[0];
|
|
7623
|
+
const oldFilePath = filePathLine.split('a/')[1]?.split(' ')[0];
|
|
7624
|
+
// Determine status based on diff headers
|
|
7625
|
+
let status = 'modified';
|
|
7626
|
+
if (fileDiff.includes('new file mode')) {
|
|
7627
|
+
status = 'added';
|
|
7628
|
+
}
|
|
7629
|
+
else if (fileDiff.includes('deleted file mode')) {
|
|
7630
|
+
status = 'deleted';
|
|
7631
|
+
}
|
|
7632
|
+
else if (fileDiff.includes('rename from')) {
|
|
7633
|
+
status = 'renamed';
|
|
7634
|
+
}
|
|
7635
|
+
return {
|
|
7636
|
+
filePath: filePath || '',
|
|
7637
|
+
oldFilePath: oldFilePath || '',
|
|
7638
|
+
status,
|
|
7639
|
+
summary: getSummaryText({ path: filePath || '', index: '', working_dir: '' }, { filePath: filePath || '', status }),
|
|
7640
|
+
};
|
|
7641
|
+
});
|
|
7642
|
+
return {
|
|
7643
|
+
staged: changes,
|
|
7644
|
+
unstaged: [],
|
|
7645
|
+
untracked: [],
|
|
7646
|
+
};
|
|
7647
|
+
}
|
|
7648
|
+
catch (error) {
|
|
7649
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
7650
|
+
console.error('Error in getDiffForBranch:', error);
|
|
7651
|
+
logger?.log(`Encountered an error getting diff between branches: ${errorMessage}`, { color: 'red' });
|
|
7652
|
+
logger?.log(`Branch details: baseBranch="${baseBranch}", headBranch="${headBranch}"`, { color: 'red' });
|
|
7653
|
+
// Re-throw the error so the caller can handle it appropriately
|
|
7654
|
+
throw error;
|
|
7655
|
+
}
|
|
7656
|
+
}
|
|
7657
|
+
|
|
7658
|
+
const template$3 = `You are a highly skilled software engineer tasked with writing a git changelog. Your response should be informative, well-structured, and in the imperative.
|
|
7464
7659
|
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7660
|
+
## Input
|
|
7661
|
+
You will be provided with a summary of changes. This summary can be one of the following:
|
|
7662
|
+
1. A list of commits, each with its author, hash, message, and body.
|
|
7663
|
+
2. A list of commits, each with its details AND the full diff of the changes.
|
|
7664
|
+
3. A single, comprehensive diff for an entire branch.
|
|
7470
7665
|
|
|
7666
|
+
## Rules
|
|
7667
|
+
- Create a descriptive title for the changelog that gives a high-level overview of the changes.
|
|
7668
|
+
- **BREAKING CHANGES**: Identify any commits that introduce breaking changes. These must be listed first under a "### 💥 BREAKING CHANGES" heading.
|
|
7669
|
+
- **Grouping**: Logically group related changes under descriptive headings (e.g., ### Features, ### Fixes, ### Refactors).
|
|
7670
|
+
- **Dependencies**: Group all dependency updates (e.g., changes to package.json, go.mod) under a "### Dependencies" section.
|
|
7671
|
+
- **Summaries**: For each change, provide a concise summary.
|
|
7672
|
+
- **Attribution**: {{author_instructions}}
|
|
7673
|
+
- **Technical Details**: If provided with diffs, use them to understand the technical details and provide a more accurate and detailed description of the changes.
|
|
7674
|
+
- **Clarity**: Avoid generalizations like "various bug fixes," "improvements," or "enhancements." Be specific.
|
|
7675
|
+
- **Formatting**: Your entire response must be valid Markdown.
|
|
7676
|
+
|
|
7677
|
+
## Formatting Instructions
|
|
7471
7678
|
{{format_instructions}}
|
|
7472
7679
|
|
|
7680
|
+
{{additional_context}}
|
|
7681
|
+
|
|
7473
7682
|
"""{{summary}}"""`;
|
|
7474
|
-
const inputVariables$2 = [
|
|
7683
|
+
const inputVariables$2 = [
|
|
7684
|
+
'format_instructions',
|
|
7685
|
+
'summary',
|
|
7686
|
+
'additional_context',
|
|
7687
|
+
'author_instructions',
|
|
7688
|
+
];
|
|
7475
7689
|
const CHANGELOG_PROMPT = new PromptTemplate({
|
|
7476
7690
|
template: template$3,
|
|
7477
7691
|
inputVariables: inputVariables$2,
|
|
@@ -7495,48 +7709,68 @@ const handler$4 = async (argv, logger) => {
|
|
|
7495
7709
|
}
|
|
7496
7710
|
async function factory() {
|
|
7497
7711
|
const branchName = await getCurrentBranchName({ git });
|
|
7498
|
-
if (
|
|
7499
|
-
logger.verbose(`Generating
|
|
7712
|
+
if (argv.onlyDiff) {
|
|
7713
|
+
logger.verbose(`Generating changelog based on branch diff`, { color: 'yellow' });
|
|
7714
|
+
const diff = await getDiffForBranch({ git, logger, baseBranch: argv.branch || 'main', headBranch: branchName });
|
|
7500
7715
|
return {
|
|
7501
7716
|
branch: branchName,
|
|
7502
|
-
|
|
7717
|
+
diff: JSON.stringify(diff.staged, null, 2),
|
|
7503
7718
|
};
|
|
7504
7719
|
}
|
|
7505
|
-
|
|
7720
|
+
let commits = [];
|
|
7721
|
+
if (config.sinceLastTag) {
|
|
7722
|
+
logger.verbose(`Generating commit log since the last tag`, { color: 'yellow' });
|
|
7723
|
+
// This function returns string[], needs to be adapted or replaced
|
|
7724
|
+
// For now, this path will have limited details.
|
|
7725
|
+
const commitMessages = await getChangesSinceLastTag({ git, logger });
|
|
7726
|
+
commits = commitMessages.map(msg => ({ message: msg }));
|
|
7727
|
+
}
|
|
7728
|
+
else if (config.range && config.range.includes(':')) {
|
|
7506
7729
|
const [from, to] = config.range.split(':');
|
|
7507
7730
|
if (!from || !to) {
|
|
7508
7731
|
logger.log(`Invalid range provided. Expected format is <from>:<to>`, { color: 'red' });
|
|
7509
7732
|
process.exit(1);
|
|
7510
7733
|
}
|
|
7511
|
-
|
|
7512
|
-
branch: branchName,
|
|
7513
|
-
commits: await getCommitLogRange(from, to, { git, noMerges: true }),
|
|
7514
|
-
};
|
|
7734
|
+
commits = await getCommitLogRangeDetails(from, to, { git, noMerges: true });
|
|
7515
7735
|
}
|
|
7516
|
-
if (argv.branch) {
|
|
7736
|
+
else if (argv.branch) {
|
|
7517
7737
|
logger.verbose(`Generating commit log against branch: ${argv.branch}`, { color: 'yellow' });
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7738
|
+
commits = await getCommitLogAgainstBranch({ git, logger, targetBranch: argv.branch });
|
|
7739
|
+
}
|
|
7740
|
+
else {
|
|
7741
|
+
logger.verbose(`No range, branch, or tag option provided. Defaulting to current branch`, {
|
|
7742
|
+
color: 'yellow',
|
|
7743
|
+
});
|
|
7744
|
+
commits = await getCommitLogCurrentBranch({ git, logger });
|
|
7745
|
+
}
|
|
7746
|
+
let commitsWithDiffText = commits;
|
|
7747
|
+
if (argv.withDiff) {
|
|
7748
|
+
commitsWithDiffText = await Promise.all(commits.map(async (commit) => ({
|
|
7749
|
+
...commit,
|
|
7750
|
+
diffText: await getDiffForCommit(commit.hash, { git }),
|
|
7751
|
+
})));
|
|
7522
7752
|
}
|
|
7523
|
-
logger.verbose(`No range, branch, or tag option provided. Defaulting to current branch`, {
|
|
7524
|
-
color: 'yellow',
|
|
7525
|
-
});
|
|
7526
|
-
const commits = await getCommitLogCurrentBranch({ git, logger });
|
|
7527
7753
|
return {
|
|
7528
7754
|
branch: branchName,
|
|
7529
|
-
commits,
|
|
7755
|
+
commits: commitsWithDiffText,
|
|
7756
|
+
withDiff: argv.withDiff,
|
|
7530
7757
|
};
|
|
7531
7758
|
}
|
|
7532
|
-
async function parser(
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
result = `## ${branch}\n\nNo commits found.`;
|
|
7759
|
+
async function parser(data) {
|
|
7760
|
+
if (data.diff) {
|
|
7761
|
+
return `## Diff for ${data.branch}\n\n${data.diff}`;
|
|
7536
7762
|
}
|
|
7537
|
-
|
|
7538
|
-
|
|
7763
|
+
if (!data.commits || data.commits.length === 0) {
|
|
7764
|
+
return `## ${data.branch}\n\nNo commits found.`;
|
|
7539
7765
|
}
|
|
7766
|
+
let result = `## ${data.branch}\n\n`;
|
|
7767
|
+
result += data.commits.map(commit => {
|
|
7768
|
+
let commitStr = `Author: ${commit.author_name}\nCommit: ${commit.hash}\nMessage: ${commit.message}\n${commit.body}`;
|
|
7769
|
+
if (data.withDiff && commit.diffText) {
|
|
7770
|
+
commitStr += `\nDiff:\n${commit.diffText}`;
|
|
7771
|
+
}
|
|
7772
|
+
return commitStr.trim();
|
|
7773
|
+
}).join('\n\n---\n\n');
|
|
7540
7774
|
return result;
|
|
7541
7775
|
}
|
|
7542
7776
|
const changelogMsg = await generateAndReviewLoop({
|
|
@@ -7560,12 +7794,21 @@ const handler$4 = async (argv, logger) => {
|
|
|
7560
7794
|
fallback: CHANGELOG_PROMPT,
|
|
7561
7795
|
});
|
|
7562
7796
|
const formatInstructions = "Only respond with a valid JSON object, containing two fields: 'title' an escaped string, no more than 65 characters, and 'content' also an escaped string.";
|
|
7797
|
+
let additional_context = '';
|
|
7798
|
+
if (argv.additional) {
|
|
7799
|
+
additional_context = `## Additional Context\n${argv.additional}`;
|
|
7800
|
+
}
|
|
7801
|
+
const author_instructions = argv.author
|
|
7802
|
+
? 'At the end of each item, attribute the author and include a reference to the commit hash, like this: `by @author_name (f6dbe61)`. Use the first 7 characters of the hash.'
|
|
7803
|
+
: 'At the end of each item, include a reference to the commit hash, like this: `(f6dbe61)`. Use the first 7 characters of the hash.';
|
|
7563
7804
|
const changelog = await executeChain({
|
|
7564
7805
|
llm,
|
|
7565
7806
|
prompt,
|
|
7566
7807
|
variables: {
|
|
7567
7808
|
summary: context,
|
|
7568
7809
|
format_instructions: formatInstructions,
|
|
7810
|
+
additional_context: additional_context,
|
|
7811
|
+
author_instructions: author_instructions,
|
|
7569
7812
|
},
|
|
7570
7813
|
parser,
|
|
7571
7814
|
});
|
|
@@ -10877,76 +11120,6 @@ async function createCommit(message, git) {
|
|
|
10877
11120
|
return await git.commit(message);
|
|
10878
11121
|
}
|
|
10879
11122
|
|
|
10880
|
-
/**
|
|
10881
|
-
* Determines the status of a file based on its changes in the Git repository.
|
|
10882
|
-
*
|
|
10883
|
-
* @param file - The file to check the status of.
|
|
10884
|
-
* @param location - The location to check the status in ('index' or 'working_dir'). Defaults to 'index'.
|
|
10885
|
-
* @returns The status of the file ('added', 'deleted', 'modified', 'renamed', 'untracked', or 'unknown').
|
|
10886
|
-
* @throws Error if the file type is invalid.
|
|
10887
|
-
*/
|
|
10888
|
-
function getStatus(file, location = 'index') {
|
|
10889
|
-
if ('index' in file && 'working_dir' in file) {
|
|
10890
|
-
const statusCode = file[location];
|
|
10891
|
-
switch (statusCode) {
|
|
10892
|
-
case 'A':
|
|
10893
|
-
return 'added';
|
|
10894
|
-
case 'D':
|
|
10895
|
-
return 'deleted';
|
|
10896
|
-
case 'M':
|
|
10897
|
-
return 'modified';
|
|
10898
|
-
case 'R':
|
|
10899
|
-
return 'renamed';
|
|
10900
|
-
case '?':
|
|
10901
|
-
return 'untracked';
|
|
10902
|
-
default:
|
|
10903
|
-
return 'unknown';
|
|
10904
|
-
}
|
|
10905
|
-
}
|
|
10906
|
-
else if ('changes' in file && 'binary' in file) {
|
|
10907
|
-
if (file.changes === 0)
|
|
10908
|
-
return 'untracked';
|
|
10909
|
-
if (file.file.includes('=>'))
|
|
10910
|
-
return 'renamed';
|
|
10911
|
-
if (file.deletions === 0 && file.insertions > 0)
|
|
10912
|
-
return 'added';
|
|
10913
|
-
if (file.insertions === 0 && file.deletions > 0)
|
|
10914
|
-
return 'deleted';
|
|
10915
|
-
if ((file.insertions > 0 && file.deletions > 0) || file.changes > 0)
|
|
10916
|
-
return 'modified';
|
|
10917
|
-
return 'unknown';
|
|
10918
|
-
}
|
|
10919
|
-
else {
|
|
10920
|
-
throw new Error('Invalid file type');
|
|
10921
|
-
}
|
|
10922
|
-
}
|
|
10923
|
-
|
|
10924
|
-
/**
|
|
10925
|
-
* Returns the summary text for a file change.
|
|
10926
|
-
*
|
|
10927
|
-
* @param file - The file status or diff result.
|
|
10928
|
-
* @param change - The partial file change object.
|
|
10929
|
-
* @returns The summary text for the file change.
|
|
10930
|
-
* @throws Error if the file type is invalid.
|
|
10931
|
-
*/
|
|
10932
|
-
function getSummaryText(file, change) {
|
|
10933
|
-
const status = change.status || getStatus(file);
|
|
10934
|
-
let filePath;
|
|
10935
|
-
if ('path' in file) {
|
|
10936
|
-
filePath = file.path;
|
|
10937
|
-
}
|
|
10938
|
-
else if ('file' in file) {
|
|
10939
|
-
filePath = change?.filePath || file.file;
|
|
10940
|
-
}
|
|
10941
|
-
else {
|
|
10942
|
-
throw new Error('Invalid file type');
|
|
10943
|
-
}
|
|
10944
|
-
if (change.oldFilePath) {
|
|
10945
|
-
return `${status}: ${change.oldFilePath} -> ${filePath}`;
|
|
10946
|
-
}
|
|
10947
|
-
return `${status}: ${filePath}`;
|
|
10948
|
-
}
|
|
10949
|
-
|
|
10950
11123
|
/**
|
|
10951
11124
|
* Retrieves the changes in the Git repository.
|
|
10952
11125
|
*
|
|
@@ -11264,7 +11437,7 @@ const handler$3 = async (argv, logger) => {
|
|
|
11264
11437
|
const schema = USE_CONVENTIONAL_COMMITS
|
|
11265
11438
|
? ConventionalCommitMessageResponseSchema
|
|
11266
11439
|
: CommitMessageResponseSchema;
|
|
11267
|
-
const formatInstructions = `You must always return valid JSON
|
|
11440
|
+
const formatInstructions = `You must always return a valid JSON object. Do not return any additional text. The JSON object you return should match the following schema:
|
|
11268
11441
|
${schema.description}
|
|
11269
11442
|
{
|
|
11270
11443
|
"title": "The commit title",
|
|
@@ -11984,86 +12157,6 @@ const getChangesByTimestamp = async ({ since, git }) => {
|
|
|
11984
12157
|
return formatCommitLog(commitLog);
|
|
11985
12158
|
};
|
|
11986
12159
|
|
|
11987
|
-
/**
|
|
11988
|
-
* Retrieves the diff between the current branch and a specified target branch.
|
|
11989
|
-
*
|
|
11990
|
-
* @param {Object} options - The options for retrieving the diff.
|
|
11991
|
-
* @param {SimpleGit} options.git - The SimpleGit instance.
|
|
11992
|
-
* @param {Logger} options.logger - The logger for logging messages.
|
|
11993
|
-
* @param {string} options.baseBranch - The base branch to compare against.
|
|
11994
|
-
* @param {string} options.headBranch - The head branch to compare.
|
|
11995
|
-
* @param {string[]} options.ignoredFiles - Array of specific files to ignore.
|
|
11996
|
-
* @param {string[]} options.ignoredExtensions - Array of file extensions to ignore.
|
|
11997
|
-
* @returns {Promise<GetChangesResult>} The diff between the current branch and the target branch.
|
|
11998
|
-
*/
|
|
11999
|
-
async function getDiffForBranch({ git, logger, baseBranch, headBranch, options, }) {
|
|
12000
|
-
try {
|
|
12001
|
-
logger?.verbose(`Getting diff for branches: baseBranch="${baseBranch}", headBranch="${headBranch}"`, {
|
|
12002
|
-
color: 'blue',
|
|
12003
|
-
});
|
|
12004
|
-
// Validate branch names
|
|
12005
|
-
if (!baseBranch || !headBranch) {
|
|
12006
|
-
throw new Error(`Invalid branch names: baseBranch="${baseBranch}", headBranch="${headBranch}"`);
|
|
12007
|
-
}
|
|
12008
|
-
const { ignoredFiles = [], ignoredExtensions = [] } = options || {};
|
|
12009
|
-
// Prepare ignore patterns
|
|
12010
|
-
const ignorePatterns = [
|
|
12011
|
-
...ignoredFiles.map((file) => `:!${file}`),
|
|
12012
|
-
...ignoredExtensions.map((ext) => `:!*${ext}`),
|
|
12013
|
-
];
|
|
12014
|
-
// Construct the diff command
|
|
12015
|
-
const diffArgs = [`${baseBranch}..${headBranch}`];
|
|
12016
|
-
if (ignorePatterns.length > 0) {
|
|
12017
|
-
diffArgs.push('--');
|
|
12018
|
-
diffArgs.push(...ignorePatterns);
|
|
12019
|
-
}
|
|
12020
|
-
logger?.verbose(`Running git diff with args: ${diffArgs.join(' ')}`, {
|
|
12021
|
-
color: 'blue',
|
|
12022
|
-
});
|
|
12023
|
-
// Get the diff
|
|
12024
|
-
const diff = await git.diff(diffArgs);
|
|
12025
|
-
logger?.verbose(`Generated diff between "${headBranch}" and "${baseBranch}"`, {
|
|
12026
|
-
color: 'blue',
|
|
12027
|
-
});
|
|
12028
|
-
const changes = diff.split('diff --git').slice(1).map((fileDiff) => {
|
|
12029
|
-
const lines = fileDiff.split('\n');
|
|
12030
|
-
const filePathLine = lines[0];
|
|
12031
|
-
const filePath = filePathLine.split('b/')[1]?.split(' ')[0];
|
|
12032
|
-
const oldFilePath = filePathLine.split('a/')[1]?.split(' ')[0];
|
|
12033
|
-
// Determine status based on diff headers
|
|
12034
|
-
let status = 'modified';
|
|
12035
|
-
if (fileDiff.includes('new file mode')) {
|
|
12036
|
-
status = 'added';
|
|
12037
|
-
}
|
|
12038
|
-
else if (fileDiff.includes('deleted file mode')) {
|
|
12039
|
-
status = 'deleted';
|
|
12040
|
-
}
|
|
12041
|
-
else if (fileDiff.includes('rename from')) {
|
|
12042
|
-
status = 'renamed';
|
|
12043
|
-
}
|
|
12044
|
-
return {
|
|
12045
|
-
filePath: filePath || '',
|
|
12046
|
-
oldFilePath: oldFilePath || '',
|
|
12047
|
-
status,
|
|
12048
|
-
summary: getSummaryText({ path: filePath || '', index: '', working_dir: '' }, { filePath: filePath || '', status }),
|
|
12049
|
-
};
|
|
12050
|
-
});
|
|
12051
|
-
return {
|
|
12052
|
-
staged: changes,
|
|
12053
|
-
unstaged: [],
|
|
12054
|
-
untracked: [],
|
|
12055
|
-
};
|
|
12056
|
-
}
|
|
12057
|
-
catch (error) {
|
|
12058
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
12059
|
-
console.error('Error in getDiffForBranch:', error);
|
|
12060
|
-
logger?.log(`Encountered an error getting diff between branches: ${errorMessage}`, { color: 'red' });
|
|
12061
|
-
logger?.log(`Branch details: baseBranch="${baseBranch}", headBranch="${headBranch}"`, { color: 'red' });
|
|
12062
|
-
// Re-throw the error so the caller can handle it appropriately
|
|
12063
|
-
throw error;
|
|
12064
|
-
}
|
|
12065
|
-
}
|
|
12066
|
-
|
|
12067
12160
|
async function noResult$1({ logger }) {
|
|
12068
12161
|
logger.log('No repo changes detected. 👀', { color: 'blue' });
|
|
12069
12162
|
throw new Error('NO_CHANGES_DETECTED');
|
package/dist/index.js
CHANGED
|
@@ -70,7 +70,7 @@ var readline__namespace = /*#__PURE__*/_interopNamespaceDefault(readline);
|
|
|
70
70
|
/**
|
|
71
71
|
* Current build version from package.json
|
|
72
72
|
*/
|
|
73
|
-
const BUILD_VERSION = "0.
|
|
73
|
+
const BUILD_VERSION = "0.22.1";
|
|
74
74
|
|
|
75
75
|
const isInteractive = (config) => {
|
|
76
76
|
return config?.mode === 'interactive' || !!config?.interactive;
|
|
@@ -1040,6 +1040,10 @@ const schema$1 = {
|
|
|
1040
1040
|
"apiKey": {
|
|
1041
1041
|
"type": "string",
|
|
1042
1042
|
"description": "API key to use when making requests to OpenAI. Defaults to the value of `OPENAI_API_KEY` environment variable."
|
|
1043
|
+
},
|
|
1044
|
+
"verbosity": {
|
|
1045
|
+
"$ref": "#/definitions/OpenAIVerbosityParam",
|
|
1046
|
+
"description": "The verbosity of the model's response."
|
|
1043
1047
|
}
|
|
1044
1048
|
}
|
|
1045
1049
|
},
|
|
@@ -1612,6 +1616,18 @@ const schema$1 = {
|
|
|
1612
1616
|
"gpt-3.5-turbo-16k-0613"
|
|
1613
1617
|
]
|
|
1614
1618
|
},
|
|
1619
|
+
"OpenAIVerbosityParam": {
|
|
1620
|
+
"type": [
|
|
1621
|
+
"string",
|
|
1622
|
+
"null"
|
|
1623
|
+
],
|
|
1624
|
+
"enum": [
|
|
1625
|
+
"low",
|
|
1626
|
+
"medium",
|
|
1627
|
+
"high",
|
|
1628
|
+
null
|
|
1629
|
+
]
|
|
1630
|
+
},
|
|
1615
1631
|
"OllamaLLMService": {
|
|
1616
1632
|
"type": "object",
|
|
1617
1633
|
"additionalProperties": false,
|
|
@@ -6076,6 +6092,26 @@ const options$4 = {
|
|
|
6076
6092
|
description: 'Generate changelog for all commits since the last tag',
|
|
6077
6093
|
default: false,
|
|
6078
6094
|
},
|
|
6095
|
+
withDiff: {
|
|
6096
|
+
type: 'boolean',
|
|
6097
|
+
description: 'Include the diff for each commit in the prompt',
|
|
6098
|
+
default: false,
|
|
6099
|
+
},
|
|
6100
|
+
onlyDiff: {
|
|
6101
|
+
type: 'boolean',
|
|
6102
|
+
description: 'Generate a changelog based only on the diff of the entire branch',
|
|
6103
|
+
default: false,
|
|
6104
|
+
},
|
|
6105
|
+
additional: {
|
|
6106
|
+
type: 'string',
|
|
6107
|
+
alias: 'a',
|
|
6108
|
+
description: 'Add extra contextual information to the prompt',
|
|
6109
|
+
},
|
|
6110
|
+
author: {
|
|
6111
|
+
type: 'boolean',
|
|
6112
|
+
description: 'Include author attribution in the changelog',
|
|
6113
|
+
default: false,
|
|
6114
|
+
},
|
|
6079
6115
|
i: {
|
|
6080
6116
|
type: 'boolean',
|
|
6081
6117
|
alias: 'interactive',
|
|
@@ -6980,45 +7016,39 @@ const getChangesSinceLastTag = async ({ git }) => {
|
|
|
6980
7016
|
};
|
|
6981
7017
|
|
|
6982
7018
|
/**
|
|
6983
|
-
* Retrieves the commit log range between two specified commits (inclusive of both commits).
|
|
7019
|
+
* Retrieves the detailed commit log range between two specified commits (inclusive of both commits).
|
|
6984
7020
|
*
|
|
6985
7021
|
* @param from - The starting commit (can be a commit hash, HEAD reference, or branch name). This commit will be included in the results.
|
|
6986
7022
|
* @param to - The ending commit (can be a commit hash, HEAD reference, or branch name). This commit will be included in the results.
|
|
6987
7023
|
* @param options - Additional options for retrieving the commit log range.
|
|
6988
|
-
* @returns A promise that resolves to an array of commit
|
|
7024
|
+
* @returns A promise that resolves to an array of commit details objects.
|
|
6989
7025
|
* @throws If there is an error retrieving the commit log range.
|
|
6990
7026
|
*/
|
|
6991
|
-
async function
|
|
7027
|
+
async function getCommitLogRangeDetails(from, to, { noMerges, git }) {
|
|
6992
7028
|
try {
|
|
6993
|
-
// Use from^..to to include the 'from' commit in the range
|
|
6994
|
-
// This works because from^..to means "commits reachable from 'to' but not from the parent of 'from'"
|
|
6995
7029
|
const logOptions = {
|
|
6996
7030
|
from: `${from}^`,
|
|
6997
7031
|
to,
|
|
6998
7032
|
'--no-merges': noMerges
|
|
6999
7033
|
};
|
|
7000
7034
|
const commitLog = await git.log(logOptions);
|
|
7001
|
-
return commitLog.all
|
|
7035
|
+
return [...commitLog.all];
|
|
7002
7036
|
}
|
|
7003
7037
|
catch (error) {
|
|
7004
|
-
// If from^ fails (e.g., 'from' is the first commit), fall back to using from..to and manually adding the 'from' commit
|
|
7005
7038
|
if (error instanceof Error && error.message.includes('unknown revision')) {
|
|
7006
7039
|
try {
|
|
7007
|
-
// Get the 'from' commit separately
|
|
7008
7040
|
const fromCommitLog = await git.log({ from: from, maxCount: 1 });
|
|
7009
7041
|
const fromCommit = fromCommitLog.latest;
|
|
7010
|
-
// Get the range from..to (excluding 'from')
|
|
7011
7042
|
const rangeLogOptions = {
|
|
7012
7043
|
from,
|
|
7013
7044
|
to,
|
|
7014
7045
|
'--no-merges': noMerges
|
|
7015
7046
|
};
|
|
7016
7047
|
const rangeCommitLog = await git.log(rangeLogOptions);
|
|
7017
|
-
// Combine the 'from' commit with the range commits
|
|
7018
7048
|
const allCommits = fromCommit
|
|
7019
7049
|
? [fromCommit, ...rangeCommitLog.all]
|
|
7020
|
-
: rangeCommitLog.all;
|
|
7021
|
-
return allCommits
|
|
7050
|
+
: [...rangeCommitLog.all];
|
|
7051
|
+
return allCommits;
|
|
7022
7052
|
}
|
|
7023
7053
|
catch (fallbackError) {
|
|
7024
7054
|
throw fallbackError;
|
|
@@ -7045,7 +7075,7 @@ async function getCurrentBranchName({ git }) {
|
|
|
7045
7075
|
* @param {SimpleGit} options.git - The SimpleGit instance.
|
|
7046
7076
|
* @param {Logger} options.logger - The logger for logging messages.
|
|
7047
7077
|
* @param {string} options.targetBranch - The target branch to compare against.
|
|
7048
|
-
* @returns {Promise<
|
|
7078
|
+
* @returns {Promise<CommitDetails[]>} The array of commit messages in the commit log.
|
|
7049
7079
|
*/
|
|
7050
7080
|
async function getCommitLogAgainstBranch({ git, logger, targetBranch, }) {
|
|
7051
7081
|
try {
|
|
@@ -7064,7 +7094,7 @@ async function getCommitLogAgainstBranch({ git, logger, targetBranch, }) {
|
|
|
7064
7094
|
return [];
|
|
7065
7095
|
}
|
|
7066
7096
|
// Retrieve commit log with messages
|
|
7067
|
-
return await
|
|
7097
|
+
return await getCommitLogRangeDetails(firstCommit, lastCommit, { git, noMerges: true });
|
|
7068
7098
|
}
|
|
7069
7099
|
catch (error) {
|
|
7070
7100
|
logger?.log('Encountered an error getting commit log between branches', { color: 'red' });
|
|
@@ -7080,7 +7110,7 @@ async function getCommitLogAgainstBranch({ git, logger, targetBranch, }) {
|
|
|
7080
7110
|
* @param {Logger} options.logger - The logger for logging messages.
|
|
7081
7111
|
* @param {string} [options.comparisonBranch='main'] - The branch to compare against.
|
|
7082
7112
|
* @param {string} [options.comparisonRemote='origin'] - The remote to compare against.
|
|
7083
|
-
* @returns {Promise<
|
|
7113
|
+
* @returns {Promise<CommitDetails[]>} The array of commit messages in the commit log.
|
|
7084
7114
|
*/
|
|
7085
7115
|
async function getCommitLogCurrentBranch({ git, logger, comparisonBranch = 'main', comparisonRemote = 'origin', }) {
|
|
7086
7116
|
try {
|
|
@@ -7116,7 +7146,7 @@ async function getCommitLogCurrentBranch({ git, logger, comparisonBranch = 'main
|
|
|
7116
7146
|
});
|
|
7117
7147
|
return [];
|
|
7118
7148
|
}
|
|
7119
|
-
return await
|
|
7149
|
+
return await getCommitLogRangeDetails(firstCommit, lastCommit, { git, noMerges: true });
|
|
7120
7150
|
}
|
|
7121
7151
|
catch (error) {
|
|
7122
7152
|
logger?.log('Encountered an error getting commit log from current branch', { color: 'red' });
|
|
@@ -7482,18 +7512,202 @@ async function handleResult({ result, mode, interactiveModeCallback }) {
|
|
|
7482
7512
|
}
|
|
7483
7513
|
}
|
|
7484
7514
|
|
|
7485
|
-
|
|
7515
|
+
/**
|
|
7516
|
+
* Fetches the diff for the given commit ID.
|
|
7517
|
+
*
|
|
7518
|
+
* @param commitId The commit ID for which the diff is to be retrieved.
|
|
7519
|
+
* @returns A promise that resolves to the diff of the commit.
|
|
7520
|
+
*/
|
|
7521
|
+
async function getDiffForCommit(commitId, { git, }) {
|
|
7522
|
+
try {
|
|
7523
|
+
return await git.diff(['-p', `${commitId}^..${commitId}`]);
|
|
7524
|
+
}
|
|
7525
|
+
catch (error) {
|
|
7526
|
+
throw new Error(`Error fetching diff for commit ${commitId}: ${error.message}`);
|
|
7527
|
+
}
|
|
7528
|
+
}
|
|
7529
|
+
|
|
7530
|
+
/**
|
|
7531
|
+
* Determines the status of a file based on its changes in the Git repository.
|
|
7532
|
+
*
|
|
7533
|
+
* @param file - The file to check the status of.
|
|
7534
|
+
* @param location - The location to check the status in ('index' or 'working_dir'). Defaults to 'index'.
|
|
7535
|
+
* @returns The status of the file ('added', 'deleted', 'modified', 'renamed', 'untracked', or 'unknown').
|
|
7536
|
+
* @throws Error if the file type is invalid.
|
|
7537
|
+
*/
|
|
7538
|
+
function getStatus(file, location = 'index') {
|
|
7539
|
+
if ('index' in file && 'working_dir' in file) {
|
|
7540
|
+
const statusCode = file[location];
|
|
7541
|
+
switch (statusCode) {
|
|
7542
|
+
case 'A':
|
|
7543
|
+
return 'added';
|
|
7544
|
+
case 'D':
|
|
7545
|
+
return 'deleted';
|
|
7546
|
+
case 'M':
|
|
7547
|
+
return 'modified';
|
|
7548
|
+
case 'R':
|
|
7549
|
+
return 'renamed';
|
|
7550
|
+
case '?':
|
|
7551
|
+
return 'untracked';
|
|
7552
|
+
default:
|
|
7553
|
+
return 'unknown';
|
|
7554
|
+
}
|
|
7555
|
+
}
|
|
7556
|
+
else if ('changes' in file && 'binary' in file) {
|
|
7557
|
+
if (file.changes === 0)
|
|
7558
|
+
return 'untracked';
|
|
7559
|
+
if (file.file.includes('=>'))
|
|
7560
|
+
return 'renamed';
|
|
7561
|
+
if (file.deletions === 0 && file.insertions > 0)
|
|
7562
|
+
return 'added';
|
|
7563
|
+
if (file.insertions === 0 && file.deletions > 0)
|
|
7564
|
+
return 'deleted';
|
|
7565
|
+
if ((file.insertions > 0 && file.deletions > 0) || file.changes > 0)
|
|
7566
|
+
return 'modified';
|
|
7567
|
+
return 'unknown';
|
|
7568
|
+
}
|
|
7569
|
+
else {
|
|
7570
|
+
throw new Error('Invalid file type');
|
|
7571
|
+
}
|
|
7572
|
+
}
|
|
7573
|
+
|
|
7574
|
+
/**
|
|
7575
|
+
* Returns the summary text for a file change.
|
|
7576
|
+
*
|
|
7577
|
+
* @param file - The file status or diff result.
|
|
7578
|
+
* @param change - The partial file change object.
|
|
7579
|
+
* @returns The summary text for the file change.
|
|
7580
|
+
* @throws Error if the file type is invalid.
|
|
7581
|
+
*/
|
|
7582
|
+
function getSummaryText(file, change) {
|
|
7583
|
+
const status = change.status || getStatus(file);
|
|
7584
|
+
let filePath;
|
|
7585
|
+
if ('path' in file) {
|
|
7586
|
+
filePath = file.path;
|
|
7587
|
+
}
|
|
7588
|
+
else if ('file' in file) {
|
|
7589
|
+
filePath = change?.filePath || file.file;
|
|
7590
|
+
}
|
|
7591
|
+
else {
|
|
7592
|
+
throw new Error('Invalid file type');
|
|
7593
|
+
}
|
|
7594
|
+
if (change.oldFilePath) {
|
|
7595
|
+
return `${status}: ${change.oldFilePath} -> ${filePath}`;
|
|
7596
|
+
}
|
|
7597
|
+
return `${status}: ${filePath}`;
|
|
7598
|
+
}
|
|
7599
|
+
|
|
7600
|
+
/**
|
|
7601
|
+
* Retrieves the diff between the current branch and a specified target branch.
|
|
7602
|
+
*
|
|
7603
|
+
* @param {Object} options - The options for retrieving the diff.
|
|
7604
|
+
* @param {SimpleGit} options.git - The SimpleGit instance.
|
|
7605
|
+
* @param {Logger} options.logger - The logger for logging messages.
|
|
7606
|
+
* @param {string} options.baseBranch - The base branch to compare against.
|
|
7607
|
+
* @param {string} options.headBranch - The head branch to compare.
|
|
7608
|
+
* @param {string[]} options.ignoredFiles - Array of specific files to ignore.
|
|
7609
|
+
* @param {string[]} options.ignoredExtensions - Array of file extensions to ignore.
|
|
7610
|
+
* @returns {Promise<GetChangesResult>} The diff between the current branch and the target branch.
|
|
7611
|
+
*/
|
|
7612
|
+
async function getDiffForBranch({ git, logger, baseBranch, headBranch, options, }) {
|
|
7613
|
+
try {
|
|
7614
|
+
logger?.verbose(`Getting diff for branches: baseBranch="${baseBranch}", headBranch="${headBranch}"`, {
|
|
7615
|
+
color: 'blue',
|
|
7616
|
+
});
|
|
7617
|
+
// Validate branch names
|
|
7618
|
+
if (!baseBranch || !headBranch) {
|
|
7619
|
+
throw new Error(`Invalid branch names: baseBranch="${baseBranch}", headBranch="${headBranch}"`);
|
|
7620
|
+
}
|
|
7621
|
+
const { ignoredFiles = [], ignoredExtensions = [] } = options || {};
|
|
7622
|
+
// Prepare ignore patterns
|
|
7623
|
+
const ignorePatterns = [
|
|
7624
|
+
...ignoredFiles.map((file) => `:!${file}`),
|
|
7625
|
+
...ignoredExtensions.map((ext) => `:!*${ext}`),
|
|
7626
|
+
];
|
|
7627
|
+
// Construct the diff command
|
|
7628
|
+
const diffArgs = [`${baseBranch}..${headBranch}`];
|
|
7629
|
+
if (ignorePatterns.length > 0) {
|
|
7630
|
+
diffArgs.push('--');
|
|
7631
|
+
diffArgs.push(...ignorePatterns);
|
|
7632
|
+
}
|
|
7633
|
+
logger?.verbose(`Running git diff with args: ${diffArgs.join(' ')}`, {
|
|
7634
|
+
color: 'blue',
|
|
7635
|
+
});
|
|
7636
|
+
// Get the diff
|
|
7637
|
+
const diff = await git.diff(diffArgs);
|
|
7638
|
+
logger?.verbose(`Generated diff between "${headBranch}" and "${baseBranch}"`, {
|
|
7639
|
+
color: 'blue',
|
|
7640
|
+
});
|
|
7641
|
+
const changes = diff.split('diff --git').slice(1).map((fileDiff) => {
|
|
7642
|
+
const lines = fileDiff.split('\n');
|
|
7643
|
+
const filePathLine = lines[0];
|
|
7644
|
+
const filePath = filePathLine.split('b/')[1]?.split(' ')[0];
|
|
7645
|
+
const oldFilePath = filePathLine.split('a/')[1]?.split(' ')[0];
|
|
7646
|
+
// Determine status based on diff headers
|
|
7647
|
+
let status = 'modified';
|
|
7648
|
+
if (fileDiff.includes('new file mode')) {
|
|
7649
|
+
status = 'added';
|
|
7650
|
+
}
|
|
7651
|
+
else if (fileDiff.includes('deleted file mode')) {
|
|
7652
|
+
status = 'deleted';
|
|
7653
|
+
}
|
|
7654
|
+
else if (fileDiff.includes('rename from')) {
|
|
7655
|
+
status = 'renamed';
|
|
7656
|
+
}
|
|
7657
|
+
return {
|
|
7658
|
+
filePath: filePath || '',
|
|
7659
|
+
oldFilePath: oldFilePath || '',
|
|
7660
|
+
status,
|
|
7661
|
+
summary: getSummaryText({ path: filePath || '', index: '', working_dir: '' }, { filePath: filePath || '', status }),
|
|
7662
|
+
};
|
|
7663
|
+
});
|
|
7664
|
+
return {
|
|
7665
|
+
staged: changes,
|
|
7666
|
+
unstaged: [],
|
|
7667
|
+
untracked: [],
|
|
7668
|
+
};
|
|
7669
|
+
}
|
|
7670
|
+
catch (error) {
|
|
7671
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
7672
|
+
console.error('Error in getDiffForBranch:', error);
|
|
7673
|
+
logger?.log(`Encountered an error getting diff between branches: ${errorMessage}`, { color: 'red' });
|
|
7674
|
+
logger?.log(`Branch details: baseBranch="${baseBranch}", headBranch="${headBranch}"`, { color: 'red' });
|
|
7675
|
+
// Re-throw the error so the caller can handle it appropriately
|
|
7676
|
+
throw error;
|
|
7677
|
+
}
|
|
7678
|
+
}
|
|
7679
|
+
|
|
7680
|
+
const template$3 = `You are a highly skilled software engineer tasked with writing a git changelog. Your response should be informative, well-structured, and in the imperative.
|
|
7486
7681
|
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7682
|
+
## Input
|
|
7683
|
+
You will be provided with a summary of changes. This summary can be one of the following:
|
|
7684
|
+
1. A list of commits, each with its author, hash, message, and body.
|
|
7685
|
+
2. A list of commits, each with its details AND the full diff of the changes.
|
|
7686
|
+
3. A single, comprehensive diff for an entire branch.
|
|
7492
7687
|
|
|
7688
|
+
## Rules
|
|
7689
|
+
- Create a descriptive title for the changelog that gives a high-level overview of the changes.
|
|
7690
|
+
- **BREAKING CHANGES**: Identify any commits that introduce breaking changes. These must be listed first under a "### 💥 BREAKING CHANGES" heading.
|
|
7691
|
+
- **Grouping**: Logically group related changes under descriptive headings (e.g., ### Features, ### Fixes, ### Refactors).
|
|
7692
|
+
- **Dependencies**: Group all dependency updates (e.g., changes to package.json, go.mod) under a "### Dependencies" section.
|
|
7693
|
+
- **Summaries**: For each change, provide a concise summary.
|
|
7694
|
+
- **Attribution**: {{author_instructions}}
|
|
7695
|
+
- **Technical Details**: If provided with diffs, use them to understand the technical details and provide a more accurate and detailed description of the changes.
|
|
7696
|
+
- **Clarity**: Avoid generalizations like "various bug fixes," "improvements," or "enhancements." Be specific.
|
|
7697
|
+
- **Formatting**: Your entire response must be valid Markdown.
|
|
7698
|
+
|
|
7699
|
+
## Formatting Instructions
|
|
7493
7700
|
{{format_instructions}}
|
|
7494
7701
|
|
|
7702
|
+
{{additional_context}}
|
|
7703
|
+
|
|
7495
7704
|
"""{{summary}}"""`;
|
|
7496
|
-
const inputVariables$2 = [
|
|
7705
|
+
const inputVariables$2 = [
|
|
7706
|
+
'format_instructions',
|
|
7707
|
+
'summary',
|
|
7708
|
+
'additional_context',
|
|
7709
|
+
'author_instructions',
|
|
7710
|
+
];
|
|
7497
7711
|
const CHANGELOG_PROMPT = new prompts$1.PromptTemplate({
|
|
7498
7712
|
template: template$3,
|
|
7499
7713
|
inputVariables: inputVariables$2,
|
|
@@ -7517,48 +7731,68 @@ const handler$4 = async (argv, logger) => {
|
|
|
7517
7731
|
}
|
|
7518
7732
|
async function factory() {
|
|
7519
7733
|
const branchName = await getCurrentBranchName({ git });
|
|
7520
|
-
if (
|
|
7521
|
-
logger.verbose(`Generating
|
|
7734
|
+
if (argv.onlyDiff) {
|
|
7735
|
+
logger.verbose(`Generating changelog based on branch diff`, { color: 'yellow' });
|
|
7736
|
+
const diff = await getDiffForBranch({ git, logger, baseBranch: argv.branch || 'main', headBranch: branchName });
|
|
7522
7737
|
return {
|
|
7523
7738
|
branch: branchName,
|
|
7524
|
-
|
|
7739
|
+
diff: JSON.stringify(diff.staged, null, 2),
|
|
7525
7740
|
};
|
|
7526
7741
|
}
|
|
7527
|
-
|
|
7742
|
+
let commits = [];
|
|
7743
|
+
if (config.sinceLastTag) {
|
|
7744
|
+
logger.verbose(`Generating commit log since the last tag`, { color: 'yellow' });
|
|
7745
|
+
// This function returns string[], needs to be adapted or replaced
|
|
7746
|
+
// For now, this path will have limited details.
|
|
7747
|
+
const commitMessages = await getChangesSinceLastTag({ git, logger });
|
|
7748
|
+
commits = commitMessages.map(msg => ({ message: msg }));
|
|
7749
|
+
}
|
|
7750
|
+
else if (config.range && config.range.includes(':')) {
|
|
7528
7751
|
const [from, to] = config.range.split(':');
|
|
7529
7752
|
if (!from || !to) {
|
|
7530
7753
|
logger.log(`Invalid range provided. Expected format is <from>:<to>`, { color: 'red' });
|
|
7531
7754
|
process.exit(1);
|
|
7532
7755
|
}
|
|
7533
|
-
|
|
7534
|
-
branch: branchName,
|
|
7535
|
-
commits: await getCommitLogRange(from, to, { git, noMerges: true }),
|
|
7536
|
-
};
|
|
7756
|
+
commits = await getCommitLogRangeDetails(from, to, { git, noMerges: true });
|
|
7537
7757
|
}
|
|
7538
|
-
if (argv.branch) {
|
|
7758
|
+
else if (argv.branch) {
|
|
7539
7759
|
logger.verbose(`Generating commit log against branch: ${argv.branch}`, { color: 'yellow' });
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7760
|
+
commits = await getCommitLogAgainstBranch({ git, logger, targetBranch: argv.branch });
|
|
7761
|
+
}
|
|
7762
|
+
else {
|
|
7763
|
+
logger.verbose(`No range, branch, or tag option provided. Defaulting to current branch`, {
|
|
7764
|
+
color: 'yellow',
|
|
7765
|
+
});
|
|
7766
|
+
commits = await getCommitLogCurrentBranch({ git, logger });
|
|
7767
|
+
}
|
|
7768
|
+
let commitsWithDiffText = commits;
|
|
7769
|
+
if (argv.withDiff) {
|
|
7770
|
+
commitsWithDiffText = await Promise.all(commits.map(async (commit) => ({
|
|
7771
|
+
...commit,
|
|
7772
|
+
diffText: await getDiffForCommit(commit.hash, { git }),
|
|
7773
|
+
})));
|
|
7544
7774
|
}
|
|
7545
|
-
logger.verbose(`No range, branch, or tag option provided. Defaulting to current branch`, {
|
|
7546
|
-
color: 'yellow',
|
|
7547
|
-
});
|
|
7548
|
-
const commits = await getCommitLogCurrentBranch({ git, logger });
|
|
7549
7775
|
return {
|
|
7550
7776
|
branch: branchName,
|
|
7551
|
-
commits,
|
|
7777
|
+
commits: commitsWithDiffText,
|
|
7778
|
+
withDiff: argv.withDiff,
|
|
7552
7779
|
};
|
|
7553
7780
|
}
|
|
7554
|
-
async function parser(
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
result = `## ${branch}\n\nNo commits found.`;
|
|
7781
|
+
async function parser(data) {
|
|
7782
|
+
if (data.diff) {
|
|
7783
|
+
return `## Diff for ${data.branch}\n\n${data.diff}`;
|
|
7558
7784
|
}
|
|
7559
|
-
|
|
7560
|
-
|
|
7785
|
+
if (!data.commits || data.commits.length === 0) {
|
|
7786
|
+
return `## ${data.branch}\n\nNo commits found.`;
|
|
7561
7787
|
}
|
|
7788
|
+
let result = `## ${data.branch}\n\n`;
|
|
7789
|
+
result += data.commits.map(commit => {
|
|
7790
|
+
let commitStr = `Author: ${commit.author_name}\nCommit: ${commit.hash}\nMessage: ${commit.message}\n${commit.body}`;
|
|
7791
|
+
if (data.withDiff && commit.diffText) {
|
|
7792
|
+
commitStr += `\nDiff:\n${commit.diffText}`;
|
|
7793
|
+
}
|
|
7794
|
+
return commitStr.trim();
|
|
7795
|
+
}).join('\n\n---\n\n');
|
|
7562
7796
|
return result;
|
|
7563
7797
|
}
|
|
7564
7798
|
const changelogMsg = await generateAndReviewLoop({
|
|
@@ -7582,12 +7816,21 @@ const handler$4 = async (argv, logger) => {
|
|
|
7582
7816
|
fallback: CHANGELOG_PROMPT,
|
|
7583
7817
|
});
|
|
7584
7818
|
const formatInstructions = "Only respond with a valid JSON object, containing two fields: 'title' an escaped string, no more than 65 characters, and 'content' also an escaped string.";
|
|
7819
|
+
let additional_context = '';
|
|
7820
|
+
if (argv.additional) {
|
|
7821
|
+
additional_context = `## Additional Context\n${argv.additional}`;
|
|
7822
|
+
}
|
|
7823
|
+
const author_instructions = argv.author
|
|
7824
|
+
? 'At the end of each item, attribute the author and include a reference to the commit hash, like this: `by @author_name (f6dbe61)`. Use the first 7 characters of the hash.'
|
|
7825
|
+
: 'At the end of each item, include a reference to the commit hash, like this: `(f6dbe61)`. Use the first 7 characters of the hash.';
|
|
7585
7826
|
const changelog = await executeChain({
|
|
7586
7827
|
llm,
|
|
7587
7828
|
prompt,
|
|
7588
7829
|
variables: {
|
|
7589
7830
|
summary: context,
|
|
7590
7831
|
format_instructions: formatInstructions,
|
|
7832
|
+
additional_context: additional_context,
|
|
7833
|
+
author_instructions: author_instructions,
|
|
7591
7834
|
},
|
|
7592
7835
|
parser,
|
|
7593
7836
|
});
|
|
@@ -10899,76 +11142,6 @@ async function createCommit(message, git) {
|
|
|
10899
11142
|
return await git.commit(message);
|
|
10900
11143
|
}
|
|
10901
11144
|
|
|
10902
|
-
/**
|
|
10903
|
-
* Determines the status of a file based on its changes in the Git repository.
|
|
10904
|
-
*
|
|
10905
|
-
* @param file - The file to check the status of.
|
|
10906
|
-
* @param location - The location to check the status in ('index' or 'working_dir'). Defaults to 'index'.
|
|
10907
|
-
* @returns The status of the file ('added', 'deleted', 'modified', 'renamed', 'untracked', or 'unknown').
|
|
10908
|
-
* @throws Error if the file type is invalid.
|
|
10909
|
-
*/
|
|
10910
|
-
function getStatus(file, location = 'index') {
|
|
10911
|
-
if ('index' in file && 'working_dir' in file) {
|
|
10912
|
-
const statusCode = file[location];
|
|
10913
|
-
switch (statusCode) {
|
|
10914
|
-
case 'A':
|
|
10915
|
-
return 'added';
|
|
10916
|
-
case 'D':
|
|
10917
|
-
return 'deleted';
|
|
10918
|
-
case 'M':
|
|
10919
|
-
return 'modified';
|
|
10920
|
-
case 'R':
|
|
10921
|
-
return 'renamed';
|
|
10922
|
-
case '?':
|
|
10923
|
-
return 'untracked';
|
|
10924
|
-
default:
|
|
10925
|
-
return 'unknown';
|
|
10926
|
-
}
|
|
10927
|
-
}
|
|
10928
|
-
else if ('changes' in file && 'binary' in file) {
|
|
10929
|
-
if (file.changes === 0)
|
|
10930
|
-
return 'untracked';
|
|
10931
|
-
if (file.file.includes('=>'))
|
|
10932
|
-
return 'renamed';
|
|
10933
|
-
if (file.deletions === 0 && file.insertions > 0)
|
|
10934
|
-
return 'added';
|
|
10935
|
-
if (file.insertions === 0 && file.deletions > 0)
|
|
10936
|
-
return 'deleted';
|
|
10937
|
-
if ((file.insertions > 0 && file.deletions > 0) || file.changes > 0)
|
|
10938
|
-
return 'modified';
|
|
10939
|
-
return 'unknown';
|
|
10940
|
-
}
|
|
10941
|
-
else {
|
|
10942
|
-
throw new Error('Invalid file type');
|
|
10943
|
-
}
|
|
10944
|
-
}
|
|
10945
|
-
|
|
10946
|
-
/**
|
|
10947
|
-
* Returns the summary text for a file change.
|
|
10948
|
-
*
|
|
10949
|
-
* @param file - The file status or diff result.
|
|
10950
|
-
* @param change - The partial file change object.
|
|
10951
|
-
* @returns The summary text for the file change.
|
|
10952
|
-
* @throws Error if the file type is invalid.
|
|
10953
|
-
*/
|
|
10954
|
-
function getSummaryText(file, change) {
|
|
10955
|
-
const status = change.status || getStatus(file);
|
|
10956
|
-
let filePath;
|
|
10957
|
-
if ('path' in file) {
|
|
10958
|
-
filePath = file.path;
|
|
10959
|
-
}
|
|
10960
|
-
else if ('file' in file) {
|
|
10961
|
-
filePath = change?.filePath || file.file;
|
|
10962
|
-
}
|
|
10963
|
-
else {
|
|
10964
|
-
throw new Error('Invalid file type');
|
|
10965
|
-
}
|
|
10966
|
-
if (change.oldFilePath) {
|
|
10967
|
-
return `${status}: ${change.oldFilePath} -> ${filePath}`;
|
|
10968
|
-
}
|
|
10969
|
-
return `${status}: ${filePath}`;
|
|
10970
|
-
}
|
|
10971
|
-
|
|
10972
11145
|
/**
|
|
10973
11146
|
* Retrieves the changes in the Git repository.
|
|
10974
11147
|
*
|
|
@@ -11286,7 +11459,7 @@ const handler$3 = async (argv, logger) => {
|
|
|
11286
11459
|
const schema = USE_CONVENTIONAL_COMMITS
|
|
11287
11460
|
? ConventionalCommitMessageResponseSchema
|
|
11288
11461
|
: CommitMessageResponseSchema;
|
|
11289
|
-
const formatInstructions = `You must always return valid JSON
|
|
11462
|
+
const formatInstructions = `You must always return a valid JSON object. Do not return any additional text. The JSON object you return should match the following schema:
|
|
11290
11463
|
${schema.description}
|
|
11291
11464
|
{
|
|
11292
11465
|
"title": "The commit title",
|
|
@@ -12006,86 +12179,6 @@ const getChangesByTimestamp = async ({ since, git }) => {
|
|
|
12006
12179
|
return formatCommitLog(commitLog);
|
|
12007
12180
|
};
|
|
12008
12181
|
|
|
12009
|
-
/**
|
|
12010
|
-
* Retrieves the diff between the current branch and a specified target branch.
|
|
12011
|
-
*
|
|
12012
|
-
* @param {Object} options - The options for retrieving the diff.
|
|
12013
|
-
* @param {SimpleGit} options.git - The SimpleGit instance.
|
|
12014
|
-
* @param {Logger} options.logger - The logger for logging messages.
|
|
12015
|
-
* @param {string} options.baseBranch - The base branch to compare against.
|
|
12016
|
-
* @param {string} options.headBranch - The head branch to compare.
|
|
12017
|
-
* @param {string[]} options.ignoredFiles - Array of specific files to ignore.
|
|
12018
|
-
* @param {string[]} options.ignoredExtensions - Array of file extensions to ignore.
|
|
12019
|
-
* @returns {Promise<GetChangesResult>} The diff between the current branch and the target branch.
|
|
12020
|
-
*/
|
|
12021
|
-
async function getDiffForBranch({ git, logger, baseBranch, headBranch, options, }) {
|
|
12022
|
-
try {
|
|
12023
|
-
logger?.verbose(`Getting diff for branches: baseBranch="${baseBranch}", headBranch="${headBranch}"`, {
|
|
12024
|
-
color: 'blue',
|
|
12025
|
-
});
|
|
12026
|
-
// Validate branch names
|
|
12027
|
-
if (!baseBranch || !headBranch) {
|
|
12028
|
-
throw new Error(`Invalid branch names: baseBranch="${baseBranch}", headBranch="${headBranch}"`);
|
|
12029
|
-
}
|
|
12030
|
-
const { ignoredFiles = [], ignoredExtensions = [] } = options || {};
|
|
12031
|
-
// Prepare ignore patterns
|
|
12032
|
-
const ignorePatterns = [
|
|
12033
|
-
...ignoredFiles.map((file) => `:!${file}`),
|
|
12034
|
-
...ignoredExtensions.map((ext) => `:!*${ext}`),
|
|
12035
|
-
];
|
|
12036
|
-
// Construct the diff command
|
|
12037
|
-
const diffArgs = [`${baseBranch}..${headBranch}`];
|
|
12038
|
-
if (ignorePatterns.length > 0) {
|
|
12039
|
-
diffArgs.push('--');
|
|
12040
|
-
diffArgs.push(...ignorePatterns);
|
|
12041
|
-
}
|
|
12042
|
-
logger?.verbose(`Running git diff with args: ${diffArgs.join(' ')}`, {
|
|
12043
|
-
color: 'blue',
|
|
12044
|
-
});
|
|
12045
|
-
// Get the diff
|
|
12046
|
-
const diff = await git.diff(diffArgs);
|
|
12047
|
-
logger?.verbose(`Generated diff between "${headBranch}" and "${baseBranch}"`, {
|
|
12048
|
-
color: 'blue',
|
|
12049
|
-
});
|
|
12050
|
-
const changes = diff.split('diff --git').slice(1).map((fileDiff) => {
|
|
12051
|
-
const lines = fileDiff.split('\n');
|
|
12052
|
-
const filePathLine = lines[0];
|
|
12053
|
-
const filePath = filePathLine.split('b/')[1]?.split(' ')[0];
|
|
12054
|
-
const oldFilePath = filePathLine.split('a/')[1]?.split(' ')[0];
|
|
12055
|
-
// Determine status based on diff headers
|
|
12056
|
-
let status = 'modified';
|
|
12057
|
-
if (fileDiff.includes('new file mode')) {
|
|
12058
|
-
status = 'added';
|
|
12059
|
-
}
|
|
12060
|
-
else if (fileDiff.includes('deleted file mode')) {
|
|
12061
|
-
status = 'deleted';
|
|
12062
|
-
}
|
|
12063
|
-
else if (fileDiff.includes('rename from')) {
|
|
12064
|
-
status = 'renamed';
|
|
12065
|
-
}
|
|
12066
|
-
return {
|
|
12067
|
-
filePath: filePath || '',
|
|
12068
|
-
oldFilePath: oldFilePath || '',
|
|
12069
|
-
status,
|
|
12070
|
-
summary: getSummaryText({ path: filePath || '', index: '', working_dir: '' }, { filePath: filePath || '', status }),
|
|
12071
|
-
};
|
|
12072
|
-
});
|
|
12073
|
-
return {
|
|
12074
|
-
staged: changes,
|
|
12075
|
-
unstaged: [],
|
|
12076
|
-
untracked: [],
|
|
12077
|
-
};
|
|
12078
|
-
}
|
|
12079
|
-
catch (error) {
|
|
12080
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
12081
|
-
console.error('Error in getDiffForBranch:', error);
|
|
12082
|
-
logger?.log(`Encountered an error getting diff between branches: ${errorMessage}`, { color: 'red' });
|
|
12083
|
-
logger?.log(`Branch details: baseBranch="${baseBranch}", headBranch="${headBranch}"`, { color: 'red' });
|
|
12084
|
-
// Re-throw the error so the caller can handle it appropriately
|
|
12085
|
-
throw error;
|
|
12086
|
-
}
|
|
12087
|
-
}
|
|
12088
|
-
|
|
12089
12182
|
async function noResult$1({ logger }) {
|
|
12090
12183
|
logger.log('No repo changes detected. 👀', { color: 'blue' });
|
|
12091
12184
|
throw new Error('NO_CHANGES_DETECTED');
|