Skip to content

Commit b8b7497

Browse files
ARBhosaleaniket.bhosale
authored andcommitted
feat: (merge --allow-unrelated-histories) adding parameter to merge unrelated branches (#2121)
* feat(merge--allow-unrelated-histories): adding parameter to merge branch with unrelated histories * adding file and commit checks post merge --------- Co-authored-by: aniket.bhosale <[email protected]>
1 parent eeb8271 commit b8b7497

File tree

11 files changed

+73
-29
lines changed

11 files changed

+73
-29
lines changed

docs/en/merge.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
<tr><td>signingKey</td><td>string</td><td>passed to <a href="/docs/en/commit">commit</a> when creating a merge commit</td></tr>
8686
<tr><td>cache</td><td>object</td><td>a <a href="/docs/en/cache">cache</a> object</td></tr>
8787
<tr><td>mergeDriver</td><td>MergeDriverCallback</td><td>a <a href="/docs/en/mergeDriver">merge driver</a> implementation</td></tr>
88+
<tr><td>allowUnrelatedHistories</td><td>boolean = false</td><td>If true, allows merging histories of two branches that started their lives independently.</td></tr>
8889
<tr><td>return</td><td>Promise&lt;MergeResult&gt;</td><td>Resolves to a description of the merge operation</td></tr>
8990
</tbody>
9091
</table>

docs/en/merge/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
<tr><td>signingKey</td><td>string</td><td>passed to <a href="/docs/en/commit">commit</a> when creating a merge commit</td></tr>
8686
<tr><td>cache</td><td>object</td><td>a <a href="/docs/en/cache">cache</a> object</td></tr>
8787
<tr><td>mergeDriver</td><td>MergeDriverCallback</td><td>a <a href="/docs/en/mergeDriver">merge driver</a> implementation</td></tr>
88+
<tr><td>allowUnrelatedHistories</td><td>boolean = false</td><td>If true, allows merging histories of two branches that started their lives independently.</td></tr>
8889
<tr><td>return</td><td>Promise&lt;MergeResult&gt;</td><td>Resolves to a description of the merge operation</td></tr>
8990
</tbody>
9091
</table>

docs/en/next/merge.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
<tr><td>signingKey</td><td>string</td><td>passed to <a href="/docs/en/next/commit">commit</a> when creating a merge commit</td></tr>
8686
<tr><td>cache</td><td>object</td><td>a <a href="/docs/en/next/cache">cache</a> object</td></tr>
8787
<tr><td>mergeDriver</td><td>MergeDriverCallback</td><td>a <a href="/docs/en/next/mergeDriver">merge driver</a> implementation</td></tr>
88+
<tr><td>allowUnrelatedHistories</td><td>boolean = false</td><td>If true, allows merging histories of two branches that started their lives independently.</td></tr>
8889
<tr><td>return</td><td>Promise&lt;MergeResult&gt;</td><td>Resolves to a description of the merge operation</td></tr>
8990
</tbody>
9091
</table>

docs/en/next/merge/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
<tr><td>signingKey</td><td>string</td><td>passed to <a href="/docs/en/next/commit">commit</a> when creating a merge commit</td></tr>
8686
<tr><td>cache</td><td>object</td><td>a <a href="/docs/en/next/cache">cache</a> object</td></tr>
8787
<tr><td>mergeDriver</td><td>MergeDriverCallback</td><td>a <a href="/docs/en/next/mergeDriver">merge driver</a> implementation</td></tr>
88+
<tr><td>allowUnrelatedHistories</td><td>boolean = false</td><td>If true, allows merging histories of two branches that started their lives independently.</td></tr>
8889
<tr><td>return</td><td>Promise&lt;MergeResult&gt;</td><td>Resolves to a description of the merge operation</td></tr>
8990
</tbody>
9091
</table>

js/isomorphic-git/index.cjs

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9440,10 +9440,8 @@ async function mergeTree({
94409440
// Modifications
94419441
if (
94429442
ours &&
9443-
base &&
94449443
theirs &&
94459444
(await ours.type()) === 'blob' &&
9446-
(await base.type()) === 'blob' &&
94479445
(await theirs.type()) === 'blob'
94489446
) {
94499447
return mergeBlobs({
@@ -9462,13 +9460,18 @@ async function mergeTree({
94629460
unmergedFiles.push(filepath);
94639461
bothModified.push(filepath);
94649462
if (!abortOnConflict) {
9465-
const baseOid = await base.oid();
9463+
let baseOid = '';
9464+
if (base && (await base.type()) === 'blob') {
9465+
baseOid = await base.oid();
9466+
}
94669467
const ourOid = await ours.oid();
94679468
const theirOid = await theirs.oid();
94689469

94699470
index.delete({ filepath });
94709471

9471-
index.insert({ filepath, oid: baseOid, stage: 1 });
9472+
if (baseOid) {
9473+
index.insert({ filepath, oid: baseOid, stage: 1 });
9474+
}
94729475
index.insert({ filepath, oid: ourOid, stage: 2 });
94739476
index.insert({ filepath, oid: theirOid, stage: 3 });
94749477
}
@@ -9655,10 +9658,16 @@ async function mergeBlobs({
96559658
const type = 'blob';
96569659
// Compute the new mode.
96579660
// Since there are ONLY two valid blob modes ('100755' and '100644') it boils down to this
9661+
let baseMode = '100755';
9662+
let baseOid = '';
9663+
let baseContent = '';
9664+
if (base && (await base.type()) === 'blob') {
9665+
baseMode = await base.mode();
9666+
baseOid = await base.oid();
9667+
baseContent = Buffer.from(await base.content()).toString('utf8');
9668+
}
96589669
const mode =
9659-
(await base.mode()) === (await ours.mode())
9660-
? await theirs.mode()
9661-
: await ours.mode();
9670+
baseMode === (await ours.mode()) ? await theirs.mode() : await ours.mode();
96629671
// The trivial case: nothing to merge except maybe mode
96639672
if ((await ours.oid()) === (await theirs.oid())) {
96649673
return {
@@ -9667,21 +9676,20 @@ async function mergeBlobs({
96679676
}
96689677
}
96699678
// if only one side made oid changes, return that side's oid
9670-
if ((await ours.oid()) === (await base.oid())) {
9679+
if ((await ours.oid()) === baseOid) {
96719680
return {
96729681
cleanMerge: true,
96739682
mergeResult: { mode, path, oid: await theirs.oid(), type },
96749683
}
96759684
}
9676-
if ((await theirs.oid()) === (await base.oid())) {
9685+
if ((await theirs.oid()) === baseOid) {
96779686
return {
96789687
cleanMerge: true,
96799688
mergeResult: { mode, path, oid: await ours.oid(), type },
96809689
}
96819690
}
96829691
// if both sides made changes do a merge
96839692
const ourContent = Buffer.from(await ours.content()).toString('utf8');
9684-
const baseContent = Buffer.from(await base.content()).toString('utf8');
96859693
const theirContent = Buffer.from(await theirs.content()).toString('utf8');
96869694
const { mergedText, cleanMerge } = await mergeDriver({
96879695
branches: [baseName, ourName, theirName],
@@ -9739,6 +9747,7 @@ async function mergeBlobs({
97399747
* @param {string} [args.signingKey]
97409748
* @param {SignCallback} [args.onSign] - a PGP signing implementation
97419749
* @param {MergeDriverCallback} [args.mergeDriver]
9750+
* @param {boolean} args.allowUnrelatedHistories
97429751
*
97439752
* @returns {Promise<MergeResult>} Resolves to a description of the merge operation
97449753
*
@@ -9761,6 +9770,7 @@ async function _merge({
97619770
signingKey,
97629771
onSign,
97639772
mergeDriver,
9773+
allowUnrelatedHistories = false,
97649774
}) {
97659775
if (ours === undefined) {
97669776
ours = await _currentBranch({ fs, gitdir, fullname: true });
@@ -9793,8 +9803,13 @@ async function _merge({
97939803
oids: [ourOid, theirOid],
97949804
});
97959805
if (baseOids.length !== 1) {
9796-
// TODO: Recursive Merge strategy
9797-
throw new MergeNotSupportedError()
9806+
if (baseOids.length === 0 && allowUnrelatedHistories) {
9807+
// 4b825… == the empty tree used by git
9808+
baseOids.push('4b825dc642cb6eb9a060e54bf8d69288fbee4904');
9809+
} else {
9810+
// TODO: Recursive Merge strategy
9811+
throw new MergeNotSupportedError()
9812+
}
97989813
}
97999814
const baseOid = baseOids[0];
98009815
// handle fast-forward case
@@ -12017,6 +12032,7 @@ async function log({
1201712032
* @param {string} [args.signingKey] - passed to [commit](commit.md) when creating a merge commit
1201812033
* @param {object} [args.cache] - a [cache](cache.md) object
1201912034
* @param {MergeDriverCallback} [args.mergeDriver] - a [merge driver](mergeDriver.md) implementation
12035+
* @param {boolean} [args.allowUnrelatedHistories = false] - If true, allows merging histories of two branches that started their lives independently.
1202012036
*
1202112037
* @returns {Promise<MergeResult>} Resolves to a description of the merge operation
1202212038
* @see MergeResult
@@ -12049,6 +12065,7 @@ async function merge({
1204912065
signingKey,
1205012066
cache = {},
1205112067
mergeDriver,
12068+
allowUnrelatedHistories = false,
1205212069
}) {
1205312070
try {
1205412071
assertParameter('fs', _fs);
@@ -12090,6 +12107,7 @@ async function merge({
1209012107
signingKey,
1209112108
onSign,
1209212109
mergeDriver,
12110+
allowUnrelatedHistories,
1209312111
})
1209412112
} catch (err) {
1209512113
err.caller = 'git.merge';

js/isomorphic-git/index.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2245,6 +2245,7 @@ export function log({ fs, dir, gitdir, filepath, ref, depth, since, force, follo
22452245
* @param {string} [args.signingKey] - passed to [commit](commit.md) when creating a merge commit
22462246
* @param {object} [args.cache] - a [cache](cache.md) object
22472247
* @param {MergeDriverCallback} [args.mergeDriver] - a [merge driver](mergeDriver.md) implementation
2248+
* @param {boolean} [args.allowUnrelatedHistories = false] - If true, allows merging histories of two branches that started their lives independently.
22482249
*
22492250
* @returns {Promise<MergeResult>} Resolves to a description of the merge operation
22502251
* @see MergeResult
@@ -2259,7 +2260,7 @@ export function log({ fs, dir, gitdir, filepath, ref, depth, since, force, follo
22592260
* console.log(m)
22602261
*
22612262
*/
2262-
export function merge({ fs: _fs, onSign, dir, gitdir, ours, theirs, fastForward, fastForwardOnly, dryRun, noUpdateBranch, abortOnConflict, message, author: _author, committer: _committer, signingKey, cache, mergeDriver, }: {
2263+
export function merge({ fs: _fs, onSign, dir, gitdir, ours, theirs, fastForward, fastForwardOnly, dryRun, noUpdateBranch, abortOnConflict, message, author: _author, committer: _committer, signingKey, cache, mergeDriver, allowUnrelatedHistories, }: {
22632264
fs: FsClient;
22642265
onSign?: SignCallback | undefined;
22652266
dir?: string | undefined;
@@ -2287,6 +2288,7 @@ export function merge({ fs: _fs, onSign, dir, gitdir, ours, theirs, fastForward,
22872288
signingKey?: string | undefined;
22882289
cache?: object;
22892290
mergeDriver?: MergeDriverCallback | undefined;
2291+
allowUnrelatedHistories?: boolean | undefined;
22902292
}): Promise<MergeResult>;
22912293
/**
22922294
*

js/isomorphic-git/index.js

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9434,10 +9434,8 @@ async function mergeTree({
94349434
// Modifications
94359435
if (
94369436
ours &&
9437-
base &&
94389437
theirs &&
94399438
(await ours.type()) === 'blob' &&
9440-
(await base.type()) === 'blob' &&
94419439
(await theirs.type()) === 'blob'
94429440
) {
94439441
return mergeBlobs({
@@ -9456,13 +9454,18 @@ async function mergeTree({
94569454
unmergedFiles.push(filepath);
94579455
bothModified.push(filepath);
94589456
if (!abortOnConflict) {
9459-
const baseOid = await base.oid();
9457+
let baseOid = '';
9458+
if (base && (await base.type()) === 'blob') {
9459+
baseOid = await base.oid();
9460+
}
94609461
const ourOid = await ours.oid();
94619462
const theirOid = await theirs.oid();
94629463

94639464
index.delete({ filepath });
94649465

9465-
index.insert({ filepath, oid: baseOid, stage: 1 });
9466+
if (baseOid) {
9467+
index.insert({ filepath, oid: baseOid, stage: 1 });
9468+
}
94669469
index.insert({ filepath, oid: ourOid, stage: 2 });
94679470
index.insert({ filepath, oid: theirOid, stage: 3 });
94689471
}
@@ -9649,10 +9652,16 @@ async function mergeBlobs({
96499652
const type = 'blob';
96509653
// Compute the new mode.
96519654
// Since there are ONLY two valid blob modes ('100755' and '100644') it boils down to this
9655+
let baseMode = '100755';
9656+
let baseOid = '';
9657+
let baseContent = '';
9658+
if (base && (await base.type()) === 'blob') {
9659+
baseMode = await base.mode();
9660+
baseOid = await base.oid();
9661+
baseContent = Buffer.from(await base.content()).toString('utf8');
9662+
}
96529663
const mode =
9653-
(await base.mode()) === (await ours.mode())
9654-
? await theirs.mode()
9655-
: await ours.mode();
9664+
baseMode === (await ours.mode()) ? await theirs.mode() : await ours.mode();
96569665
// The trivial case: nothing to merge except maybe mode
96579666
if ((await ours.oid()) === (await theirs.oid())) {
96589667
return {
@@ -9661,21 +9670,20 @@ async function mergeBlobs({
96619670
}
96629671
}
96639672
// if only one side made oid changes, return that side's oid
9664-
if ((await ours.oid()) === (await base.oid())) {
9673+
if ((await ours.oid()) === baseOid) {
96659674
return {
96669675
cleanMerge: true,
96679676
mergeResult: { mode, path, oid: await theirs.oid(), type },
96689677
}
96699678
}
9670-
if ((await theirs.oid()) === (await base.oid())) {
9679+
if ((await theirs.oid()) === baseOid) {
96719680
return {
96729681
cleanMerge: true,
96739682
mergeResult: { mode, path, oid: await ours.oid(), type },
96749683
}
96759684
}
96769685
// if both sides made changes do a merge
96779686
const ourContent = Buffer.from(await ours.content()).toString('utf8');
9678-
const baseContent = Buffer.from(await base.content()).toString('utf8');
96799687
const theirContent = Buffer.from(await theirs.content()).toString('utf8');
96809688
const { mergedText, cleanMerge } = await mergeDriver({
96819689
branches: [baseName, ourName, theirName],
@@ -9733,6 +9741,7 @@ async function mergeBlobs({
97339741
* @param {string} [args.signingKey]
97349742
* @param {SignCallback} [args.onSign] - a PGP signing implementation
97359743
* @param {MergeDriverCallback} [args.mergeDriver]
9744+
* @param {boolean} args.allowUnrelatedHistories
97369745
*
97379746
* @returns {Promise<MergeResult>} Resolves to a description of the merge operation
97389747
*
@@ -9755,6 +9764,7 @@ async function _merge({
97559764
signingKey,
97569765
onSign,
97579766
mergeDriver,
9767+
allowUnrelatedHistories = false,
97589768
}) {
97599769
if (ours === undefined) {
97609770
ours = await _currentBranch({ fs, gitdir, fullname: true });
@@ -9787,8 +9797,13 @@ async function _merge({
97879797
oids: [ourOid, theirOid],
97889798
});
97899799
if (baseOids.length !== 1) {
9790-
// TODO: Recursive Merge strategy
9791-
throw new MergeNotSupportedError()
9800+
if (baseOids.length === 0 && allowUnrelatedHistories) {
9801+
// 4b825… == the empty tree used by git
9802+
baseOids.push('4b825dc642cb6eb9a060e54bf8d69288fbee4904');
9803+
} else {
9804+
// TODO: Recursive Merge strategy
9805+
throw new MergeNotSupportedError()
9806+
}
97929807
}
97939808
const baseOid = baseOids[0];
97949809
// handle fast-forward case
@@ -12011,6 +12026,7 @@ async function log({
1201112026
* @param {string} [args.signingKey] - passed to [commit](commit.md) when creating a merge commit
1201212027
* @param {object} [args.cache] - a [cache](cache.md) object
1201312028
* @param {MergeDriverCallback} [args.mergeDriver] - a [merge driver](mergeDriver.md) implementation
12029+
* @param {boolean} [args.allowUnrelatedHistories = false] - If true, allows merging histories of two branches that started their lives independently.
1201412030
*
1201512031
* @returns {Promise<MergeResult>} Resolves to a description of the merge operation
1201612032
* @see MergeResult
@@ -12043,6 +12059,7 @@ async function merge({
1204312059
signingKey,
1204412060
cache = {},
1204512061
mergeDriver,
12062+
allowUnrelatedHistories = false,
1204612063
}) {
1204712064
try {
1204812065
assertParameter('fs', _fs);
@@ -12084,6 +12101,7 @@ async function merge({
1208412101
signingKey,
1208512102
onSign,
1208612103
mergeDriver,
12104+
allowUnrelatedHistories,
1208712105
})
1208812106
} catch (err) {
1208912107
err.caller = 'git.merge';

js/isomorphic-git/index.umd.min.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2245,6 +2245,7 @@ export function log({ fs, dir, gitdir, filepath, ref, depth, since, force, follo
22452245
* @param {string} [args.signingKey] - passed to [commit](commit.md) when creating a merge commit
22462246
* @param {object} [args.cache] - a [cache](cache.md) object
22472247
* @param {MergeDriverCallback} [args.mergeDriver] - a [merge driver](mergeDriver.md) implementation
2248+
* @param {boolean} [args.allowUnrelatedHistories = false] - If true, allows merging histories of two branches that started their lives independently.
22482249
*
22492250
* @returns {Promise<MergeResult>} Resolves to a description of the merge operation
22502251
* @see MergeResult
@@ -2259,7 +2260,7 @@ export function log({ fs, dir, gitdir, filepath, ref, depth, since, force, follo
22592260
* console.log(m)
22602261
*
22612262
*/
2262-
export function merge({ fs: _fs, onSign, dir, gitdir, ours, theirs, fastForward, fastForwardOnly, dryRun, noUpdateBranch, abortOnConflict, message, author: _author, committer: _committer, signingKey, cache, mergeDriver, }: {
2263+
export function merge({ fs: _fs, onSign, dir, gitdir, ours, theirs, fastForward, fastForwardOnly, dryRun, noUpdateBranch, abortOnConflict, message, author: _author, committer: _committer, signingKey, cache, mergeDriver, allowUnrelatedHistories, }: {
22632264
fs: FsClient;
22642265
onSign?: SignCallback | undefined;
22652266
dir?: string | undefined;
@@ -2287,6 +2288,7 @@ export function merge({ fs: _fs, onSign, dir, gitdir, ours, theirs, fastForward,
22872288
signingKey?: string | undefined;
22882289
cache?: object;
22892290
mergeDriver?: MergeDriverCallback | undefined;
2291+
allowUnrelatedHistories?: boolean | undefined;
22902292
}): Promise<MergeResult>;
22912293
/**
22922294
*

js/isomorphic-git/index.umd.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/isomorphic-git/index.umd.min.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)