Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Script for syncing changes between forks (#18550)
Adds command `yarn merge-fork`. ```sh yarn merge-fork --base-dir=packages/react-reconciler/src ReactFiberWorkLoop ``` This will take all the changes in `ReactFiberWorkLoop.new.js` and apply them to `ReactFiberWorkLoop.old.js`. You can merge multiple modules at a time: ```sh yarn merge-fork \ --base-dir=packages/react-reconciler/src \ ReactFiberWorkLoop \ ReactFiberBeginWork \ ReactFiberCompleteWork \ ReactFiberCommitWork ``` You can provide explicit "old" and "new" file names. This only works for one module at a time: ```sh yarn merge-fork \ --base-dir=packages/react-reconciler/src \ --old=ReactFiberExpirationTime.js \ --new=ReactFiberLane.js ``` The default is to merge changes from the new module to the old one. To merge changes in the opposite direction, use `--reverse`. ```sh yarn merge-fork \ --reverse \ --base-dir=packages/react-reconciler/src \ ReactFiberWorkLoop ``` By default, the changes are compared to HEAD. You can use `--base-ref` to compare to any rev. For example, while working on a PR, you might make multiple commits to the new fork before you're ready to backport them to the old one. In that case, you want to compare to the merge base of your PR branch: ```sh yarn merge-fork \ --base-ref=$(git merge-base HEAD origin/master) --base-dir=packages/react-reconciler/src \ ReactFiberWorkLoop ```
- Loading branch information
Showing
3 changed files
with
169 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# merge-fork | ||
|
||
Script for syncing changes between forked modules. | ||
|
||
## Basic example | ||
|
||
```sh | ||
yarn merge-fork --base-dir=packages/react-reconciler/src ReactFiberWorkLoop | ||
``` | ||
|
||
This will take all the changes in `ReactFiberWorkLoop.new.js` and apply them to `ReactFiberWorkLoop.old.js`. | ||
|
||
## Syncing multiple modules at once | ||
|
||
You can merge multiple modules at a time: | ||
|
||
```sh | ||
yarn merge-fork \ | ||
--base-dir=packages/react-reconciler/src \ | ||
ReactFiberWorkLoop \ | ||
ReactFiberBeginWork \ | ||
ReactFiberCompleteWork \ | ||
ReactFiberCommitWork | ||
``` | ||
|
||
## Syncing modules with different names | ||
|
||
You can provide explicit "old" and "new" file names. This only works for one module at a time: | ||
|
||
```sh | ||
yarn merge-fork \ | ||
--base-dir=packages/react-reconciler/src \ | ||
--old=ReactFiberExpirationTime.js \ | ||
--new=ReactFiberLane.js | ||
``` | ||
|
||
## Syncing modules in the opposite direction (old -> new) | ||
|
||
The default is to merge changes from the new module to the old one. To merge changes in the opposite direction, use `--reverse`. | ||
|
||
```sh | ||
yarn merge-fork \ | ||
--reverse \ | ||
--base-dir=packages/react-reconciler/src \ | ||
ReactFiberWorkLoop | ||
``` | ||
|
||
## Comparing changes to an older base rev | ||
|
||
By default, the changes are compared to HEAD. You can use `--base-ref` to compare to any rev. For example, while working on a PR, you might make multiple commits to the new fork before you're ready to backport them to the old one. In that case, you want to compare to the merge base of your PR branch: | ||
|
||
```sh | ||
yarn merge-fork \ | ||
--base-ref=$(git merge-base HEAD origin/master) | ||
--base-dir=packages/react-reconciler/src \ | ||
ReactFiberWorkLoop | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
'use strict'; | ||
|
||
/* eslint-disable no-for-of-loops/no-for-of-loops */ | ||
|
||
const {writeFileSync} = require('fs'); | ||
const path = require('path'); | ||
const {spawnSync} = require('child_process'); | ||
const minimist = require('minimist'); | ||
const tmp = require('tmp'); | ||
|
||
const argv = minimist(process.argv.slice(2), { | ||
boolean: ['reverse'], | ||
default: { | ||
'base-ref': 'HEAD', | ||
}, | ||
}); | ||
|
||
const baseRef = argv['base-ref']; | ||
const baseDir = argv['base-dir']; | ||
|
||
function resolvePath(file) { | ||
return baseDir !== undefined ? path.join(baseDir, file) : file; | ||
} | ||
|
||
function getTransforms() { | ||
const old = argv.old; | ||
const base = argv.base; | ||
const _new = argv.new; | ||
if (old !== undefined) { | ||
if (_new === undefined) { | ||
throw Error('Cannot provide --old without also providing --new'); | ||
} | ||
const oldPath = resolvePath(old); | ||
const newPath = resolvePath(_new); | ||
|
||
let basePath; | ||
let fromPath; | ||
let toPath; | ||
if (argv.reverse) { | ||
fromPath = oldPath; | ||
toPath = newPath; | ||
basePath = base !== undefined ? resolvePath(basePath) : oldPath; | ||
} else { | ||
fromPath = newPath; | ||
toPath = oldPath; | ||
basePath = base !== undefined ? resolvePath(basePath) : newPath; | ||
} | ||
|
||
return [ | ||
{ | ||
base: basePath, | ||
from: fromPath, | ||
to: toPath, | ||
}, | ||
]; | ||
} else if (_new !== undefined) { | ||
throw Error('Cannot provide --new without also providing --old'); | ||
} | ||
return argv._.map(filename => { | ||
const oldPath = resolvePath(filename + '.old.js'); | ||
const newPath = resolvePath(filename + '.new.js'); | ||
|
||
let basePath; | ||
let fromPath; | ||
let toPath; | ||
if (argv.reverse) { | ||
fromPath = oldPath; | ||
toPath = newPath; | ||
basePath = base !== undefined ? resolvePath(basePath) : oldPath; | ||
} else { | ||
fromPath = newPath; | ||
toPath = oldPath; | ||
basePath = base !== undefined ? resolvePath(basePath) : newPath; | ||
} | ||
return { | ||
base: basePath, | ||
from: fromPath, | ||
to: toPath, | ||
}; | ||
}); | ||
} | ||
|
||
for (const {base: baseFilename, from, to} of getTransforms()) { | ||
// Write the base file contents to a temporary file | ||
const gitShowResult = spawnSync( | ||
'git', | ||
['show', `${baseRef}:${baseFilename}`], | ||
{ | ||
stdio: 'pipe', | ||
} | ||
); | ||
if (gitShowResult.status !== 0) { | ||
console.error('' + gitShowResult.stderr); | ||
continue; | ||
} | ||
|
||
const baseFileContents = gitShowResult.stdout; | ||
const base = tmp.fileSync().name; | ||
writeFileSync(base, baseFileContents); | ||
|
||
// Run the merge with `git merge-file` | ||
const mergeFileResult = spawnSync('git', ['merge-file', to, base, from], { | ||
stdio: 'pipe', | ||
}); | ||
|
||
if (mergeFileResult.status !== 0) { | ||
console.error('' + mergeFileResult.stderr); | ||
} | ||
} |