Skip to content

Commit

Permalink
fix, set the parent according to the currently used version, not the …
Browse files Browse the repository at this point in the history
…head (#7073)
  • Loading branch information
davidfirst authored Feb 24, 2023
1 parent 3d8783c commit 14f44da
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 77 deletions.
44 changes: 35 additions & 9 deletions e2e/harmony/lanes/diverged-from-remote-lane.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,55 @@ chai.use(require('chai-fs'));
describe('local is diverged from the remote', function () {
this.timeout(0);
let helper: Helper;
let beforeDiverge: string;
before(() => {
helper = new Helper();
helper.scopeHelper.setNewLocalAndRemoteScopes();
helper.command.createLane();
helper.fixtures.populateComponents(1, false);
helper.command.snapAllComponentsWithoutBuild();
helper.command.export();
const beforeDiverge = helper.scopeHelper.cloneLocalScope();
beforeDiverge = helper.scopeHelper.cloneLocalScope();
helper.command.snapAllComponentsWithoutBuild('--unmodified');
helper.command.export();
helper.scopeHelper.getClonedLocalScope(beforeDiverge);
helper.command.snapAllComponentsWithoutBuild('--unmodified');
helper.command.import();
});
after(() => {
helper.scopeHelper.destroy();
});
it('bit status should show it as merge-pending', () => {
const status = helper.command.statusJson();
expect(status.mergePendingComponents).to.have.lengthOf(1);
describe('snap first then import', () => {
before(() => {
helper.scopeHelper.getClonedLocalScope(beforeDiverge);
helper.command.snapAllComponentsWithoutBuild('--unmodified');
helper.command.import();
});
it('bit status should show it as merge-pending', () => {
const status = helper.command.statusJson();
expect(status.mergePendingComponents).to.have.lengthOf(1);
});
it('bit reset should not throw', () => {
expect(() => helper.command.untagAll()).to.not.throw();
});
});
it('bit reset should not throw', () => {
expect(() => helper.command.untagAll()).to.not.throw();
describe('import first then snap', () => {
let currentSnap: string;
before(() => {
helper.scopeHelper.getClonedLocalScope(beforeDiverge);
currentSnap = helper.command.getHeadOfLane('dev', 'comp1');
helper.command.import();
helper.command.snapAllComponentsWithoutBuild('--unmodified');
});
// previous bug was using the head on the lane as the parent.
it('parent of the snap should be the previous local snap, not the remote snap', () => {
const cmp = helper.command.catComponent('comp1@latest');
const parent = cmp.parents[0];
expect(parent).to.equal(currentSnap);
const remoteSnap = helper.general.getRemoteHead('comp1', 'dev');
expect(parent).to.not.equal(remoteSnap);
});
it('bit status should show it as merge-pending', () => {
const status = helper.command.statusJson();
expect(status.mergePendingComponents).to.have.lengthOf(1);
});
});
});

Expand Down
4 changes: 1 addition & 3 deletions scopes/component/merging/merging.main.runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -617,10 +617,8 @@ other: ${otherLaneHead.toString()}`);
if (!localLane) throw new Error('localLane must be defined when resolvedUnrelated');
if (!resolvedUnrelated?.head) throw new Error('resolvedUnrelated must have head prop');
localLane.addComponent({ id, head: resolvedUnrelated.head });
const head = modelComponent.getRef(currentComponent.id.version as string);
if (!head) throw new Error(`unable to get the head for resolved-unrelated ${id.toString()}`);
unmergedComponent.laneId = localLane.toLaneId();
unmergedComponent.head = head;
unmergedComponent.head = resolvedUnrelated.head;
unmergedComponent.unrelated = true;
consumer.scope.objects.unmergedComponents.addEntry(unmergedComponent);
return { id, filesStatus };
Expand Down
15 changes: 11 additions & 4 deletions scopes/component/snapping/snapping.main.runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import ImporterAspect, { ImporterMain } from '@teambit/importer';
import { ExportAspect, ExportMain } from '@teambit/export';
import UnmergedComponents from '@teambit/legacy/dist/scope/lanes/unmerged-components';
import { ComponentID } from '@teambit/component-id';
import { BitObject, Repository } from '@teambit/legacy/dist/scope/objects';
import { BitObject, Ref, Repository } from '@teambit/legacy/dist/scope/objects';
import {
ArtifactFiles,
ArtifactSource,
Expand Down Expand Up @@ -700,15 +700,22 @@ there are matching among unmodified components thought. consider using --unmodif
const { version, files } = await this.scope.legacyScope.sources.consumerComponentToVersion(source);
this.objectsRepo.add(version);
if (!source.version) throw new Error(`addSource expects source.version to be set`);
component.addVersion(version, source.version, lane, this.objectsRepo);
component.addVersion(version, source.version, lane, this.objectsRepo, source.previouslyUsedVersion);

const unmergedComponent = consumer.scope.objects.unmergedComponents.getEntry(component.name);
if (unmergedComponent) {
if (unmergedComponent.unrelated) {
this.logger.debug(
`sources.addSource, unmerged component "${component.name}". adding an unrelated entry ${unmergedComponent.head.hash}`
);
version.unrelated = { head: unmergedComponent.head, laneId: unmergedComponent.laneId };
if (!source.previouslyUsedVersion) {
throw new Error(
`source.previouslyUsedVersion must be set for ${component.name} because it's unrelated resolved.`
);
}
const unrelatedHead = Ref.from(source.previouslyUsedVersion);
version.unrelated = { head: unrelatedHead, laneId: unmergedComponent.laneId };
version.addAsOnlyParent(unmergedComponent.head);
} else {
// this is adding a second parent to the version. the order is important. the first parent is coming from the current-lane.
version.addParent(unmergedComponent.head);
Expand Down Expand Up @@ -736,7 +743,7 @@ there are matching among unmodified components thought. consider using --unmodif
const { version, files } = await this.scope.legacyScope.sources.consumerComponentToVersion(source);
objectRepo.add(version);
if (!source.version) throw new Error(`addSource expects source.version to be set`);
component.addVersion(version, source.version, lane, objectRepo);
component.addVersion(version, source.version, lane, objectRepo, source.previouslyUsedVersion);
objectRepo.add(component);
files.forEach((file) => objectRepo.add(file.file));
if (artifacts) artifacts.forEach((file) => objectRepo.add(file.source));
Expand Down
73 changes: 37 additions & 36 deletions scopes/component/snapping/tag-model-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ function updateDependenciesVersions(

function setHashes(componentsToTag: ConsumerComponent[]): void {
componentsToTag.forEach((componentToTag) => {
componentToTag.version = sha1(v4());
componentToTag.setNewVersion(sha1(v4()));
});
}

Expand All @@ -111,44 +111,45 @@ async function setFutureVersions(
const isAutoTag = autoTagIds.hasWithoutVersion(componentToTag.id);
const modelComponent = await scope.sources.findOrAddComponent(componentToTag);
const nextVersion = componentToTag.componentMap?.nextVersion?.version;
componentToTag.previouslyUsedVersion = componentToTag.version;
if (tagDataPerComp) {
const tagData = tagDataPerComp.find((t) => t.componentId._legacy.isEqualWithoutVersion(componentToTag.id));
if (!tagData) throw new Error(`tag-data is missing for ${componentToTag.id.toStringWithoutVersion()}`);
if (!tagData.versionToTag)
throw new Error(`tag-data.TagResults is missing for ${componentToTag.id.toStringWithoutVersion()}`);
const exactVersionOrReleaseType = getValidVersionOrReleaseType(tagData.versionToTag);
componentToTag.version = modelComponent.getVersionToAdd(
exactVersionOrReleaseType.releaseType,
exactVersionOrReleaseType.exactVersion,
undefined,
tagData.prereleaseId
);
} else if (nextVersion && persist) {
const exactVersionOrReleaseType = getValidVersionOrReleaseType(nextVersion);
componentToTag.version = modelComponent.getVersionToAdd(
exactVersionOrReleaseType.releaseType,
exactVersionOrReleaseType.exactVersion,
undefined,
componentToTag.componentMap?.nextVersion?.preRelease
);
} else if (isAutoTag) {
// auto-tag always bumped as patch unless it's pre-release
if (isPreReleaseLike) {
componentToTag.version = soft
? releaseType
: modelComponent.getVersionToAdd(releaseType, exactVersion, incrementBy, preReleaseId);
} else {
componentToTag.version = soft
? 'patch'
: modelComponent.getVersionToAdd('patch', undefined, incrementBy, preReleaseId);
const getNewVersion = (): string => {
if (tagDataPerComp) {
const tagData = tagDataPerComp.find((t) => t.componentId._legacy.isEqualWithoutVersion(componentToTag.id));
if (!tagData) throw new Error(`tag-data is missing for ${componentToTag.id.toStringWithoutVersion()}`);
if (!tagData.versionToTag)
throw new Error(`tag-data.TagResults is missing for ${componentToTag.id.toStringWithoutVersion()}`);
const exactVersionOrReleaseType = getValidVersionOrReleaseType(tagData.versionToTag);
return modelComponent.getVersionToAdd(
exactVersionOrReleaseType.releaseType,
exactVersionOrReleaseType.exactVersion,
undefined,
tagData.prereleaseId
);
}
if (nextVersion && persist) {
const exactVersionOrReleaseType = getValidVersionOrReleaseType(nextVersion);
return modelComponent.getVersionToAdd(
exactVersionOrReleaseType.releaseType,
exactVersionOrReleaseType.exactVersion,
undefined,
componentToTag.componentMap?.nextVersion?.preRelease
);
}
if (isAutoTag) {
// auto-tag always bumped as patch unless it's pre-release
if (isPreReleaseLike) {
return soft
? (releaseType as string)
: modelComponent.getVersionToAdd(releaseType, exactVersion, incrementBy, preReleaseId);
}
return soft ? 'patch' : modelComponent.getVersionToAdd('patch', undefined, incrementBy, preReleaseId);
}
} else {
const versionByEnteredId = getVersionByEnteredId(ids, componentToTag, modelComponent);
componentToTag.version = soft
? versionByEnteredId || exactVersion || releaseType
return soft
? versionByEnteredId || exactVersion || (releaseType as string)
: versionByEnteredId || modelComponent.getVersionToAdd(releaseType, exactVersion, incrementBy, preReleaseId);
}
};
const newVersion = getNewVersion();
componentToTag.setNewVersion(newVersion);
})
);
}
Expand Down
9 changes: 2 additions & 7 deletions scopes/lanes/lanes/lanes.docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,6 @@ The `LaneId` _always_ has these two props.

Currently, if it imports with no-deps, it doesn't ask for parents, if it imports with deps it imports with all parents. It is originated from src/api/scope/lib/fetch.ts: `const collectParents = !noDependencies;`. We need to make a decision here.

### Model changes

- `Version.parents`. This is added only if the lanes feature is enabled. (it doesn't change the hash).
- `Component.head` This is added only if the lanes feature is enabled. (it doesn't change the hash).

### Hash

- Snap's hash is generated by a UUID and then converted into sha1. `sha1(v4())`.
Expand All @@ -62,7 +57,7 @@ More places that stores lanes related data:

Summary of when/what lanes data is saved per command:

- `bit lane create`: creates a new lane-object and creates a new lane record in scope.json.
- `bit lane create`: creates a new lane-object and creates a new lane record in scope.json and .bitmap.
- `bit snap`: adds an entry to the lane-object.
- `bit export`: pushes the lane-object to the remote. On the remote, the lane-object is created or updated/merged.
- `bit switch --remote`: 1) creates/updates lane-object in the scope. 2) creates/updates remote-lane. the remote-lane is updated also for main.
Expand Down Expand Up @@ -115,7 +110,7 @@ To make this process easier, there are some tools that can help. keep reading.
#### source code merges/conflicts

To find out when a change was introduced to a file or when a file was added/removed, run `bit log-file <file-path>`.
It might be helpful to create a new temorary workspace, import the other lane and run the same there.
It might be helpful to create a new temporary workspace, import the other lane and run the same there.
This way, you get the full picture from both lanes when a change was introduced, and you can see whether it happened before and after the split from the base-snap.
Obviously, `bit log <comp-id> --one-line` is helpful here to see the distance from each lane from the base-snap.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,10 +251,10 @@ to bypass this error, use --skip-new-scope-validation flag (not recommended. it
const modelComponent = await this.scope.legacyScope.getModelComponent(legacyComp.id);
if (this.updateDepsOptions.tag) {
const { releaseType, exactVersion } = getValidVersionOrReleaseType(depUpdateItem.versionToTag || 'patch');
legacyComp.version = modelComponent.getVersionToAdd(releaseType, exactVersion);
legacyComp.setNewVersion(modelComponent.getVersionToAdd(releaseType, exactVersion));
} else {
// snap is the default
legacyComp.version = modelComponent.getSnapToAdd();
legacyComp.setNewVersion();
}
});
}
Expand Down
8 changes: 7 additions & 1 deletion src/consumer/component/consumer-component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import fs from 'fs-extra';
import { v4 } from 'uuid';
import * as path from 'path';
import R from 'ramda';
import { IssuesList } from '@teambit/component-issues';
Expand All @@ -18,7 +19,7 @@ import logger from '../../logger/logger';
import ComponentWithDependencies from '../../scope/component-dependencies';
import { ScopeListItem } from '../../scope/models/model-component';
import Version, { DepEdge, Log } from '../../scope/models/version';
import { pathNormalizeToLinux } from '../../utils';
import { pathNormalizeToLinux, sha1 } from '../../utils';
import { PathLinux, PathOsBased, PathOsBasedRelative } from '../../utils/path';
import ComponentMap from '../bit-map/component-map';
import { IgnoredDirectory } from '../component-ops/add-components/exceptions/ignored-directory';
Expand Down Expand Up @@ -257,6 +258,11 @@ export default class Component {
this.devDependencies = new Dependencies(devDependencies);
}

