From 243f2b28cde4209aefa2399a7f035876a9fb549f Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Thu, 17 Mar 2022 19:04:37 +0100 Subject: [PATCH 1/6] fix for gitGraph (:) and spaces (new line) issue --- src/diagrams/git/parser/gitGraph.jison | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/diagrams/git/parser/gitGraph.jison b/src/diagrams/git/parser/gitGraph.jison index 1a7d24901..0b63dd2ba 100644 --- a/src/diagrams/git/parser/gitGraph.jison +++ b/src/diagrams/git/parser/gitGraph.jison @@ -52,7 +52,8 @@ %% /* language grammar */ start - : GG ':' document EOF{ return $3; } + : GG document EOF{ return $3; } + | GG ':' document EOF{ return $3; } | GG DIR ':' document EOF {yy.setDirection($2); return $4;} ; @@ -71,7 +72,7 @@ body | body line {$1.push($2); $$=$1;} ; line - : statement NL{$$ =$1} + : statement {$$ =$1} | NL ; From 01970d2bbdf4da7ace30fecbec0ab330e252fd75 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Thu, 17 Mar 2022 19:05:19 +0100 Subject: [PATCH 2/6] fix broken rendering for math error --- src/diagrams/git/gitGraphRenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/diagrams/git/gitGraphRenderer.js b/src/diagrams/git/gitGraphRenderer.js index 83d4d8b0a..953f150d2 100644 --- a/src/diagrams/git/gitGraphRenderer.js +++ b/src/diagrams/git/gitGraphRenderer.js @@ -240,7 +240,7 @@ const findLane = (y1, y2, _depth) => { lanes.push(candidate); return candidate; } - const diff = math.abs(y1 - y2); + const diff = Math.abs(y1 - y2); return findLane(y1, y2-(diff/5), depth); } From c1dead11873eba1e81ca7234df032fcbb5d19e2c Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Thu, 17 Mar 2022 19:06:31 +0100 Subject: [PATCH 3/6] Added branch, merge and checkout error handling scenarios --- src/diagrams/git/gitGraphAst.js | 102 +++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 9 deletions(-) diff --git a/src/diagrams/git/gitGraphAst.js b/src/diagrams/git/gitGraphAst.js index d449768b8..7938432e9 100644 --- a/src/diagrams/git/gitGraphAst.js +++ b/src/diagrams/git/gitGraphAst.js @@ -2,8 +2,8 @@ import { log } from '../../logger'; import { random } from '../../utils'; let commits = {}; let head = null; -let branches = { master: head }; -let curBranch = 'master'; +let branches = { main: head }; +let curBranch = 'main'; let direction = 'LR'; let seq = 0; @@ -101,13 +101,85 @@ export const commit = function (msg, id, type, tag) { export const branch = function (name) { if (!branches[name]) { branches[name] = head != null ? head.id : null; + checkout(name); log.debug('in createBranch'); + } else { + let error = new Error( + 'Trying to create an existing branch. (Help: Either use a new name if you want create a new branch or try using "checkout ' + + name + + '")' + ); + error.hash = { + text: 'branch ' + name, + token: 'branch ' + name, + line: '1', + loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, + expected: ['"checkout ' + name + '"'], + }; + throw error; } }; export const merge = function (otherBranch) { const currentCommit = commits[branches[curBranch]]; const otherCommit = commits[branches[otherBranch]]; + if (curBranch === otherBranch) { + let error = new Error('Incorrect usage of "merge". Cannot merge a branch to itself'); + error.hash = { + text: 'merge ' + otherBranch, + token: 'merge ' + otherBranch, + line: '1', + loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, + expected: ['branch abc'], + }; + throw error; + } else if (!currentCommit) { + let error = new Error( + 'Incorrect usage of "merge". Current branch (' + curBranch + ')has no commits' + ); + error.hash = { + text: 'merge ' + otherBranch, + token: 'merge ' + otherBranch, + line: '1', + loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, + expected: ['commit'], + }; + throw error; + } else if (!branches[otherBranch]) { + let error = new Error( + 'Incorrect usage of "merge". Branch to be merged (' + otherBranch + ') does not exist' + ); + error.hash = { + text: 'merge ' + otherBranch, + token: 'merge ' + otherBranch, + line: '1', + loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, + expected: ['branch ' + otherBranch], + }; + throw error; + } else if (!otherCommit) { + let error = new Error( + 'Incorrect usage of "merge". Branch to be merged (' + otherBranch + ') has no commits' + ); + error.hash = { + text: 'merge ' + otherBranch, + token: 'merge ' + otherBranch, + line: '1', + loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, + expected: ['"commit"'], + }; + throw error; + } else if (currentCommit === otherCommit) { + let error = new Error('Incorrect usage of "merge". Both branches have same head'); + error.hash = { + text: 'merge ' + otherBranch, + token: 'merge ' + otherBranch, + line: '1', + loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, + expected: ['branch abc'], + }; + throw error; + } // if (isReachableFrom(currentCommit, otherCommit)) { // log.debug('Already merged'); // return; @@ -136,12 +208,24 @@ export const merge = function (otherBranch) { export const checkout = function (branch) { log.debug('in checkout'); if (!branches[branch]) { - branches[branch] = head != null ? head.id : null; - log.debug('in createBranch'); + let error = new Error( + 'Trying to checkout branch which is not yet created. (Help try using "branch ' + branch + '")' + ); + error.hash = { + text: 'checkout ' + branch, + token: 'checkout ' + branch, + line: '1', + loc: { first_line: 1, last_line: 1, first_column: 1, last_column: 1 }, + expected: ['"branch ' + branch + '"'], + }; + throw error; + //branches[branch] = head != null ? head.id : null; + //log.debug('in createBranch'); + } else { + curBranch = branch; + const id = branches[curBranch]; + head = commits[id]; } - curBranch = branch; - const id = branches[curBranch]; - head = commits[id]; }; // export const reset = function (commitRef) { @@ -219,8 +303,8 @@ export const prettyPrint = function () { export const clear = function () { commits = {}; head = null; - branches = { master: head }; - curBranch = 'master'; + branches = { main: head }; + curBranch = 'main'; seq = 0; }; From 1ef58e286cf545a927af0ac858d5bfaf1535e131 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Thu, 17 Mar 2022 19:07:16 +0100 Subject: [PATCH 4/6] Fixed rendering test cases --- cypress/integration/rendering/gitGraph.spec.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cypress/integration/rendering/gitGraph.spec.js b/cypress/integration/rendering/gitGraph.spec.js index 0de0c58ac..8202babda 100644 --- a/cypress/integration/rendering/gitGraph.spec.js +++ b/cypress/integration/rendering/gitGraph.spec.js @@ -3,7 +3,7 @@ import { imgSnapshotTest } from '../../helpers/util.js'; describe('Git Graph diagram', () => { it('1: should render a simple gitgraph with commit on master branch', () => { imgSnapshotTest( - `gitgraph + `gitGraph commit commit commit @@ -13,7 +13,7 @@ describe('Git Graph diagram', () => { }); it('2: should render a simple gitgraph with commit on master branch with Id', () => { imgSnapshotTest( - `gitgraph + `gitGraph commit id: "One" commit id: "Two" commit id: "Three" @@ -23,7 +23,7 @@ describe('Git Graph diagram', () => { }); it('3: should render a simple gitgraph with different commitTypes on master branch ', () => { imgSnapshotTest( - `gitgraph + `gitGraph commit id: "Normal Commit" commit id: "Reverse Commit" commitType: REVERSE commit id: "Hightlight Commit" commitType: HIGHLIGHT @@ -33,7 +33,7 @@ describe('Git Graph diagram', () => { }); it('4: should render a simple gitgraph with tags commitTypes on master branch ', () => { imgSnapshotTest( - `gitgraph + `gitGraph commit id: "Normal Commit with tag" teg: "v1.0.0" commit id: "Reverse Commit with tag" commitType: REVERSE tag: "RC_1" commit id: "Hightlight Commit" commitType: HIGHLIGHT tag: "8.8.4" @@ -43,7 +43,7 @@ describe('Git Graph diagram', () => { }); it('5: should render a simple gitgraph with two branches', () => { imgSnapshotTest( - `gitgraph + `gitGraph commit commit branch develop @@ -59,7 +59,7 @@ describe('Git Graph diagram', () => { }); it('6: should render a simple gitgraph with two branches and merge commit', () => { imgSnapshotTest( - `gitgraph + `gitGraph commit commit branch develop @@ -76,7 +76,7 @@ describe('Git Graph diagram', () => { }); it('7: should render a simple gitgraph with three branches and merge commit', () => { imgSnapshotTest( - `gitgraph + `gitGraph commit commit branch nice_feature From 501e62894b3e1b87efa4262d0994be2f98cebcee Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Thu, 17 Mar 2022 19:07:57 +0100 Subject: [PATCH 5/6] Added gitGraph jest test cases --- src/diagrams/git/gitGraphParserV2.spec.js | 237 +++++++++++++++++++--- 1 file changed, 214 insertions(+), 23 deletions(-) diff --git a/src/diagrams/git/gitGraphParserV2.spec.js b/src/diagrams/git/gitGraphParserV2.spec.js index ee5ae02dd..7abded235 100644 --- a/src/diagrams/git/gitGraphParserV2.spec.js +++ b/src/diagrams/git/gitGraphParserV2.spec.js @@ -8,18 +8,17 @@ import { logger } from '../../logger'; //jest.mock('crypto-random-string'); -describe('when parsing a gitGraph', function() { +describe('when parsing a gitGraph', function () { let randomNumber; - beforeEach(function() { + beforeEach(function () { parser.yy = gitGraphAst; parser.yy.clear(); randomNumber = 0; - }); // afterEach(function() { // cryptoRandomString.mockReset(); // }); - it('should handle a gitGraph commit with NO pararms, get auto-genrated reandom ID', function() { + it('should handle a gitGraph commit with NO pararms, get auto-genrated reandom ID', function () { const str = `gitGraph: commit `; @@ -37,7 +36,7 @@ describe('when parsing a gitGraph', function() { expect(commits[key].type).toBe(0); }); - it('should handle a gitGraph commit with custom commit id only', function() { + it('should handle a gitGraph commit with custom commit id only', function () { const str = `gitGraph: commit id:"1111" `; @@ -55,11 +54,11 @@ describe('when parsing a gitGraph', function() { expect(commits[key].type).toBe(0); }); - it('should handle a gitGraph commit with custom commit tag only', function() { + it('should handle a gitGraph commit with custom commit tag only', function () { const str = `gitGraph: commit tag:"test" `; - + parser.parse(str); const commits = parser.yy.getCommits(); expect(Object.keys(commits).length).toBe(1); @@ -73,11 +72,11 @@ describe('when parsing a gitGraph', function() { expect(commits[key].type).toBe(0); }); - it('should handle a gitGraph commit with custom commit type HIGHLIGHT only', function() { + it('should handle a gitGraph commit with custom commit type HIGHLIGHT only', function () { const str = `gitGraph: commit type: HIGHLIGHT `; - + parser.parse(str); const commits = parser.yy.getCommits(); expect(Object.keys(commits).length).toBe(1); @@ -91,11 +90,11 @@ describe('when parsing a gitGraph', function() { expect(commits[key].type).toBe(2); }); - it('should handle a gitGraph commit with custom commit type REVERSE only', function() { + it('should handle a gitGraph commit with custom commit type REVERSE only', function () { const str = `gitGraph: commit type: REVERSE `; - + parser.parse(str); const commits = parser.yy.getCommits(); expect(Object.keys(commits).length).toBe(1); @@ -109,11 +108,11 @@ describe('when parsing a gitGraph', function() { expect(commits[key].type).toBe(1); }); - it('should handle a gitGraph commit with custom commit type NORMAL only', function() { + it('should handle a gitGraph commit with custom commit type NORMAL only', function () { const str = `gitGraph: commit type: NORMAL `; - + parser.parse(str); const commits = parser.yy.getCommits(); expect(Object.keys(commits).length).toBe(1); @@ -127,11 +126,11 @@ describe('when parsing a gitGraph', function() { expect(commits[key].type).toBe(0); }); - it('should handle a gitGraph commit with custom commit msg only', function() { + it('should handle a gitGraph commit with custom commit msg only', function () { const str = `gitGraph: commit "test commit" `; - + parser.parse(str); const commits = parser.yy.getCommits(); expect(Object.keys(commits).length).toBe(1); @@ -145,11 +144,29 @@ describe('when parsing a gitGraph', function() { expect(commits[key].type).toBe(0); }); - it('should handle a gitGraph commit with custom commit id, tag only', function() { + it('should handle a gitGraph commit with custom commit "msg:" key only', function () { + const str = `gitGraph: + commit msg: "test commit" + `; + + parser.parse(str); + const commits = parser.yy.getCommits(); + expect(Object.keys(commits).length).toBe(1); + expect(parser.yy.getCurrentBranch()).toBe('master'); + expect(parser.yy.getDirection()).toBe('LR'); + expect(Object.keys(parser.yy.getBranches()).length).toBe(1); + const key = Object.keys(commits)[0]; + expect(commits[key].message).toBe('test commit'); + expect(commits[key].id).not.toBeNull(); + expect(commits[key].tag).toBe(''); + expect(commits[key].type).toBe(0); + }); + + it('should handle a gitGraph commit with custom commit id, tag only', function () { const str = `gitGraph: commit id:"1111" tag: "test tag" `; - + parser.parse(str); const commits = parser.yy.getCommits(); expect(Object.keys(commits).length).toBe(1); @@ -163,11 +180,11 @@ describe('when parsing a gitGraph', function() { expect(commits[key].type).toBe(0); }); - it('should handle a gitGraph commit with custom commit type, tag only', function() { + it('should handle a gitGraph commit with custom commit type, tag only', function () { const str = `gitGraph: commit type:HIGHLIGHT tag: "test tag" `; - + parser.parse(str); const commits = parser.yy.getCommits(); expect(Object.keys(commits).length).toBe(1); @@ -181,11 +198,11 @@ describe('when parsing a gitGraph', function() { expect(commits[key].type).toBe(2); }); - it('should handle a gitGraph commit with custom commit tag and type only', function() { + it('should handle a gitGraph commit with custom commit tag and type only', function () { const str = `gitGraph: commit tag: "test tag" type:HIGHLIGHT `; - + parser.parse(str); const commits = parser.yy.getCommits(); expect(Object.keys(commits).length).toBe(1); @@ -199,11 +216,11 @@ describe('when parsing a gitGraph', function() { expect(commits[key].type).toBe(2); }); - it('should handle a gitGraph commit with custom commit id, type and tag only', function() { + it('should handle a gitGraph commit with custom commit id, type and tag only', function () { const str = `gitGraph: commit id:"1111" type:REVERSE tag: "test tag" `; - + parser.parse(str); const commits = parser.yy.getCommits(); expect(Object.keys(commits).length).toBe(1); @@ -217,6 +234,180 @@ describe('when parsing a gitGraph', function() { expect(commits[key].type).toBe(1); }); + it('should handle a gitGraph commit with custom commit id, type, tag and msg', function () { + const str = `gitGraph: + commit id:"1111" type:REVERSE tag: "test tag" msg:"test msg" + `; + parser.parse(str); + const commits = parser.yy.getCommits(); + expect(Object.keys(commits).length).toBe(1); + expect(parser.yy.getCurrentBranch()).toBe('master'); + expect(parser.yy.getDirection()).toBe('LR'); + expect(Object.keys(parser.yy.getBranches()).length).toBe(1); + const key = Object.keys(commits)[0]; + expect(commits[key].message).toBe('test msg'); + expect(commits[key].id).toBe('1111'); + expect(commits[key].tag).toBe('test tag'); + expect(commits[key].type).toBe(1); + }); + it('should handle a gitGraph commit with custom type,tag, msg, commit id,', function () { + const str = `gitGraph: + commit type:REVERSE tag: "test tag" msg: "test msg" id: "1111" + + `; + + parser.parse(str); + const commits = parser.yy.getCommits(); + expect(Object.keys(commits).length).toBe(1); + expect(parser.yy.getCurrentBranch()).toBe('master'); + expect(parser.yy.getDirection()).toBe('LR'); + expect(Object.keys(parser.yy.getBranches()).length).toBe(1); + const key = Object.keys(commits)[0]; + expect(commits[key].message).toBe('test msg'); + expect(commits[key].id).toBe('1111'); + expect(commits[key].tag).toBe('test tag'); + expect(commits[key].type).toBe(1); + }); + + it('should handle a gitGraph commit with custom tag, msg, commit id, type,', function () { + const str = `gitGraph: + commit tag: "test tag" msg:"test msg" id:"1111" type:REVERSE + `; + + parser.parse(str); + const commits = parser.yy.getCommits(); + expect(Object.keys(commits).length).toBe(1); + expect(parser.yy.getCurrentBranch()).toBe('master'); + expect(parser.yy.getDirection()).toBe('LR'); + expect(Object.keys(parser.yy.getBranches()).length).toBe(1); + const key = Object.keys(commits)[0]; + expect(commits[key].message).toBe('test msg'); + expect(commits[key].id).toBe('1111'); + expect(commits[key].tag).toBe('test tag'); + expect(commits[key].type).toBe(1); + }); + + it('should handle a gitGraph commit with custom msg, commit id, type,tag', function () { + const str = `gitGraph: + commit msg:"test msg" id:"1111" type:REVERSE tag: "test tag" + `; + + parser.parse(str); + const commits = parser.yy.getCommits(); + expect(Object.keys(commits).length).toBe(1); + expect(parser.yy.getCurrentBranch()).toBe('master'); + expect(parser.yy.getDirection()).toBe('LR'); + expect(Object.keys(parser.yy.getBranches()).length).toBe(1); + const key = Object.keys(commits)[0]; + expect(commits[key].message).toBe('test msg'); + expect(commits[key].id).toBe('1111'); + expect(commits[key].tag).toBe('test tag'); + expect(commits[key].type).toBe(1); + }); + + it('should handle 3 straight commits', function () { + const str = `gitGraph: + commit + commit + commit + `; + + parser.parse(str); + const commits = parser.yy.getCommits(); + expect(Object.keys(commits).length).toBe(3); + expect(parser.yy.getCurrentBranch()).toBe('master'); + expect(parser.yy.getDirection()).toBe('LR'); + expect(Object.keys(parser.yy.getBranches()).length).toBe(1); + }); + + it('should handle new branch creation', function () { + const str = `gitGraph: + commit + branch testBranch + `; + + parser.parse(str); + const commits = parser.yy.getCommits(); + expect(Object.keys(commits).length).toBe(1); + expect(parser.yy.getCurrentBranch()).toBe('master'); + expect(parser.yy.getDirection()).toBe('LR'); + expect(Object.keys(parser.yy.getBranches()).length).toBe(2); + }); + + it('should handle new branch checkout', function () { + const str = `gitGraph: + commit + branch testBranch + checkout testBranch + `; + + parser.parse(str); + const commits = parser.yy.getCommits(); + expect(Object.keys(commits).length).toBe(1); + expect(parser.yy.getCurrentBranch()).toBe('testBranch'); + expect(parser.yy.getDirection()).toBe('LR'); + expect(Object.keys(parser.yy.getBranches()).length).toBe(2); + }); + + it('should handle new branch checkout & commit', function () { + const str = `gitGraph: + commit + branch testBranch + checkout testBranch + commit + `; + + parser.parse(str); + const commits = parser.yy.getCommits(); + expect(Object.keys(commits).length).toBe(2); + expect(parser.yy.getCurrentBranch()).toBe('testBranch'); + expect(parser.yy.getDirection()).toBe('LR'); + expect(Object.keys(parser.yy.getBranches()).length).toBe(2); + const commit1 = Object.keys(commits)[0]; + const commit2 = Object.keys(commits)[1]; + expect(commits[commit1].branch).toBe('master'); + expect(commits[commit1].parents).toStrictEqual([]); + expect(commits[commit2].branch).toBe('testBranch'); + expect(commits[commit2].parents).toStrictEqual([commit1]); + }); + + it('should handle new branch checkout & commit and merge', function () { + const str = `gitGraph: + commit + branch testBranch + checkout testBranch + commit + commit + checkout master + merge testBranch + `; + + parser.parse(str); + const commits = parser.yy.getCommits(); + expect(Object.keys(commits).length).toBe(4); + expect(parser.yy.getCurrentBranch()).toBe('master'); + expect(parser.yy.getDirection()).toBe('LR'); + expect(Object.keys(parser.yy.getBranches()).length).toBe(2); + const commit1 = Object.keys(commits)[0]; + const commit2 = Object.keys(commits)[1]; + const commit3 = Object.keys(commits)[2]; + const commit4 = Object.keys(commits)[3]; + expect(commits[commit1].branch).toBe('master'); + console.log(commits); + + console.log(commits[commit1].parents); + expect(commits[commit1].parents).toStrictEqual([]); + expect(commits[commit2].branch).toBe('testBranch'); + expect(commits[commit2].parents).toStrictEqual([commits[commit1].id]); + expect(commits[commit3].branch).toBe('testBranch'); + expect(commits[commit3].parents).toStrictEqual([commits[commit2].id]); + expect(commits[commit4].branch).toBe('master'); + expect(commits[commit4].parents).toStrictEqual([commits[commit1].id, commits[commit3].id]); + expect(parser.yy.getBranchesAsObjArray()).toStrictEqual([ + { name: 'master' }, + { name: 'testBranch' }, + ]); + }); }); From a7c33b7d5ad92c694e255023720f9d7061890675 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Thu, 17 Mar 2022 19:19:25 +0100 Subject: [PATCH 6/6] Replacing master to main in test cases --- .../integration/rendering/gitGraph.spec.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/cypress/integration/rendering/gitGraph.spec.js b/cypress/integration/rendering/gitGraph.spec.js index 8202babda..9040a90fd 100644 --- a/cypress/integration/rendering/gitGraph.spec.js +++ b/cypress/integration/rendering/gitGraph.spec.js @@ -1,7 +1,7 @@ import { imgSnapshotTest } from '../../helpers/util.js'; describe('Git Graph diagram', () => { - it('1: should render a simple gitgraph with commit on master branch', () => { + it('1: should render a simple gitgraph with commit on main branch', () => { imgSnapshotTest( `gitGraph commit @@ -11,7 +11,7 @@ describe('Git Graph diagram', () => { {} ); }); - it('2: should render a simple gitgraph with commit on master branch with Id', () => { + it('2: should render a simple gitgraph with commit on main branch with Id', () => { imgSnapshotTest( `gitGraph commit id: "One" @@ -21,7 +21,7 @@ describe('Git Graph diagram', () => { {} ); }); - it('3: should render a simple gitgraph with different commitTypes on master branch ', () => { + it('3: should render a simple gitgraph with different commitTypes on main branch ', () => { imgSnapshotTest( `gitGraph commit id: "Normal Commit" @@ -31,7 +31,7 @@ describe('Git Graph diagram', () => { {} ); }); - it('4: should render a simple gitgraph with tags commitTypes on master branch ', () => { + it('4: should render a simple gitgraph with tags commitTypes on main branch ', () => { imgSnapshotTest( `gitGraph commit id: "Normal Commit with tag" teg: "v1.0.0" @@ -50,7 +50,7 @@ describe('Git Graph diagram', () => { checkout develop commit commit - checkout master + checkout main commit commit `, @@ -66,7 +66,7 @@ describe('Git Graph diagram', () => { checkout develop commit commit - checkout master + checkout main merge develop commit commit @@ -82,21 +82,21 @@ describe('Git Graph diagram', () => { branch nice_feature checkout nice_feature commit - checkout master + checkout main commit checkout nice_feature branch very_nice_feature checkout very_nice_feature commit - checkout master + checkout main commit checkout nice_feature commit - checkout master + checkout main merge nice_feature checkout very_nice_feature commit - checkout master + checkout main commit `, {}