Skip to content

Commit

Permalink
Merge pull request #3115 from mermaid-js/#3060-Support-cherry-commit-…
Browse files Browse the repository at this point in the history
…in-Gitgraph

#3060 support cherry commit in gitgraph
  • Loading branch information
knsv committed Jun 7, 2022
2 parents 7a4acb5 + 84ae430 commit 348fa33
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 4 deletions.
51 changes: 49 additions & 2 deletions cypress/integration/rendering/gitGraph.spec.js
Expand Up @@ -126,12 +126,11 @@ describe('Git Graph diagram', () => {
branch branch8
branch branch9
checkout branch1
commit
commit id: "1"
`,
{}
);
});

it('9: should render a simple gitgraph with rotated labels', () => {
imgSnapshotTest(
`%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'gitGraph': {
Expand Down Expand Up @@ -160,4 +159,52 @@ describe('Git Graph diagram', () => {
{}
);
});
it('11: should render a simple gitgraph with cherry pick commit', () => {
imgSnapshotTest(
`
gitGraph
commit id: "ZERO"
branch develop
commit id:"A"
checkout main
commit id:"ONE"
checkout develop
commit id:"B"
checkout main
commit id:"TWO"
cherry-pick id:"A"
commit id:"THREE"
checkout develop
commit id:"C"
`,
{}
);
});

it('11: should render a simple gitgraph with two cherry pick commit', () => {
imgSnapshotTest(
`
gitGraph
commit id: "ZERO"
branch develop
commit id:"A"
checkout main
commit id:"ONE"
checkout develop
commit id:"B"
branch featureA
commit id:"FIX"
commit id: "FIX-2"
checkout main
commit id:"TWO"
cherry-pick id:"A"
commit id:"THREE"
cherry-pick id:"FIX"
checkout develop
commit id:"C"
merge featureA
`,
{}
);
});
});
31 changes: 31 additions & 0 deletions docs/gitgraph.md
Expand Up @@ -182,6 +182,37 @@ After this we made use of the `checkout` keyword to set the current branch as `m
After this we merge the `develop` branch onto the current branch `main`, resulting in a merge commit.
Since the current branch at this point is still `main`, the last two commits are registered against that.

### Cherry Pick commit from another branch
Similar to how 'git' allows you to cherry pick a commit from **another branch** onto the **current** branch, Mermaid also suports this functionality. You can also cherry pick a commit from another branch using the `cherry-pick` keyword.

To use the `cherry-pick` keyword, you must specify the id using the `id` attribute, followed by `:` and your desired commit id within `""` quote. For example:

`cherry-pick id: "your_custom_id"`

Here, a new commt representing the cherry pick is created on the current branch, and is visually highlighted in the diagram with a **cherry** and a tag depicting the commit id from which it is cherry picked from.

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.
2. The given commit must not exist on the current branch. Cherry picked commit must always be a different branch than the current branch.
3. Current branch must have atleast one commit, before you can cherry pick a commit, otherwise it will case an error is throw.

Let see an example:
```mermaid-example
gitGraph
commit id: "ZERO"
branch develop
commit id:"A"
checkout main
commit id:"ONE"
checkout develop
commit id:"B"
checkout main
commit id:"TWO"
cherry-pick id:"A"
commit id:"THREE"
checkout develop
commit id:"C"
```
## Gitgraph specific configuration options
In Mermaid, you have the option to configure the gitgraph diagram. You can configure the following options:
- `showBranches` : Boolean, default is `true`. If set to `false`, the branches are not shown in the diagram.
Expand Down
81 changes: 81 additions & 0 deletions src/diagrams/git/gitGraphAst.js
Expand Up @@ -235,6 +235,85 @@ export const merge = function (otherBranch, tag) {
log.debug('in mergeBranch');
};

export const cherryPick = function (sourceId, targetId) {
sourceId = common.sanitizeText(sourceId, configApi.getConfig());
targetId = common.sanitizeText(targetId, configApi.getConfig());

if (!sourceId || typeof commits[sourceId] === 'undefined') {
let error = new Error(
'Incorrect usage of "cherryPick". Source commit id should exist and provided'
);
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;
}

let sourceCommit = commits[sourceId];
let sourceCommitBranch = sourceCommit.branch;
if (sourceCommit.type === commitType.MERGE) {
let error = new Error(
'Incorrect usage of "cherryPick". Source commit should not be a merge commit'
);
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;
}
if (!targetId || typeof commits[targetId] === 'undefined') {
// cherry-pick source commit to current branch

if (sourceCommitBranch === curBranch) {
let error = new Error(
'Incorrect usage of "cherryPick". Source commit is already on current branch'
);
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;
}
const currentCommit = commits[branches[curBranch]];
if (typeof currentCommit === 'undefined' || !currentCommit) {
let error = new Error(
'Incorrect usage of "cherry-pick". Current branch (' + curBranch + ')has no commits'
);
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;
}
const commit = {
id: seq + '-' + getId(),
message: 'cherry-picked ' + sourceCommit + ' into ' + curBranch,
seq: seq++,
parents: [head == null ? null : head.id, sourceCommit.id],
branch: curBranch,
type: commitType.CHERRY_PICK,
tag: 'cherry-pick:' + sourceCommit.id,
};
head = commit;
commits[commit.id] = commit;
branches[curBranch] = commit.id;
log.debug(branches);
log.debug('in cheeryPick');
}
};
export const checkout = function (branch) {
branch = common.sanitizeText(branch, configApi.getConfig());
if (typeof branches[branch] === 'undefined') {
Expand Down Expand Up @@ -390,6 +469,7 @@ export const commitType = {
REVERSE: 1,
HIGHLIGHT: 2,
MERGE: 3,
CHERRY_PICK: 4,
};

export default {
Expand All @@ -401,6 +481,7 @@ export default {
commit,
branch,
merge,
cherryPick,
checkout,
//reset,
prettyPrint,
Expand Down
48 changes: 46 additions & 2 deletions src/diagrams/git/gitGraphRenderer.js
Expand Up @@ -14,6 +14,7 @@ const commitType = {
REVERSE: 1,
HIGHLIGHT: 2,
MERGE: 3,
CHERRY_PICK: 4,
};

let branchPos = {};
Expand Down Expand Up @@ -103,6 +104,9 @@ const drawCommits = (svg, commits, modifyGraph) => {
case commitType.MERGE:
typeClass = 'commit-merge';
break;
case commitType.CHERRY_PICK:
typeClass = 'commit-cherry-pick';
break;
default:
typeClass = 'commit-normal';
}
Expand Down Expand Up @@ -139,6 +143,43 @@ const drawCommits = (svg, commits, modifyGraph) => {
typeClass +
'-inner'
);
} else if (commit.type === commitType.CHERRY_PICK) {
gBullets
.append('circle')
.attr('cx', x)
.attr('cy', y)
.attr('r', 10)
.attr('class', 'commit ' + commit.id + ' ' + typeClass);
gBullets
.append('circle')
.attr('cx', x - 3)
.attr('cy', y + 2)
.attr('r', 2.75)
.attr('fill', '#fff')
.attr('class', 'commit ' + commit.id + ' ' + typeClass);
gBullets
.append('circle')
.attr('cx', x + 3)
.attr('cy', y + 2)
.attr('r', 2.75)
.attr('fill', '#fff')
.attr('class', 'commit ' + commit.id + ' ' + typeClass);
gBullets
.append('line')
.attr('x1', x + 3)
.attr('y1', y + 1)
.attr('x2', x)
.attr('y2', y - 5)
.attr('stroke', '#fff')
.attr('class', 'commit ' + commit.id + ' ' + typeClass);
gBullets
.append('line')
.attr('x1', x - 3)
.attr('y1', y + 1)
.attr('x2', x)
.attr('y2', y - 5)
.attr('stroke', '#fff')
.attr('class', 'commit ' + commit.id + ' ' + typeClass);
} else {
const circle = gBullets.append('circle');
circle.attr('cx', x);
Expand Down Expand Up @@ -175,7 +216,11 @@ const drawCommits = (svg, commits, modifyGraph) => {
const px = 4;
const py = 2;
// Draw the commit label
if (commit.type !== commitType.MERGE && gitGraphConfig.showCommitLabel) {
if (
commit.type !== commitType.CHERRY_PICK &&
commit.type !== commitType.MERGE &&
gitGraphConfig.showCommitLabel
) {
const wrapper = gLabels.append('g');
const labelBkg = wrapper.insert('rect').attr('class', 'commit-label-bkg');

Expand All @@ -197,7 +242,6 @@ const drawCommits = (svg, commits, modifyGraph) => {
if (gitGraphConfig.rotateCommitLabel) {
let r_x = -7.5 - ((bbox.width + 10) / 25) * 9.5;
let r_y = 10 + (bbox.width / 25) * 8.5;
//wrapper.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + bbox.width / 2 + ') ');
wrapper.attr(
'transform',
'translate(' + r_x + ', ' + r_y + ') rotate(' + -45 + ', ' + pos + ', ' + y + ')'
Expand Down
6 changes: 6 additions & 0 deletions src/diagrams/git/parser/gitGraph.jison
Expand Up @@ -48,6 +48,7 @@ accDescr\s*"{"\s* { this.begin("ac
"branch" return 'BRANCH';
"order:" return 'ORDER';
"merge" return 'MERGE';
"cherry-pick" return 'CHERRY_PICK';
// "reset" return 'RESET';
"checkout" return 'CHECKOUT';
"LR" return 'DIR';
Expand Down Expand Up @@ -102,6 +103,7 @@ line
statement
: commitStatement
| mergeStatement
| cherryPickStatement
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); } | section {yy.addSection($1.substr(8));$$=$1.substr(8);}
Expand All @@ -114,6 +116,10 @@ branchStatement
| BRANCH ID ORDER NUM {yy.branch($2, $4)}
;

cherryPickStatement
: CHERRY_PICK COMMIT_ID STR {yy.cherryPick($3)}
;

mergeStatement
: MERGE ID {yy.merge($2)}
| MERGE ID COMMIT_TAG STR {yy.merge($2, $4)}
Expand Down

0 comments on commit 348fa33

Please sign in to comment.