setNewVersion(version = sha1(v4())) {
this.previouslyUsedVersion = this.version;
this.version = version;
}

getFileExtension(): string {
switch (this.lang) {
case DEFAULT_LANGUAGE:
Expand Down
2 changes: 1 addition & 1 deletion src/scope/component-ops/get-diverge-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ bit import ${modelComponent.id()} --objects`);

if (!commonSnapBeforeDiverge) {
const unmergedData = repo.unmergedComponents.getEntry(modelComponent.name);
const isUnrelatedFromUnmerged = unmergedData?.unrelated && unmergedData.head.isEqual(targetHead);
const isUnrelatedFromUnmerged = unmergedData?.unrelated && unmergedData.head.isEqual(localHead);
const isUnrelatedFromVersionObj = localVersion.unrelated?.isEqual(targetHead);
if (isUnrelatedFromUnmerged || isUnrelatedFromVersionObj) {
return new SnapsDistance(snapsOnSource, snapsOnTarget, undefined);
Expand Down
37 changes: 23 additions & 14 deletions src/scope/models/model-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { forEach, isEmpty, pickBy, mapValues } from 'lodash';
import { Mutex } from 'async-mutex';
import * as semver from 'semver';
import { versionParser, isHash, isTag } from '@teambit/component-version';
import { v4 } from 'uuid';
import { LaneId, DEFAULT_LANE } from '@teambit/lane-id';
import pMapSeries from 'p-map-series';
import { LegacyComponentLog } from '@teambit/legacy-component-log';
Expand All @@ -22,7 +21,7 @@ import GeneralError from '../../error/general-error';
import ShowDoctorError from '../../error/show-doctor-error';
import ValidationError from '../../error/validation-error';
import logger from '../../logger/logger';
import { getStringifyArgs, sha1 } from '../../utils';
import { getStringifyArgs } from '../../utils';
import findDuplications from '../../utils/array/find-duplications';
import ComponentObjects from '../component-objects';
import { SnapsDistance } from '../component-ops/snaps-distance';
Expand Down Expand Up @@ -567,11 +566,13 @@ export default class Component extends BitObject {
return hasSameVersions;
}

getSnapToAdd() {
return sha1(v4());
}

addVersion(version: Version, versionToAdd: string, lane: Lane | null, repo: Repository): string {
addVersion(
version: Version,
versionToAdd: string,
lane: Lane | null,
repo: Repository,
previouslyUsedVersion?: string
): string {
if (lane) {
if (isTag(versionToAdd)) {
throw new GeneralError(
Expand All @@ -580,10 +581,18 @@ export default class Component extends BitObject {
}
const currentBitId = this.toBitId();
const versionToAddRef = Ref.from(versionToAdd);
const existingComponentInLane = lane.getComponentByName(currentBitId);
const currentHead = (existingComponentInLane && existingComponentInLane.head) || this.getHead();
if (currentHead && !currentHead.isEqual(versionToAddRef)) {
version.addAsOnlyParent(currentHead);
const parent = previouslyUsedVersion ? this.getRef(previouslyUsedVersion) : null;
if (!parent) {
const existingComponentInLane = lane.getComponentByName(currentBitId);
const currentHead = (existingComponentInLane && existingComponentInLane.head) || this.getHead();
if (currentHead) {
throw new Error(
`component ${currentBitId.toString()} has a head (${currentHead.toString()}) but previouslyUsedVersion is empty`
);
}
}
if (parent && !parent.isEqual(versionToAddRef)) {
version.addAsOnlyParent(parent);
}
lane.addComponent({ id: currentBitId, head: versionToAddRef });

Expand All @@ -598,8 +607,8 @@ export default class Component extends BitObject {
const head = this.getHead();
if (
head &&
head.toString() !== versionToAdd && // happens with auto-snap
!this.hasTag(versionToAdd)
head.toString() !== versionToAdd &&
!this.hasTag(versionToAdd) // happens with auto-snap
) {
// happens with auto-tag
// if this is a tag and this tag exists, the same version was added before with a different hash.
Expand All @@ -609,7 +618,7 @@ export default class Component extends BitObject {
// @todo: fix it in a more elegant way
version.addAsOnlyParent(head);
}
if (!version.isLegacy) this.setHead(version.hash());
this.setHead(version.hash());
if (isTag(versionToAdd)) {
this.setVersion(versionToAdd, version.hash());
}
Expand Down

0 comments on commit 14f44da

Please sign in to comment.