Skip to content

Commit

Permalink
fix(status): avoid showing components as pending-update when their re…
Browse files Browse the repository at this point in the history
…mote-lane is empty (#7138)

currently, if the remote-lane doesn't exist, or it doesn't have this
specific component, it falls back to the either the forked-lane or to
the `component.head`. For bit-status or bit-checkout-head this is
incorrect. The comparison needs to be against the remote-lane, and if it
doesn't exist there, it should assume that the local is ahead.
  • Loading branch information
davidfirst authored Mar 10, 2023
1 parent 9b26590 commit 249e546
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 16 deletions.
22 changes: 22 additions & 0 deletions e2e/harmony/lanes/bit-import-on-lanes.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,4 +262,26 @@ describe('bit lane command', function () {
expect(helper.command.getHead('comp1')).to.equal(secondSnapMain, 'main was not updated');
});
});
describe('import previous version from main when on a lane', () => {
before(() => {
helper.scopeHelper.setNewLocalAndRemoteScopes();
helper.fixtures.populateComponents(1, false);
helper.command.tagAllWithoutBuild();
helper.command.tagAllWithoutBuild('--unmodified');
helper.command.export();

helper.scopeHelper.reInitLocalScope();
helper.scopeHelper.addRemoteScope();
helper.command.createLane();
helper.command.importComponent('[email protected]');
helper.command.snapAllComponentsWithoutBuild('--unmodified');
});
// previous bug showed this component in the pending-updates section.
// it was because the calculation whether it's up-to-date was based also on the component-head.
// it should be based on the remote-lane object only.
it('bit status should not show the component as pending-updates because it does not exits on the remote lane', () => {
const status = helper.command.statusJson();
expect(status.outdatedComponents).to.have.lengthOf(0);
});
});
});
52 changes: 36 additions & 16 deletions src/scope/models/model-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ export default class Component extends BitObject {
* when checked out to a lane, this prop is either Ref or null. otherwise (when on main), this prop is undefined.
*/
laneHeadRemote?: Ref | null;

/**
* when checked out to a lane, calculate what should be the head on the remote.
* if the laneHeadRemote is null, for example, when the lane is new, then used the the lane it was forked from.
* it no head is found on the lane/forked, then use the component.head.
*/
calculatedRemoteHeadWhenOnLane?: Ref | null;

laneId?: LaneId; // doesn't get saved in the scope.
laneDataIsPopulated = false; // doesn't get saved in the scope, used to improve performance of loading the lane data
schema: string | undefined;
Expand Down Expand Up @@ -280,7 +288,7 @@ export default class Component extends BitObject {
*/
async setDivergeData(repo: Repository, throws = true, fromCache = true): Promise<void> {
if (!this.divergeData || !fromCache) {
const remoteHead = (this.laneId ? this.laneHeadRemote : this.remoteHead) || null;
const remoteHead = (this.laneId ? this.calculatedRemoteHeadWhenOnLane : this.remoteHead) || null;
this.divergeData = await getDivergeData({
repo,
modelComponent: this,
Expand All @@ -301,21 +309,27 @@ export default class Component extends BitObject {
async populateLocalAndRemoteHeads(repo: Repository, lane: Lane | null) {
this.setLaneHeadLocal(lane);
if (lane) this.laneId = lane.toLaneId();
if (this.scope) {
if (lane) {
const getRemoteToCheck = () => {
if (!lane.isNew) return lane.toLaneId();
if (lane.forkedFrom) return lane.forkedFrom;
return LaneId.from(DEFAULT_LANE, this.scope as string);
};
const remoteToCheck = getRemoteToCheck();
// if no remote-ref was found, because it's checked out to a lane, it's safe to assume that
// this.head should be on the original-remote. hence, FetchMissingHistory will retrieve it on lane-remote
this.laneHeadRemote = (await repo.remoteLanes.getRef(remoteToCheck, this.toBitId())) || this.head;
}
// we need also the remote head of main, otherwise, the diverge-data assumes all versions are local
this.remoteHead = await repo.remoteLanes.getRef(LaneId.from(DEFAULT_LANE, this.scope), this.toBitId());
if (!this.scope) {
return; // no remote to update. it's local.
}
this.remoteHead = await repo.remoteLanes.getRef(LaneId.from(DEFAULT_LANE, this.scope), this.toBitId());
if (!lane) {
return;
}
this.laneHeadRemote = lane.isNew ? null : await repo.remoteLanes.getRef(lane.toLaneId(), this.toBitId());

const calculateRemote = async () => {
if (this.laneHeadRemote) return this.laneHeadRemote;
if (lane.isNew && lane.forkedFrom) {
const headFromFork = await repo.remoteLanes.getRef(lane.forkedFrom, this.toBitId());
if (headFromFork) return headFromFork;
}
// if no remote-ref was found, because it's checked out to a lane, it's safe to assume that
// this.head should be on the original-remote. hence, FetchMissingHistory will retrieve it on lane-remote
return this.remoteHead || this.head;
};

this.calculatedRemoteHeadWhenOnLane = await calculateRemote();
}

setLaneHeadLocal(lane: Lane | null) {
Expand Down Expand Up @@ -396,6 +410,12 @@ export default class Component extends BitObject {
}

// either a user is on main or a lane, check whether the remote is ahead of the local
if (this.laneId && !this.laneHeadRemote) {
// when on a lane, setDivergeData is using the `this.calculatedRemoteHeadWhenOnLane`,
// which takes into account main-head and forked-head. here, we don't want this. we care only about the
// remote-lane head.
return latestLocally;
}
await this.setDivergeData(repo, false);
const divergeData = this.getDivergeData();
return divergeData.isTargetAhead() ? remoteHead.toString() : latestLocally;
Expand All @@ -415,7 +435,7 @@ export default class Component extends BitObject {
isLatestGreaterThan(version: string | null | undefined): boolean {
if (!version) throw TypeError('isLatestGreaterThan expect to get a Version');
const latest = this.getHeadRegardlessOfLaneAsTagOrHash(true);
if (this.isEmpty() && !this.laneHeadRemote) {
if (this.isEmpty() && !this.calculatedRemoteHeadWhenOnLane) {
return false; // in case a snap was created on another lane
}
if (isTag(latest) && isTag(version)) {
Expand Down

0 comments on commit 249e546

Please sign in to comment.