Skip to content

Commit 348fa33

Browse files
authored
Merge pull request mermaid-js#3115 from mermaid-js/mermaid-js#3060-Support-cherry-commit-in-Gitgraph
mermaid-js#3060 support cherry commit in gitgraph
2 parents 7a4acb5 + 84ae430 commit 348fa33

File tree

5 files changed

+213
-4
lines changed

5 files changed

+213
-4
lines changed

cypress/integration/rendering/gitGraph.spec.js

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,11 @@ describe('Git Graph diagram', () => {
126126
branch branch8
127127
branch branch9
128128
checkout branch1
129-
commit
129+
commit id: "1"
130130
`,
131131
{}
132132
);
133133
});
134-
135134
it('9: should render a simple gitgraph with rotated labels', () => {
136135
imgSnapshotTest(
137136
`%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'gitGraph': {
@@ -160,4 +159,52 @@ describe('Git Graph diagram', () => {
160159
{}
161160
);
162161
});
162+
it('11: should render a simple gitgraph with cherry pick commit', () => {
163+
imgSnapshotTest(
164+
`
165+
gitGraph
166+
commit id: "ZERO"
167+
branch develop
168+
commit id:"A"
169+
checkout main
170+
commit id:"ONE"
171+
checkout develop
172+
commit id:"B"
173+
checkout main
174+
commit id:"TWO"
175+
cherry-pick id:"A"
176+
commit id:"THREE"
177+
checkout develop
178+
commit id:"C"
179+
`,
180+
{}
181+
);
182+
});
183+
184+
it('11: should render a simple gitgraph with two cherry pick commit', () => {
185+
imgSnapshotTest(
186+
`
187+
gitGraph
188+
commit id: "ZERO"
189+
branch develop
190+
commit id:"A"
191+
checkout main
192+
commit id:"ONE"
193+
checkout develop
194+
commit id:"B"
195+
branch featureA
196+
commit id:"FIX"
197+
commit id: "FIX-2"
198+
checkout main
199+
commit id:"TWO"
200+
cherry-pick id:"A"
201+
commit id:"THREE"
202+
cherry-pick id:"FIX"
203+
checkout develop
204+
commit id:"C"
205+
merge featureA
206+
`,
207+
{}
208+
);
209+
});
163210
});

