Skip to content

Commit 59785b0

Browse files
arcanismerceyz
andauthored
Fixes rebase and cherry-pick conflict resolution (#5102)
* Fixes rebase conflict resolution * Adds cherry-pick support * Update packages/gatsby/content/advanced/error-codes.md Co-authored-by: Kristoffer K. <[email protected]> Co-authored-by: Kristoffer K. <[email protected]>
1 parent 84c62e6 commit 59785b0

File tree

6 files changed

+241
-5
lines changed

6 files changed

+241
-5
lines changed

.yarn/versions/a95040d3.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
releases:
2+
"@yarnpkg/cli": patch
3+
"@yarnpkg/core": patch
4+
"@yarnpkg/plugin-essentials": patch
5+
6+
declined:
7+
- "@yarnpkg/plugin-compat"
8+
- "@yarnpkg/plugin-constraints"
9+
- "@yarnpkg/plugin-dlx"
10+
- "@yarnpkg/plugin-exec"
11+
- "@yarnpkg/plugin-file"
12+
- "@yarnpkg/plugin-git"
13+
- "@yarnpkg/plugin-github"
14+
- "@yarnpkg/plugin-http"
15+
- "@yarnpkg/plugin-init"
16+
- "@yarnpkg/plugin-interactive-tools"
17+
- "@yarnpkg/plugin-link"
18+
- "@yarnpkg/plugin-nm"
19+
- "@yarnpkg/plugin-npm"
20+
- "@yarnpkg/plugin-npm-cli"
21+
- "@yarnpkg/plugin-pack"
22+
- "@yarnpkg/plugin-patch"
23+
- "@yarnpkg/plugin-pnp"
24+
- "@yarnpkg/plugin-pnpm"
25+
- "@yarnpkg/plugin-stage"
26+
- "@yarnpkg/plugin-typescript"
27+
- "@yarnpkg/plugin-version"
28+
- "@yarnpkg/plugin-workspace-tools"
29+
- "@yarnpkg/builder"
30+
- "@yarnpkg/doctor"
31+
- "@yarnpkg/extensions"
32+
- "@yarnpkg/nm"
33+
- "@yarnpkg/pnpify"
34+
- "@yarnpkg/sdks"

packages/acceptance-tests/pkg-tests-specs/sources/features/__snapshots__/mergeConflictResolution.test.js.snap

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,101 @@ exports[`Features Merge Conflict Resolution it should properly fix merge conflic
9797
",
9898
}
9999
`;
100+
101+
exports[`Features Merge Conflict Resolution it should support fixing cherry-pick conflicts 1`] = `
102+
"# This file is generated by running "yarn install" inside your project.
103+
# Manual changes might be lost - proceed with caution!
104+
105+
__metadata:
106+
version: 7
107+
cacheKey: 0
108+
109+
"no-deps@npm:*":
110+
<<<<<<< HEAD
111+
version: 1.0.0
112+
resolution: "no-deps@npm:1.0.0"
113+
checksum: <checksum stripped>
114+
=======
115+
version: 2.0.0
116+
resolution: "no-deps@npm:2.0.0"
117+
checksum: <checksum stripped>
118+
>>>>>>> 0000000 (commit-2.0.0)
119+
languageName: node
120+
linkType: hard
121+
122+
"root-workspace-0b6124@workspace:.":
123+
version: 0.0.0-use.local
124+
resolution: "root-workspace-0b6124@workspace:."
125+
dependencies:
126+
no-deps: "npm:*"
127+
languageName: unknown
128+
linkType: soft
129+
"
130+
`;
131+
132+
exports[`Features Merge Conflict Resolution it should support fixing cherry-pick conflicts 2`] = `
133+
{
134+
"code": 0,
135+
"stderr": "",
136+
"stdout": "➤ YN0048: Automatically fixed merge conflicts 👍
137+
138+
YN0000: ┌ Resolution step
139+
YN0000: └ Completed
140+
YN0000: ┌ Fetch step
141+
YN0019: │ no-deps-npm-2.0.0-8c4f3f8395-9c77ddf9a6.zip appears to be unused - removing
142+
YN0000: └ Completed
143+
YN0000: ┌ Link step
144+
YN0000: └ Completed
145+
YN0000: Done
146+
",
147+
}
148+
`;
149+
150+
exports[`Features Merge Conflict Resolution it should support fixing rebase conflicts 1`] = `
151+
"# This file is generated by running "yarn install" inside your project.
152+
# Manual changes might be lost - proceed with caution!
153+
154+
__metadata:
155+
version: 7
156+
cacheKey: 0
157+
158+
"no-deps@npm:*":
159+
<<<<<<< HEAD
160+
version: 2.0.0
161+
resolution: "no-deps@npm:2.0.0"
162+
checksum: <checksum stripped>
163+
=======
164+
version: 1.0.0
165+
resolution: "no-deps@npm:1.0.0"
166+
checksum: <checksum stripped>
167+
>>>>>>> 0000000 (commit-1.0.0)
168+
languageName: node
169+
linkType: hard
170+
171+
"root-workspace-0b6124@workspace:.":
172+
version: 0.0.0-use.local
173+
resolution: "root-workspace-0b6124@workspace:."
174+
dependencies:
175+
no-deps: "npm:*"
176+
languageName: unknown
177+
linkType: soft
178+
"
179+
`;
180+
181+
exports[`Features Merge Conflict Resolution it should support fixing rebase conflicts 2`] = `
182+
{
183+
"code": 0,
184+
"stderr": "",
185+
"stdout": "➤ YN0048: Automatically fixed merge conflicts 👍
186+
187+
YN0000: ┌ Resolution step
188+
YN0000: └ Completed
189+
YN0000: ┌ Fetch step
190+
YN0019: │ no-deps-npm-1.0.0-cf533b267a-8bd88a447c.zip appears to be unused - removing
191+
YN0000: └ Completed
192+
YN0000: ┌ Link step
193+
YN0000: └ Completed
194+
YN0000: Done
195+
",
196+
}
197+
`;

packages/acceptance-tests/pkg-tests-specs/sources/features/mergeConflictResolution.test.js

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ describe(`Features`, () => {
6262
let lockfile = await xfs.readFilePromise(`${path}/yarn.lock`, `utf8`);
6363
lockfile = lockfile.replace(/(checksum: ).*/g, `$1<checksum stripped>`);
6464

65-
await expect(lockfile).toMatchSnapshot();
65+
expect(lockfile).toMatchSnapshot();
6666
await expect(run(`install`)).resolves.toMatchSnapshot();
6767
},
6868
),
@@ -103,7 +103,93 @@ describe(`Features`, () => {
103103
let lockfile = await xfs.readFilePromise(`${path}/yarn.lock`, `utf8`);
104104
lockfile = lockfile.replace(/(checksum: ).*/g, `$1<checksum stripped>`);
105105

106-
await expect(lockfile).toMatchSnapshot();
106+
expect(lockfile).toMatchSnapshot();
107+
await expect(run(`install`)).resolves.toMatchSnapshot();
108+
},
109+
),
110+
);
111+
112+
test(
113+
`it should support fixing rebase conflicts`,
114+
makeTemporaryEnv(
115+
{},
116+
async ({path, run, source}) => {
117+
await execFile(`git`, [`init`], {cwd: path});
118+
await execFile(`git`, [`config`, `user.email`, `[email protected]`], {cwd: path});
119+
await execFile(`git`, [`config`, `user.name`, `Your Name`], {cwd: path});
120+
await execFile(`git`, [`config`, `commit.gpgSign`, `false`], {cwd: path});
121+
122+
await run(`install`);
123+
await xfs.writeJsonPromise(`${path}/package.json`, {dependencies: {[`no-deps`]: `*`}});
124+
125+
await execFile(`git`, [`add`, `-A`], {cwd: path});
126+
await execFile(`git`, [`commit`, `-a`, `-m`, `my-commit`], {cwd: path});
127+
128+
await execFile(`git`, [`checkout`, `master`], {cwd: path});
129+
await execFile(`git`, [`checkout`, `-b`, `1.0.0`], {cwd: path});
130+
await run(`set`, `resolution`, `no-deps@npm:*`, `npm:1.0.0`);
131+
await execFile(`git`, [`add`, `-A`], {cwd: path});
132+
await execFile(`git`, [`commit`, `-a`, `-m`, `commit-1.0.0`], {cwd: path});
133+
134+
await execFile(`git`, [`checkout`, `master`], {cwd: path});
135+
await execFile(`git`, [`checkout`, `-b`, `2.0.0`], {cwd: path});
136+
await run(`set`, `resolution`, `no-deps@npm:*`, `npm:2.0.0`);
137+
await execFile(`git`, [`add`, `-A`], {cwd: path});
138+
await execFile(`git`, [`commit`, `-a`, `-m`, `commit-2.0.0`], {cwd: path});
139+
140+
await execFile(`git`, [`checkout`, `master`], {cwd: path});
141+
await execFile(`git`, [`merge`, `1.0.0`], {cwd: path});
142+
143+
await expect(execFile(`git`, [`rebase`, `2.0.0`], {cwd: path, env: {LC_ALL: `C`}})).rejects.toThrow(/CONFLICT/);
144+
145+
let lockfile = await xfs.readFilePromise(`${path}/yarn.lock`, `utf8`);
146+
lockfile = lockfile.replace(/(checksum: ).*/g, `$1<checksum stripped>`);
147+
lockfile = lockfile.replace(/(>>>>>>>).*(\(commit-1.0.0\))/g, `$1 0000000 $2`);
148+
149+
expect(lockfile).toMatchSnapshot();
150+
await expect(run(`install`)).resolves.toMatchSnapshot();
151+
},
152+
),
153+
);
154+
155+
test(
156+
`it should support fixing cherry-pick conflicts`,
157+
makeTemporaryEnv(
158+
{},
159+
async ({path, run, source}) => {
160+
await execFile(`git`, [`init`], {cwd: path});
161+
await execFile(`git`, [`config`, `user.email`, `[email protected]`], {cwd: path});
162+
await execFile(`git`, [`config`, `user.name`, `Your Name`], {cwd: path});
163+
await execFile(`git`, [`config`, `commit.gpgSign`, `false`], {cwd: path});
164+
165+
await run(`install`);
166+
await xfs.writeJsonPromise(`${path}/package.json`, {dependencies: {[`no-deps`]: `*`}});
167+
168+
await execFile(`git`, [`add`, `-A`], {cwd: path});
169+
await execFile(`git`, [`commit`, `-a`, `-m`, `my-commit`], {cwd: path});
170+
171+
await execFile(`git`, [`checkout`, `master`], {cwd: path});
172+
await execFile(`git`, [`checkout`, `-b`, `1.0.0`], {cwd: path});
173+
await run(`set`, `resolution`, `no-deps@npm:*`, `npm:1.0.0`);
174+
await execFile(`git`, [`add`, `-A`], {cwd: path});
175+
await execFile(`git`, [`commit`, `-a`, `-m`, `commit-1.0.0`], {cwd: path});
176+
177+
await execFile(`git`, [`checkout`, `master`], {cwd: path});
178+
await execFile(`git`, [`checkout`, `-b`, `2.0.0`], {cwd: path});
179+
await run(`set`, `resolution`, `no-deps@npm:*`, `npm:2.0.0`);
180+
await execFile(`git`, [`add`, `-A`], {cwd: path});
181+
await execFile(`git`, [`commit`, `-a`, `-m`, `commit-2.0.0`], {cwd: path});
182+
183+
await execFile(`git`, [`checkout`, `master`], {cwd: path});
184+
await execFile(`git`, [`merge`, `1.0.0`], {cwd: path});
185+
186+
await expect(execFile(`git`, [`cherry-pick`, `2.0.0`], {cwd: path, env: {LC_ALL: `C`}})).rejects.toThrow(/CONFLICT/);
187+
188+
let lockfile = await xfs.readFilePromise(`${path}/yarn.lock`, `utf8`);
189+
lockfile = lockfile.replace(/(checksum: ).*/g, `$1<checksum stripped>`);
190+
lockfile = lockfile.replace(/(>>>>>>>).*(\(commit-)/g, `$1 0000000 $2`);
191+
192+
expect(lockfile).toMatchSnapshot();
107193
await expect(run(`install`)).resolves.toMatchSnapshot();
108194
},
109195
),

packages/gatsby/content/advanced/error-codes.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,3 +404,10 @@ Yarn failed to locate a package version that could satisfy the requested range.
404404
- The registry may not have been set properly (so Yarn is querying the public npm registry instead of your internal one)
405405

406406
- The version may have been unpublished (although this shouldn't be possible for the public registry)
407+
408+
## YN0083 - `AUTOMERGE_GIT_ERROR`
409+
410+
When autofixing merge conflicts, Yarn needs to know what are the two lockfile versions it must merge together. To do that, it'll run `git rev-parse MERGE_HEAD HEAD` and/or `git rev-parse REBASE_HEAD HEAD`, depending on the situation. If both of those commands fail, the merge cannot succeed.
411+
412+
This may happen if someone accidentally committed the lockfile without first resolving the merge conflicts - should that happen, you'll need to revert the lockfile to an earlier working version and run `yarn install`.
413+

packages/plugin-essentials/sources/commands/install.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,12 +360,24 @@ async function autofixMergeConflicts(configuration: Configuration, immutable: bo
360360
if (immutable)
361361
throw new ReportError(MessageName.AUTOMERGE_IMMUTABLE, `Cannot autofix a lockfile when running an immutable install`);
362362

363-
const commits = await execUtils.execvp(`git`, [`rev-parse`, `MERGE_HEAD`, `HEAD`], {
363+
let commits = await execUtils.execvp(`git`, [`rev-parse`, `MERGE_HEAD`, `HEAD`], {
364364
cwd: configuration.projectCwd,
365365
});
366366

367+
if (commits.code !== 0) {
368+
commits = await execUtils.execvp(`git`, [`rev-parse`, `REBASE_HEAD`, `HEAD`], {
369+
cwd: configuration.projectCwd,
370+
});
371+
}
372+
373+
if (commits.code !== 0) {
374+
commits = await execUtils.execvp(`git`, [`rev-parse`, `CHERRY_PICK_HEAD`, `HEAD`], {
375+
cwd: configuration.projectCwd,
376+
});
377+
}
378+
367379
if (commits.code !== 0)
368-
throw new ReportError(MessageName.AUTOMERGE_GIT_ERROR, `Git returned an error when trying to find the commits pertaining to the merge conflict`);
380+
throw new ReportError(MessageName.AUTOMERGE_GIT_ERROR, `Git returned an error when trying to find the commits pertaining to the conflict`);
369381

370382
let variants = await Promise.all(commits.stdout.trim().split(/\n/).map(async hash => {
371383
const content = await execUtils.execvp(`git`, [`show`, `${hash}:./${Filename.lockfile}`], {

packages/yarnpkg-core/sources/MessageName.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ export enum MessageName {
9595
NETWORK_UNSAFE_HTTP = 81,
9696
RESOLUTION_FAILED = 82,
9797
AUTOMERGE_GIT_ERROR = 83,
98-
AUTOMERGE_LOCKFILE_VERSION_MISMATCH = 84,
9998
}
10099

101100
export function stringifyMessageName(name: MessageName | number): string {

0 commit comments

Comments
 (0)