Skip to content

Commit

Permalink
fix(lane-create): use the version from .bitmap when creating a lane f…
Browse files Browse the repository at this point in the history
…rom lane (#6929)

Currently it's using the version from the lane, which can be ahead of the .bitmap and result in unexpected history on the new lane.
  • Loading branch information
davidfirst authored Jan 17, 2023
1 parent 37b4180 commit a6f1099
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 100 deletions.
149 changes: 149 additions & 0 deletions e2e/harmony/lanes/lane-from-lane.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import chai, { expect } from 'chai';
import Helper from '../../../src/e2e-helper/e2e-helper';
import * as fixtures from '../../../src/fixtures/fixtures';
import { removeChalkCharacters } from '../../../src/utils';

chai.use(require('chai-fs'));

describe('bit lane command', function () {
this.timeout(0);
let helper: Helper;
before(() => {
helper = new Helper();
});
after(() => {
helper.scopeHelper.destroy();
});
describe('main => lane-a => lane-b, so laneB branched from laneA', () => {
let beforeSwitchingBack;
before(() => {
helper.scopeHelper.setNewLocalAndRemoteScopes();
// main
helper.fs.outputFile('utils/is-type/is-type.js', fixtures.isType);
helper.command.addComponent('utils/is-type', { i: 'utils/is-type' });
helper.command.snapAllComponentsWithoutBuild();

// laneA
helper.command.createLane('lane-a');
helper.fs.outputFile(
'utils/is-string/is-string.js',
"const isType = require('../is-type/is-type.js'); module.exports = function isString() { return isType() + ' and got is-string'; };"
);
helper.command.addComponent('utils/is-string', { i: 'utils/is-string' });
helper.command.linkAndRewire();
helper.command.compile();
helper.command.snapAllComponentsWithoutBuild();

// laneB
helper.command.createLane('lane-b');
helper.fixtures.createComponentBarFoo();
helper.fixtures.addComponentBarFooAsDir();
helper.command.snapAllComponentsWithoutBuild();

beforeSwitchingBack = helper.scopeHelper.cloneLocalScope();
});
it('lane-a should not contain components from main', () => {
const lane = helper.command.showOneLaneParsed('lane-a');
expect(lane.components).to.have.lengthOf(1);
});
it('laneB object should include components from laneA, but not from main', () => {
const lane = helper.command.showOneLaneParsed('lane-b');
expect(lane.components).to.have.lengthOf(2);
});
it('bit list should show all components available to lane-b', () => {
const list = helper.command.listLocalScopeParsed();
expect(list).to.have.lengthOf(3);
});
describe('checking out to lane-a', () => {
let switchOutput;
before(() => {
switchOutput = helper.command.switchLocalLane('lane-a');
});
it('should indicate that it switched to the new lane', () => {
expect(switchOutput).to.have.string(
removeChalkCharacters('successfully set "lane-a" as the active lane') as string
);
});
// main components belong to lane-a only if they are snapped on lane-a, so utils/is-type
// doesn't belong to lane-a and should not appear as staged when on lane-a.
it('bit status should not show neither lane-b nor main components as staged', () => {
const staged = helper.command.getStagedIdsFromStatus();
expect(staged).to.deep.equal(['utils/is-string']);
const status = helper.command.status();
expect(status).to.not.have.string('bar/foo');
});
it('bit list should not show lane-b components', () => {
const list = helper.command.listParsed();
expect(list).to.have.lengthOf(2);
});
// @todo: test each one of the commands on bar/foo
});
describe('checking out from lane-b to main', () => {
before(() => {
helper.scopeHelper.getClonedLocalScope(beforeSwitchingBack);
helper.command.switchLocalLane('main');
});
it('bit list should only show main components', () => {
const list = helper.command.listParsed();
expect(list).to.have.lengthOf(1);
});
it('bit status should show only main components as staged', () => {
const staged = helper.command.getStagedIdsFromStatus();
expect(staged).to.deep.equal(['utils/is-type']);
const status = helper.command.status();
expect(status).to.not.have.string('bar/foo');
expect(status).to.not.have.string('utils/is-string');
});
});
describe('switching to lane-a then to main', () => {
before(() => {
helper.scopeHelper.getClonedLocalScope(beforeSwitchingBack);
helper.command.switchLocalLane('lane-a');
helper.command.switchLocalLane('main');
});
it('bit list should only show main components', () => {
const list = helper.command.listParsed();
expect(list).to.have.lengthOf(1);
});
it('bit status should show only main components as staged', () => {
const staged = helper.command.getStagedIdsFromStatus();
expect(staged).to.deep.equal(['utils/is-type']);
const status = helper.command.status();
expect(status).to.not.have.string('bar/foo');
expect(status).to.not.have.string('utils/is-string');
});
});
});
describe('creating lane-b from lane-a when lane-a is out-of-date', () => {
let outOfDateState: string;
let firstSnap: string;
let secondSnap: string;
before(() => {
helper.scopeHelper.setNewLocalAndRemoteScopes();
helper.command.createLane('lane-a');
helper.fixtures.populateComponents(1, false);
helper.command.snapAllComponentsWithoutBuild();
firstSnap = helper.command.getHeadOfLane('lane-a', 'comp1');
helper.command.export();
outOfDateState = helper.scopeHelper.cloneLocalScope();

helper.fixtures.populateComponents(1, false, 'v2');
helper.command.snapAllComponentsWithoutBuild();
secondSnap = helper.command.getHeadOfLane('lane-a', 'comp1');
helper.command.export();

helper.scopeHelper.getClonedLocalScope(outOfDateState);
helper.command.import();
// intermediate step to make sure the lane is out-of-date
const status = helper.command.statusJson();
expect(status.outdatedComponents).to.have.lengthOf(1);

helper.command.createLane('lane-b');
});
it('should add the component from the current/first snap (from .bitmap) and not from the head (lane-a object)', () => {
const headOnLaneB = helper.command.getHeadOfLane('lane-b', 'comp1');
expect(headOnLaneB).to.equal(firstSnap);
expect(headOnLaneB).to.not.equal(secondSnap);
});
});
});
99 changes: 0 additions & 99 deletions e2e/harmony/lanes/lanes.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,106 +305,7 @@ describe('bit lane command', function () {
);
});
});
describe('main => lane-a => lane-b, so laneB branched from laneA', () => {
let beforeSwitchingBack;
before(() => {
helper.scopeHelper.setNewLocalAndRemoteScopes();
// main
helper.fs.outputFile('utils/is-type/is-type.js', fixtures.isType);
helper.command.addComponent('utils/is-type', { i: 'utils/is-type' });
helper.command.snapAllComponentsWithoutBuild();

// laneA
helper.command.createLane('lane-a');
helper.fs.outputFile(
'utils/is-string/is-string.js',
"const isType = require('../is-type/is-type.js'); module.exports = function isString() { return isType() + ' and got is-string'; };"
);
helper.command.addComponent('utils/is-string', { i: 'utils/is-string' });
helper.command.linkAndRewire();
helper.command.compile();
helper.command.snapAllComponentsWithoutBuild();

// laneB
helper.command.createLane('lane-b');
helper.fixtures.createComponentBarFoo();
helper.fixtures.addComponentBarFooAsDir();
helper.command.snapAllComponentsWithoutBuild();

beforeSwitchingBack = helper.scopeHelper.cloneLocalScope();
});
it('lane-a should not contain components from main', () => {
const lane = helper.command.showOneLaneParsed('lane-a');
expect(lane.components).to.have.lengthOf(1);
});
it('laneB object should include components from laneA, but not from main', () => {
const lane = helper.command.showOneLaneParsed('lane-b');
expect(lane.components).to.have.lengthOf(2);
});
it('bit list should show all components available to lane-b', () => {
const list = helper.command.listLocalScopeParsed();
expect(list).to.have.lengthOf(3);
});
describe('checking out to lane-a', () => {
let switchOutput;
before(() => {
switchOutput = helper.command.switchLocalLane('lane-a');
});
it('should indicate that it switched to the new lane', () => {
expect(switchOutput).to.have.string(
removeChalkCharacters('successfully set "lane-a" as the active lane') as string
);
});
// main components belong to lane-a only if they are snapped on lane-a, so utils/is-type
// doesn't belong to lane-a and should not appear as staged when on lane-a.
it('bit status should not show neither lane-b nor main components as staged', () => {
const staged = helper.command.getStagedIdsFromStatus();
expect(staged).to.deep.equal(['utils/is-string']);
const status = helper.command.status();
expect(status).to.not.have.string('bar/foo');
});
it('bit list should not show lane-b components', () => {
const list = helper.command.listParsed();
expect(list).to.have.lengthOf(2);
});
// @todo: test each one of the commands on bar/foo
});
describe('checking out from lane-b to main', () => {
before(() => {
helper.scopeHelper.getClonedLocalScope(beforeSwitchingBack);
helper.command.switchLocalLane('main');
});
it('bit list should only show main components', () => {
const list = helper.command.listParsed();
expect(list).to.have.lengthOf(1);
});
it('bit status should show only main components as staged', () => {
const staged = helper.command.getStagedIdsFromStatus();
expect(staged).to.deep.equal(['utils/is-type']);
const status = helper.command.status();
expect(status).to.not.have.string('bar/foo');
expect(status).to.not.have.string('utils/is-string');
});
});
describe('switching to lane-a then to main', () => {
before(() => {
helper.scopeHelper.getClonedLocalScope(beforeSwitchingBack);
helper.command.switchLocalLane('lane-a');
helper.command.switchLocalLane('main');
});
it('bit list should only show main components', () => {
const list = helper.command.listParsed();
expect(list).to.have.lengthOf(1);
});
it('bit status should show only main components as staged', () => {
const staged = helper.command.getStagedIdsFromStatus();
expect(staged).to.deep.equal(['utils/is-type']);
const status = helper.command.status();
expect(status).to.not.have.string('bar/foo');
expect(status).to.not.have.string('utils/is-string');
});
});
});
describe('main => lane => main => lane', () => {
before(() => {
helper.scopeHelper.setNewLocalAndRemoteScopes();
Expand Down
14 changes: 13 additions & 1 deletion scopes/lanes/lanes/create-lane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { Consumer } from '@teambit/legacy/dist/consumer';
import { ScopeMain } from '@teambit/scope';
// import { BitIds } from '@teambit/legacy/dist/bit-id';
import Lane, { LaneComponent } from '@teambit/legacy/dist/scope/models/lane';
import { isHash } from '@teambit/component-version';
import { Ref } from '@teambit/legacy/dist/scope/objects';

export async function createLane(
consumer: Consumer,
Expand All @@ -21,7 +23,17 @@ export async function createLane(
// when branching from one lane to another, copy components from the origin lane
// when branching from main, no need to copy anything
const currentLaneObject = await consumer.getCurrentLaneObject();
return currentLaneObject ? currentLaneObject.components : [];
if (!currentLaneObject) return [];
const laneComponents = currentLaneObject.components;
const workspaceIds = consumer.bitMap.getAllBitIds();
const laneComponentWithBitmapHead = laneComponents.map(({ id, head }) => {
const bitmapHead = workspaceIds.searchWithoutVersion(id);
if (bitmapHead && isHash(bitmapHead.version)) {
return { id, head: Ref.from(bitmapHead.version as string) };
}
return { id, head };
});
return laneComponentWithBitmapHead;
};

const forkedFrom = await getLaneOrigin(consumer);
Expand Down

0 comments on commit a6f1099

Please sign in to comment.