docs/gitgraph.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,37 @@ After this we made use of the `checkout` keyword to set the current branch as `m
182182
After this we merge the `develop` branch onto the current branch `main`, resulting in a merge commit.
183183
Since the current branch at this point is still `main`, the last two commits are registered against that.
184184

185+
### Cherry Pick commit from another branch
186+
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.
187+
188+
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:
189+
190+
`cherry-pick id: "your_custom_id"`
191+
192+
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.
193+
194+
Few Important rules to note here are:
195+
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.
196+
2. The given commit must not exist on the current branch. Cherry picked commit must always be a different branch than the current branch.
197+
3. Current branch must have atleast one commit, before you can cherry pick a commit, otherwise it will case an error is throw.
198+
199+
Let see an example:
200+
```mermaid-example
201+
gitGraph
202+
commit id: "ZERO"
203+
branch develop
204+
commit id:"A"
205+
checkout main
206+
commit id:"ONE"
207+
checkout develop
208+
commit id:"B"
209+
checkout main
210+
commit id:"TWO"
211+
cherry-pick id:"A"
212+
commit id:"THREE"
213+
checkout develop
214+
commit id:"C"
215+
```
185216
## Gitgraph specific configuration options
186217
In Mermaid, you have the option to configure the gitgraph diagram. You can configure the following options:
187218
- `showBranches` : Boolean, default is `true`. If set to `false`, the branches are not shown in the diagram.

src/diagrams/git/gitGraphAst.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,85 @@ export const merge = function (otherBranch, tag) {
235235
log.debug('in mergeBranch');
236236
};
237237

238+
export const cherryPick = function (sourceId, targetId) {
239+
sourceId = common.sanitizeText(sourceId, configApi.getConfig());
240+
targetId = common.sanitizeText(targetId, configApi.getConfig());
241+
242+
if (!sourceId || typeof commits[sourceId] === 'undefined') {
243+
let error = new Error(
244+
'Incorrect usage of "cherryPick". Source commit id should exist and provided'
245+
);
246+
error.hash = {
247+
text: 'cherryPick ' + sourceId + ' ' + targetId,
248+
token: 'cherryPick ' + sourceId + ' ' + targetId,
249+
line: '1',
250+
loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 },
251+
expected: ['cherry-pick abc'],
252+
};
253+
throw error;
254+
}
255+
256+
let sourceCommit = commits[sourceId];
257+
let sourceCommitBranch = sourceCommit.branch;
258+
if (sourceCommit.type === commitType.MERGE) {
259+
let error = new Error(
260+
'Incorrect usage of "cherryPick". Source commit should not be a merge commit'
261+
);
262+
error.hash = {
263+
text: 'cherryPick ' + sourceId + ' ' + targetId,
264+
token: 'cherryPick ' + sourceId + ' ' + targetId,
265+
line: '1',
266+
loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 },
267+
expected: ['cherry-pick abc'],
268+
};
269+
throw error;
270+
}
271+
if (!targetId || typeof commits[targetId] === 'undefined') {
272+
// cherry-pick source commit to current branch
273+
274+
if (sourceCommitBranch === curBranch) {
275+
let error = new Error(
276+
'Incorrect usage of "cherryPick". Source commit is already on current branch'
277+
);
278+
error.hash = {
279+
text: 'cherryPick ' + sourceId + ' ' + targetId,
280+
token: 'cherryPick ' + sourceId + ' ' + targetId,
281+
line: '1',
282+
loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 },
283+
expected: ['cherry-pick abc'],
284+
};
285+
throw error;
286+
}
287+
const currentCommit = commits[branches[curBranch]];
288+
if (typeof currentCommit === 'undefined' || !currentCommit) {
289+
let error = new Error(
290+
'Incorrect usage of "cherry-pick". Current branch (' + curBranch + ')has no commits'
291+
);
292+
error.hash = {
293+
text: 'cherryPick ' + sourceId + ' ' + targetId,
294+
token: 'cherryPick ' + sourceId + ' ' + targetId,
295+
line: '1',
296+
loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 },
297+
expected: ['cherry-pick abc'],
298+
};
299+
throw error;
300+
}
301+
const commit = {
302+
id: seq + '-' + getId(),
303+
message: 'cherry-picked ' + sourceCommit + ' into ' + curBranch,
304+
seq: seq++,
305+
parents: [head == null ? null : head.id, sourceCommit.id],
306+
branch: curBranch,
307+
type: commitType.CHERRY_PICK,
308+
tag: 'cherry-pick:' + sourceCommit.id,
309+
};
310+
head = commit;
311+
commits[commit.id] = commit;
312+
branches[curBranch] = commit.id;
313+
log.debug(branches);
314+
log.debug('in cheeryPick');
315+
}
316+
};
238317
export const checkout = function (branch) {
239318
branch = common.sanitizeText(branch, configApi.getConfig());
240319
if (typeof branches[branch] === 'undefined') {
@@ -390,6 +469,7 @@ export const commitType = {
390469
REVERSE: 1,
391470
HIGHLIGHT: 2,
392471
MERGE: 3,
472+
CHERRY_PICK: 4,
393473
};
394474

395475
export default {
@@ -401,6 +481,7 @@ export default {
401481
commit,
402482
branch,
403483
merge,
484+
cherryPick,
404485
checkout,
405486
//reset,
406487
prettyPrint,

src/diagrams/git/gitGraphRenderer.js

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const commitType = {
1414
REVERSE: 1,
1515
HIGHLIGHT: 2,
1616
MERGE: 3,
17+
CHERRY_PICK: 4,
1718
};
1819

1920
let branchPos = {};
@@ -103,6 +104,9 @@ const drawCommits = (svg, commits, modifyGraph) => {
103104
case commitType.MERGE:
104105
typeClass = 'commit-merge';
105106
break;
107+
case commitType.CHERRY_PICK:
108+
typeClass = 'commit-cherry-pick';
109+
break;
106110
default:
107111
typeClass = 'commit-normal';
108112
}
@@ -139,6 +143,43 @@ const drawCommits = (svg, commits, modifyGraph) => {
139143
typeClass +
140144
'-inner'
141145
);
146+
} else if (commit.type === commitType.CHERRY_PICK) {
147+
gBullets
148+
.append('circle')
149+
.attr('cx', x)
150+
.attr('cy', y)
151+
.attr('r', 10)
152+
.attr('class', 'commit ' + commit.id + ' ' + typeClass);
153+
gBullets
154+
.append('circle')
155+
.attr('cx', x - 3)
156+
.attr('cy', y + 2)
157+
.attr('r', 2.75)
158+
.attr('fill', '#fff')
159+
.attr('class', 'commit ' + commit.id + ' ' + typeClass);
160+
gBullets
161+
.append('circle')
162+
.attr('cx', x + 3)
163+
.attr('cy', y + 2)
164+
.attr('r', 2.75)
165+
.attr('fill', '#fff')
166+
.attr('class', 'commit ' + commit.id + ' ' + typeClass);
167+
gBullets
168+
.append('line')
169+
.attr('x1', x + 3)
170+
.attr('y1', y + 1)
171+
.attr('x2', x)
172+
.attr('y2', y - 5)
173+
.attr('stroke', '#fff')
174+
.attr('class', 'commit ' + commit.id + ' ' + typeClass);
175+
gBullets
176+
.append('line')
177+
.attr('x1', x - 3)
178+
.attr('y1', y + 1)
179+
.attr('x2', x)
180+
.attr('y2', y - 5)
181+
.attr('stroke', '#fff')
182+
.attr('class', 'commit ' + commit.id + ' ' + typeClass);
142183
} else {
143184
const circle = gBullets.append('circle');
144185
circle.attr('cx', x);
@@ -175,7 +216,11 @@ const drawCommits = (svg, commits, modifyGraph) => {
175216
const px = 4;
176217
const py = 2;
177218
// Draw the commit label
178-
if (commit.type !== commitType.MERGE && gitGraphConfig.showCommitLabel) {
219+
if (
220+
commit.type !== commitType.CHERRY_PICK &&
221+
commit.type !== commitType.MERGE &&
222+
gitGraphConfig.showCommitLabel
223+
) {
179224
const wrapper = gLabels.append('g');
180225
const labelBkg = wrapper.insert('rect').attr('class', 'commit-label-bkg');
181226

@@ -197,7 +242,6 @@ const drawCommits = (svg, commits, modifyGraph) => {
197242
if (gitGraphConfig.rotateCommitLabel) {
198243
let r_x = -7.5 - ((bbox.width + 10) / 25) * 9.5;
199244
let r_y = 10 + (bbox.width / 25) * 8.5;
200-
//wrapper.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + bbox.width / 2 + ') ');
201245
wrapper.attr(
202246
'transform',
203247
'translate(' + r_x + ', ' + r_y + ') rotate(' + -45 + ', ' + pos + ', ' + y + ')'

src/diagrams/git/parser/gitGraph.jison

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ accDescr\s*"{"\s* { this.begin("ac
4848
"branch" return 'BRANCH';
4949
"order:" return 'ORDER';
5050
"merge" return 'MERGE';
51+
"cherry-pick" return 'CHERRY_PICK';
5152
// "reset" return 'RESET';
5253
"checkout" return 'CHECKOUT';
5354
"LR" return 'DIR';
@@ -102,6 +103,7 @@ line
102103
statement
103104
: commitStatement
104105
| mergeStatement
106+
| cherryPickStatement
105107
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
106108
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
107109
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); } | section {yy.addSection($1.substr(8));$$=$1.substr(8);}
@@ -114,6 +116,10 @@ branchStatement
114116
| BRANCH ID ORDER NUM {yy.branch($2, $4)}
115117
;
116118

119+
cherryPickStatement
120+
: CHERRY_PICK COMMIT_ID STR {yy.cherryPick($3)}
121+
;
122+
117123
mergeStatement
118124
: MERGE ID {yy.merge($2)}
119125
| MERGE ID COMMIT_TAG STR {yy.merge($2, $4)}

0 commit comments

Comments
 (0)