mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-30 13:46:43 +02:00
Merge pull request #4944 from RounakJoshi09/bug/#4497_unable-to-cherrypick-merge-commit
Bug/#4497 Unable to Cherry Pick Merge Commit Solved
This commit is contained in:
@@ -811,4 +811,19 @@ gitGraph TB:
|
|||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
it('40: should render a simple gitgraph with cherry pick merge commit', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`gitGraph
|
||||||
|
commit id: "ZERO"
|
||||||
|
branch feature
|
||||||
|
branch release
|
||||||
|
checkout feature
|
||||||
|
commit id: "A"
|
||||||
|
commit id: "B"
|
||||||
|
checkout main
|
||||||
|
merge feature id: "M"
|
||||||
|
checkout release
|
||||||
|
cherry-pick id: "M" parent:"B"`
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -366,6 +366,8 @@ A few important rules to note here are:
|
|||||||
1. You need to provide the `id` for an existing commit to be cherry-picked. If given commit id does not exist it will result in an error. For this, make use of the `commit id:$value` format of declaring commits. See the examples from above.
|
1. You need to provide the `id` for an existing commit to be cherry-picked. If given commit id does not exist it will result in an error. For this, make use of the `commit id:$value` format of declaring commits. See the examples from above.
|
||||||
2. The given commit must not exist on the current branch. The cherry-picked commit must always be a different branch than the current branch.
|
2. The given commit must not exist on the current branch. The cherry-picked commit must always be a different branch than the current branch.
|
||||||
3. Current branch must have at least one commit, before you can cherry-pick, otherwise it will cause an error is throw.
|
3. Current branch must have at least one commit, before you can cherry-pick, otherwise it will cause an error is throw.
|
||||||
|
4. When cherry-picking a merge commit, providing a parent commit ID is mandatory. If the parent attribute is omitted or an invalid parent commit ID is provided, an error will be thrown.
|
||||||
|
5. The specified parent commit must be an immediate parent of the merge commit being cherry-picked.
|
||||||
|
|
||||||
Let see an example:
|
Let see an example:
|
||||||
|
|
||||||
@@ -373,14 +375,17 @@ Let see an example:
|
|||||||
gitGraph
|
gitGraph
|
||||||
commit id: "ZERO"
|
commit id: "ZERO"
|
||||||
branch develop
|
branch develop
|
||||||
|
branch release
|
||||||
commit id:"A"
|
commit id:"A"
|
||||||
checkout main
|
checkout main
|
||||||
commit id:"ONE"
|
commit id:"ONE"
|
||||||
checkout develop
|
checkout develop
|
||||||
commit id:"B"
|
commit id:"B"
|
||||||
checkout main
|
checkout main
|
||||||
|
merge develop id:"MERGE"
|
||||||
commit id:"TWO"
|
commit id:"TWO"
|
||||||
cherry-pick id:"A"
|
checkout release
|
||||||
|
cherry-pick id:"MERGE" parent:"B"
|
||||||
commit id:"THREE"
|
commit id:"THREE"
|
||||||
checkout develop
|
checkout develop
|
||||||
commit id:"C"
|
commit id:"C"
|
||||||
@@ -390,14 +395,17 @@ Let see an example:
|
|||||||
gitGraph
|
gitGraph
|
||||||
commit id: "ZERO"
|
commit id: "ZERO"
|
||||||
branch develop
|
branch develop
|
||||||
|
branch release
|
||||||
commit id:"A"
|
commit id:"A"
|
||||||
checkout main
|
checkout main
|
||||||
commit id:"ONE"
|
commit id:"ONE"
|
||||||
checkout develop
|
checkout develop
|
||||||
commit id:"B"
|
commit id:"B"
|
||||||
checkout main
|
checkout main
|
||||||
|
merge develop id:"MERGE"
|
||||||
commit id:"TWO"
|
commit id:"TWO"
|
||||||
cherry-pick id:"A"
|
checkout release
|
||||||
|
cherry-pick id:"MERGE" parent:"B"
|
||||||
commit id:"THREE"
|
commit id:"THREE"
|
||||||
checkout develop
|
checkout develop
|
||||||
commit id:"C"
|
commit id:"C"
|
||||||
|
@@ -255,11 +255,12 @@ export const merge = function (otherBranch, custom_id, override_type, custom_tag
|
|||||||
log.debug('in mergeBranch');
|
log.debug('in mergeBranch');
|
||||||
};
|
};
|
||||||
|
|
||||||
export const cherryPick = function (sourceId, targetId, tag) {
|
export const cherryPick = function (sourceId, targetId, tag, parentCommitId) {
|
||||||
log.debug('Entering cherryPick:', sourceId, targetId, tag);
|
log.debug('Entering cherryPick:', sourceId, targetId, tag);
|
||||||
sourceId = common.sanitizeText(sourceId, getConfig());
|
sourceId = common.sanitizeText(sourceId, getConfig());
|
||||||
targetId = common.sanitizeText(targetId, getConfig());
|
targetId = common.sanitizeText(targetId, getConfig());
|
||||||
tag = common.sanitizeText(tag, getConfig());
|
tag = common.sanitizeText(tag, getConfig());
|
||||||
|
parentCommitId = common.sanitizeText(parentCommitId, getConfig());
|
||||||
|
|
||||||
if (!sourceId || commits[sourceId] === undefined) {
|
if (!sourceId || commits[sourceId] === undefined) {
|
||||||
let error = new Error(
|
let error = new Error(
|
||||||
@@ -274,20 +275,21 @@ export const cherryPick = function (sourceId, targetId, tag) {
|
|||||||
};
|
};
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
let sourceCommit = commits[sourceId];
|
let sourceCommit = commits[sourceId];
|
||||||
let sourceCommitBranch = sourceCommit.branch;
|
let sourceCommitBranch = sourceCommit.branch;
|
||||||
if (sourceCommit.type === commitType.MERGE) {
|
if (
|
||||||
|
parentCommitId &&
|
||||||
|
!(Array.isArray(sourceCommit.parents) && sourceCommit.parents.includes(parentCommitId))
|
||||||
|
) {
|
||||||
let error = new Error(
|
let error = new Error(
|
||||||
'Incorrect usage of "cherryPick". Source commit should not be a merge commit'
|
'Invalid operation: The specified parent commit is not an immediate parent of the cherry-picked commit.'
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
if (sourceCommit.type === commitType.MERGE && !parentCommitId) {
|
||||||
|
let error = new Error(
|
||||||
|
'Incorrect usage of cherry-pick: If the source commit is a merge commit, an immediate parent commit must be specified.'
|
||||||
);
|
);
|
||||||
error.hash = {
|
|
||||||
text: 'cherryPick ' + sourceId + ' ' + targetId,
|
|
||||||
token: 'cherryPick ' + sourceId + ' ' + targetId,
|
|
||||||
line: '1',
|
|
||||||
loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 },
|
|
||||||
expected: ['cherry-pick abc'],
|
|
||||||
};
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
if (!targetId || commits[targetId] === undefined) {
|
if (!targetId || commits[targetId] === undefined) {
|
||||||
@@ -327,7 +329,11 @@ export const cherryPick = function (sourceId, targetId, tag) {
|
|||||||
parents: [head == null ? null : head.id, sourceCommit.id],
|
parents: [head == null ? null : head.id, sourceCommit.id],
|
||||||
branch: curBranch,
|
branch: curBranch,
|
||||||
type: commitType.CHERRY_PICK,
|
type: commitType.CHERRY_PICK,
|
||||||
tag: tag ?? 'cherry-pick:' + sourceCommit.id,
|
tag:
|
||||||
|
tag ??
|
||||||
|
`cherry-pick:${sourceCommit.id}${
|
||||||
|
sourceCommit.type === commitType.MERGE ? `|parent:${parentCommitId}` : ''
|
||||||
|
}`,
|
||||||
};
|
};
|
||||||
head = commit;
|
head = commit;
|
||||||
commits[commit.id] = commit;
|
commits[commit.id] = commit;
|
||||||
|
@@ -673,6 +673,145 @@ describe('when parsing a gitGraph', function () {
|
|||||||
expect(commits[cherryPickCommitID].branch).toBe('main');
|
expect(commits[cherryPickCommitID].branch).toBe('main');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support cherry-picking of merge commits', function () {
|
||||||
|
const str = `gitGraph
|
||||||
|
commit id: "ZERO"
|
||||||
|
branch feature
|
||||||
|
branch release
|
||||||
|
checkout feature
|
||||||
|
commit id: "A"
|
||||||
|
commit id: "B"
|
||||||
|
checkout main
|
||||||
|
merge feature id: "M"
|
||||||
|
checkout release
|
||||||
|
cherry-pick id: "M" parent:"B"
|
||||||
|
`;
|
||||||
|
|
||||||
|
parser.parse(str);
|
||||||
|
const commits = parser.yy.getCommits();
|
||||||
|
const cherryPickCommitID = Object.keys(commits)[4];
|
||||||
|
expect(commits[cherryPickCommitID].tag).toBe('cherry-pick:M|parent:B');
|
||||||
|
expect(commits[cherryPickCommitID].branch).toBe('release');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support cherry-picking of merge commits with tag', function () {
|
||||||
|
const str = `gitGraph
|
||||||
|
commit id: "ZERO"
|
||||||
|
branch feature
|
||||||
|
branch release
|
||||||
|
checkout feature
|
||||||
|
commit id: "A"
|
||||||
|
commit id: "B"
|
||||||
|
checkout main
|
||||||
|
merge feature id: "M"
|
||||||
|
checkout release
|
||||||
|
cherry-pick id: "M" parent:"ZERO" tag: "v1.0"
|
||||||
|
`;
|
||||||
|
|
||||||
|
parser.parse(str);
|
||||||
|
const commits = parser.yy.getCommits();
|
||||||
|
const cherryPickCommitID = Object.keys(commits)[4];
|
||||||
|
expect(commits[cherryPickCommitID].tag).toBe('v1.0');
|
||||||
|
expect(commits[cherryPickCommitID].branch).toBe('release');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support cherry-picking of merge commits with additional commit', function () {
|
||||||
|
const str = `gitGraph
|
||||||
|
commit id: "ZERO"
|
||||||
|
branch feature
|
||||||
|
branch release
|
||||||
|
checkout feature
|
||||||
|
commit id: "A"
|
||||||
|
commit id: "B"
|
||||||
|
checkout main
|
||||||
|
merge feature id: "M"
|
||||||
|
checkout release
|
||||||
|
commit id: "C"
|
||||||
|
cherry-pick id: "M" tag: "v2.1:ZERO" parent:"ZERO"
|
||||||
|
commit id: "D"
|
||||||
|
`;
|
||||||
|
|
||||||
|
parser.parse(str);
|
||||||
|
const commits = parser.yy.getCommits();
|
||||||
|
const cherryPickCommitID = Object.keys(commits)[5];
|
||||||
|
expect(commits[cherryPickCommitID].tag).toBe('v2.1:ZERO');
|
||||||
|
expect(commits[cherryPickCommitID].branch).toBe('release');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support cherry-picking of merge commits with empty tag', function () {
|
||||||
|
const str = `gitGraph
|
||||||
|
commit id: "ZERO"
|
||||||
|
branch feature
|
||||||
|
branch release
|
||||||
|
checkout feature
|
||||||
|
commit id: "A"
|
||||||
|
commit id: "B"
|
||||||
|
checkout main
|
||||||
|
merge feature id: "M"
|
||||||
|
checkout release
|
||||||
|
commit id: "C"
|
||||||
|
cherry-pick id:"M" parent: "ZERO" tag:""
|
||||||
|
commit id: "D"
|
||||||
|
cherry-pick id:"M" tag:"" parent: "B"
|
||||||
|
`;
|
||||||
|
|
||||||
|
parser.parse(str);
|
||||||
|
const commits = parser.yy.getCommits();
|
||||||
|
const cherryPickCommitID = Object.keys(commits)[5];
|
||||||
|
const cherryPickCommitID2 = Object.keys(commits)[7];
|
||||||
|
expect(commits[cherryPickCommitID].tag).toBe('');
|
||||||
|
expect(commits[cherryPickCommitID2].tag).toBe('');
|
||||||
|
expect(commits[cherryPickCommitID].branch).toBe('release');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail cherry-picking of merge commits if the parent of merge commits is not specified', function () {
|
||||||
|
expect(() =>
|
||||||
|
parser
|
||||||
|
.parse(
|
||||||
|
`gitGraph
|
||||||
|
commit id: "ZERO"
|
||||||
|
branch feature
|
||||||
|
branch release
|
||||||
|
checkout feature
|
||||||
|
commit id: "A"
|
||||||
|
commit id: "B"
|
||||||
|
checkout main
|
||||||
|
merge feature id: "M"
|
||||||
|
checkout release
|
||||||
|
commit id: "C"
|
||||||
|
cherry-pick id:"M"
|
||||||
|
`
|
||||||
|
)
|
||||||
|
.toThrow(
|
||||||
|
'Incorrect usage of cherry-pick: If the source commit is a merge commit, an immediate parent commit must be specified.'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail cherry-picking of merge commits when the parent provided is not an immediate parent of cherry picked commit', function () {
|
||||||
|
expect(() =>
|
||||||
|
parser
|
||||||
|
.parse(
|
||||||
|
`gitGraph
|
||||||
|
commit id: "ZERO"
|
||||||
|
branch feature
|
||||||
|
branch release
|
||||||
|
checkout feature
|
||||||
|
commit id: "A"
|
||||||
|
commit id: "B"
|
||||||
|
checkout main
|
||||||
|
merge feature id: "M"
|
||||||
|
checkout release
|
||||||
|
commit id: "C"
|
||||||
|
cherry-pick id:"M" parent: "A"
|
||||||
|
`
|
||||||
|
)
|
||||||
|
.toThrow(
|
||||||
|
'Invalid operation: The specified parent commit is not an immediate parent of the cherry-picked commit.'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should throw error when try to branch existing branch: main', function () {
|
it('should throw error when try to branch existing branch: main', function () {
|
||||||
const str = `gitGraph
|
const str = `gitGraph
|
||||||
commit
|
commit
|
||||||
|
@@ -39,6 +39,7 @@ branch(?=\s|$) return 'BRANCH';
|
|||||||
"order:" return 'ORDER';
|
"order:" return 'ORDER';
|
||||||
merge(?=\s|$) return 'MERGE';
|
merge(?=\s|$) return 'MERGE';
|
||||||
cherry\-pick(?=\s|$) return 'CHERRY_PICK';
|
cherry\-pick(?=\s|$) return 'CHERRY_PICK';
|
||||||
|
"parent:" return 'PARENT_COMMIT'
|
||||||
// "reset" return 'RESET';
|
// "reset" return 'RESET';
|
||||||
checkout(?=\s|$) return 'CHECKOUT';
|
checkout(?=\s|$) return 'CHECKOUT';
|
||||||
"LR" return 'DIR';
|
"LR" return 'DIR';
|
||||||
@@ -109,10 +110,17 @@ branchStatement
|
|||||||
|
|
||||||
cherryPickStatement
|
cherryPickStatement
|
||||||
: CHERRY_PICK COMMIT_ID STR {yy.cherryPick($3, '', undefined)}
|
: CHERRY_PICK COMMIT_ID STR {yy.cherryPick($3, '', undefined)}
|
||||||
|
| CHERRY_PICK COMMIT_ID STR PARENT_COMMIT STR {yy.cherryPick($3, '', undefined,$5)}
|
||||||
| CHERRY_PICK COMMIT_ID STR COMMIT_TAG STR {yy.cherryPick($3, '', $5)}
|
| CHERRY_PICK COMMIT_ID STR COMMIT_TAG STR {yy.cherryPick($3, '', $5)}
|
||||||
| CHERRY_PICK COMMIT_ID STR COMMIT_TAG EMPTYSTR {yy.cherryPick($3, '', '')}
|
| CHERRY_PICK COMMIT_ID STR PARENT_COMMIT STR COMMIT_TAG STR {yy.cherryPick($3, '', $7,$5)}
|
||||||
|
| CHERRY_PICK COMMIT_ID STR COMMIT_TAG STR PARENT_COMMIT STR {yy.cherryPick($3, '', $5,$7)}
|
||||||
| CHERRY_PICK COMMIT_TAG STR COMMIT_ID STR {yy.cherryPick($5, '', $3)}
|
| CHERRY_PICK COMMIT_TAG STR COMMIT_ID STR {yy.cherryPick($5, '', $3)}
|
||||||
| CHERRY_PICK COMMIT_TAG EMPTYSTR COMMIT_ID STR {yy.cherryPick($3, '', '')}
|
| CHERRY_PICK COMMIT_TAG EMPTYSTR COMMIT_ID STR {yy.cherryPick($5, '', '')}
|
||||||
|
| CHERRY_PICK COMMIT_ID STR COMMIT_TAG EMPTYSTR {yy.cherryPick($3, '', '')}
|
||||||
|
| CHERRY_PICK COMMIT_ID STR PARENT_COMMIT STR COMMIT_TAG EMPTYSTR {yy.cherryPick($3, '', '',$5)}
|
||||||
|
| CHERRY_PICK COMMIT_ID STR COMMIT_TAG EMPTYSTR PARENT_COMMIT STR {yy.cherryPick($3, '', '',$7)}
|
||||||
|
| CHERRY_PICK COMMIT_TAG STR COMMIT_ID STR PARENT_COMMIT STR {yy.cherryPick($5, '', $3,$7)}
|
||||||
|
| CHERRY_PICK COMMIT_TAG EMPTYSTR COMMIT_ID STR PARENT_COMMIT STR{yy.cherryPick($5, '', '',$7)}
|
||||||
;
|
;
|
||||||
|
|
||||||
mergeStatement
|
mergeStatement
|
||||||
|
@@ -244,6 +244,8 @@ A few important rules to note here are:
|
|||||||
1. You need to provide the `id` for an existing commit to be cherry-picked. If given commit id does not exist it will result in an error. For this, make use of the `commit id:$value` format of declaring commits. See the examples from above.
|
1. You need to provide the `id` for an existing commit to be cherry-picked. If given commit id does not exist it will result in an error. For this, make use of the `commit id:$value` format of declaring commits. See the examples from above.
|
||||||
2. The given commit must not exist on the current branch. The cherry-picked commit must always be a different branch than the current branch.
|
2. The given commit must not exist on the current branch. The cherry-picked commit must always be a different branch than the current branch.
|
||||||
3. Current branch must have at least one commit, before you can cherry-pick, otherwise it will cause an error is throw.
|
3. Current branch must have at least one commit, before you can cherry-pick, otherwise it will cause an error is throw.
|
||||||
|
4. When cherry-picking a merge commit, providing a parent commit ID is mandatory. If the parent attribute is omitted or an invalid parent commit ID is provided, an error will be thrown.
|
||||||
|
5. The specified parent commit must be an immediate parent of the merge commit being cherry-picked.
|
||||||
|
|
||||||
Let see an example:
|
Let see an example:
|
||||||
|
|
||||||
@@ -251,14 +253,17 @@ Let see an example:
|
|||||||
gitGraph
|
gitGraph
|
||||||
commit id: "ZERO"
|
commit id: "ZERO"
|
||||||
branch develop
|
branch develop
|
||||||
|
branch release
|
||||||
commit id:"A"
|
commit id:"A"
|
||||||
checkout main
|
checkout main
|
||||||
commit id:"ONE"
|
commit id:"ONE"
|
||||||
checkout develop
|
checkout develop
|
||||||
commit id:"B"
|
commit id:"B"
|
||||||
checkout main
|
checkout main
|
||||||
|
merge develop id:"MERGE"
|
||||||
commit id:"TWO"
|
commit id:"TWO"
|
||||||
cherry-pick id:"A"
|
checkout release
|
||||||
|
cherry-pick id:"MERGE" parent:"B"
|
||||||
commit id:"THREE"
|
commit id:"THREE"
|
||||||
checkout develop
|
checkout develop
|
||||||
commit id:"C"
|
commit id:"C"
|
||||||
|
Reference in New Issue
Block a user