Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(lane-merge), merge recovered components #7760

Merged
merged 6 commits into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions e2e/harmony/recover.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,4 +235,53 @@ describe('bit recover command', function () {
expect(removeData.config.removed).to.be.true;
});
});
describe('remove in one lane, recover in other lane, then merged to the first lane', () => {
before(() => {
helper.scopeHelper.setNewLocalAndRemoteScopes();
helper.command.createLane('lane-a');
helper.fixtures.populateComponents(2);
helper.command.snapAllComponentsWithoutBuild();
helper.command.export();

helper.command.createLane('lane-b');
helper.command.snapAllComponentsWithoutBuild('--unmodified');
helper.command.export();

helper.command.switchLocalLane('lane-a', '-x');
helper.command.removeLaneComp('comp1');
helper.command.snapAllComponentsWithoutBuild();
helper.command.export();

helper.command.switchLocalLane('lane-b', '-x');
helper.command.mergeLane('lane-a', '-x');
helper.command.recover(`${helper.scopes.remote}/comp1`);
helper.command.snapAllComponentsWithoutBuild();
helper.command.export();
helper.command.switchLocalLane('lane-a', '-x');
helper.command.mergeLane('lane-b');
});
it('should bring back the previously removed component', () => {
const list = helper.command.listParsed();
expect(list).to.have.lengthOf(2);
helper.bitMap.expectToHaveId('comp1');
});
});

describe('remove in one lane, recover in an empty lane', () => {
before(() => {
helper.scopeHelper.setNewLocalAndRemoteScopes();
helper.command.createLane('lane-a');
helper.fixtures.populateComponents(2);
helper.command.snapAllComponentsWithoutBuild();
helper.command.export();
helper.command.removeLaneComp('comp1');
helper.command.snapAllComponentsWithoutBuild();
helper.command.export();
helper.command.createLane('lane-b');
});
// currently it threw version "0.0.0" of component onpp7beq-remote/comp1 was not found
it('should show a descriptive error', () => {
expect(() => helper.command.recover(`${helper.scopes.remote}/comp1`)).to.throw('unable to find the component');
});
});
});
12 changes: 11 additions & 1 deletion scopes/component/merging/merging.main.runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,17 @@ export class MergingMain {
};
const currentComponent = await getCurrentComponent();
if (currentComponent.isRemoved()) {
return returnUnmerged(`component has been removed`, true);
// we made sure before that the component is not removed on the "other". so we have a few options:
// 1. other is ahead. in this case, other recovered the component. so we can continue with the merge.
// it is possible that it is diverged, in which case, still continue with the merge, and later on, the
// merge-config will show a config conflict of the remove aspect.
// 2. other is not ahead. in this case, just ignore this component, no point to merge it, we want it removed.
// 3. there are errors when calculating the divergeData, e.g. no snap in common. in such cases, we assume
// there are issues with this component, and is better not to merge it.
const divergeData = await getDivergeData({ repo, modelComponent, targetHead: otherLaneHead, throws: false });
if (divergeData.err || !divergeData.isTargetAhead()) {
return returnUnmerged(`component has been removed`, true);
}
}
const isModified = async () => {
const componentModificationStatus = await consumer.getComponentStatusById(currentComponent.id);
Expand Down
20 changes: 17 additions & 3 deletions scopes/component/remove/remove.main.runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ComponentID } from '@teambit/component-id';
import { BitError } from '@teambit/bit-error';
import deleteComponentsFiles from '@teambit/legacy/dist/consumer/component-ops/delete-component-files';
import ComponentAspect, { Component, ComponentMain } from '@teambit/component';
import { VersionNotFound } from '@teambit/legacy/dist/scope/exceptions';
import { removeComponentsFromNodeModules } from '@teambit/legacy/dist/consumer/component/package-json-utils';
import { RemoveCmd } from './remove-cmd';
import { removeComponents } from './remove-components';
Expand Down Expand Up @@ -163,11 +164,24 @@ export class RemoveMain {
return true;
}
const compId = await this.workspace.scope.resolveComponentId(compIdStr);
const compFromScope = await this.workspace.scope.get(compId);
const currentLane = await this.workspace.getCurrentLaneObject();
const idOnLane = currentLane?.getComponent(compId._legacy);
const compIdWithPossibleVer = idOnLane ? compId.changeVersion(idOnLane.head.toString()) : compId;
let compFromScope: Component | undefined;
try {
compFromScope = await this.workspace.scope.get(compIdWithPossibleVer);
} catch (err: any) {
if (err instanceof VersionNotFound && err.version === '0.0.0') {
throw new BitError(
`unable to find the component ${compIdWithPossibleVer.toString()} in the current lane or main`
);
}
throw err;
}
if (compFromScope && this.isRemoved(compFromScope)) {
// case #2 and #3
await importComp(compId._legacy.toString());
await setAsRemovedFalse(compId);
await importComp(compIdWithPossibleVer._legacy.toString());
await setAsRemovedFalse(compIdWithPossibleVer);
return true;
}
// case #5
Expand Down
2 changes: 1 addition & 1 deletion src/scope/exceptions/version-not-found.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import chalk from 'chalk';
* @see VersionNotFoundOnFS for cases when the Version object is missing from the filesystem.
*/
export default class VersionNotFound extends BitError {
constructor(version: string, componentId: string) {
constructor(readonly version: string, componentId: string) {
super(`error: version "${chalk.bold(version)}" of component ${chalk.bold(componentId)} was not found.`);
}
}