@teambit/merging 1.0.676 → 1.0.678
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +4 -4
- package/dist/index.js +12 -72
- package/dist/index.js.map +1 -1
- package/dist/merge-cmd.d.ts +3 -19
- package/dist/merge-cmd.js +6 -121
- package/dist/merge-cmd.js.map +1 -1
- package/dist/merge-status-provider.d.ts +1 -1
- package/dist/merge-status-provider.js +4 -4
- package/dist/merge-status-provider.js.map +1 -1
- package/dist/merging.main.runtime.d.ts +3 -43
- package/dist/merging.main.runtime.js +10 -12
- package/dist/merging.main.runtime.js.map +1 -1
- package/package.json +28 -35
- package/dist/merge-files.d.ts +0 -31
- package/dist/merge-files.js +0 -86
- package/dist/merge-files.js.map +0 -1
- package/dist/merge-version/index.d.ts +0 -2
- package/dist/merge-version/index.js +0 -63
- package/dist/merge-version/index.js.map +0 -1
- package/dist/merge-version/merge-version.d.ts +0 -25
- package/dist/merge-version/merge-version.js +0 -76
- package/dist/merge-version/merge-version.js.map +0 -1
- package/dist/merge-version/three-way-merge.d.ts +0 -65
- package/dist/merge-version/three-way-merge.js +0 -254
- package/dist/merge-version/three-way-merge.js.map +0 -1
- package/merge-version/index.ts +0 -8
- package/merge-version/merge-version.ts +0 -41
- package/merge-version/three-way-merge.ts +0 -245
- /package/dist/{preview-1753993326673.js → preview-1754005301803.js} +0 -0
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
import { BitError } from '@teambit/bit-error';
|
|
2
|
-
import type { Source, Version, SourceFileModel } from '@teambit/objects';
|
|
3
|
-
import { sha1 } from '@teambit/toolbox.crypto.sha1';
|
|
4
|
-
import type { PathLinux, PathOsBased } from '@teambit/toolbox.path.path';
|
|
5
|
-
import { pathNormalizeToLinux } from '@teambit/toolbox.path.path';
|
|
6
|
-
import * as eol from '@teambit/toolbox.string.eol';
|
|
7
|
-
import type { MergeFileParams, MergeFileResult } from '../merge-files';
|
|
8
|
-
import { mergeFiles } from '../merge-files';
|
|
9
|
-
import type { ConsumerComponent as Component } from '@teambit/legacy.consumer-component';
|
|
10
|
-
import { SourceFile } from '@teambit/component.sources';
|
|
11
|
-
import type { Scope } from '@teambit/legacy.scope';
|
|
12
|
-
import { Tmp } from '@teambit/legacy.scope';
|
|
13
|
-
import { isEmpty } from 'lodash';
|
|
14
|
-
|
|
15
|
-
export type MergeResultsThreeWay = {
|
|
16
|
-
addFiles: Array<{
|
|
17
|
-
filePath: PathLinux;
|
|
18
|
-
fsFile: SourceFile;
|
|
19
|
-
}>;
|
|
20
|
-
removeFiles: Array<{
|
|
21
|
-
filePath: PathLinux;
|
|
22
|
-
}>;
|
|
23
|
-
remainDeletedFiles: Array<{
|
|
24
|
-
filePath: PathLinux;
|
|
25
|
-
}>;
|
|
26
|
-
deletedConflictFiles: Array<{
|
|
27
|
-
filePath: PathLinux;
|
|
28
|
-
fsFile?: SourceFile;
|
|
29
|
-
}>;
|
|
30
|
-
modifiedFiles: Array<{
|
|
31
|
-
filePath: PathLinux;
|
|
32
|
-
fsFile: SourceFile;
|
|
33
|
-
baseFile?: SourceFileModel;
|
|
34
|
-
otherFile: SourceFileModel;
|
|
35
|
-
output: string | null | undefined;
|
|
36
|
-
conflict: string | null | undefined;
|
|
37
|
-
isBinaryConflict?: boolean;
|
|
38
|
-
}>;
|
|
39
|
-
unModifiedFiles: Array<{
|
|
40
|
-
filePath: PathLinux;
|
|
41
|
-
fsFile: SourceFile;
|
|
42
|
-
}>;
|
|
43
|
-
overrideFiles: Array<{
|
|
44
|
-
filePath: PathLinux;
|
|
45
|
-
fsFile: SourceFile;
|
|
46
|
-
}>;
|
|
47
|
-
updatedFiles: Array<{
|
|
48
|
-
filePath: PathLinux;
|
|
49
|
-
otherFile: SourceFileModel;
|
|
50
|
-
content: Buffer;
|
|
51
|
-
}>;
|
|
52
|
-
hasConflicts: boolean;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* to do the actual merge we use git, specifically `merge-file` command, so we try to use the same
|
|
57
|
-
* terminology as git. From the command help:
|
|
58
|
-
* `git merge-file <current-file> <base-file> <other-file>
|
|
59
|
-
* git merge-file incorporates all changes that lead from the <base-file> to <other-file> into
|
|
60
|
-
* <current-file>. The result ordinarily goes into <current-file>.`
|
|
61
|
-
*
|
|
62
|
-
* see checkout-version.getBaseVersion() for a case when a component is modified and then the base-file is not the
|
|
63
|
-
* common file before other-file and current-file.
|
|
64
|
-
* otherwise, Git terminology pretty much reflects what we do here. current-file is the one that is currently written
|
|
65
|
-
* to the filesystem. other-file is the one the user wants to checkout to. base-file is the original file where both:
|
|
66
|
-
* base-file and other-file were originated from.
|
|
67
|
-
*/
|
|
68
|
-
export async function threeWayMerge({
|
|
69
|
-
scope,
|
|
70
|
-
otherComponent,
|
|
71
|
-
otherLabel,
|
|
72
|
-
currentComponent,
|
|
73
|
-
currentLabel,
|
|
74
|
-
baseComponent,
|
|
75
|
-
}: {
|
|
76
|
-
scope: Scope;
|
|
77
|
-
otherComponent: Version;
|
|
78
|
-
otherLabel: string;
|
|
79
|
-
currentComponent: Component;
|
|
80
|
-
currentLabel: string;
|
|
81
|
-
baseComponent: Version;
|
|
82
|
-
}): Promise<MergeResultsThreeWay> {
|
|
83
|
-
// baseFiles and currentFiles come from the model, therefore their paths include the
|
|
84
|
-
// sharedOriginallyDir. fsFiles come from the Fs, therefore their paths don't include the
|
|
85
|
-
// sharedOriginallyDir.
|
|
86
|
-
// option 1) strip sharedOriginallyDir from baseFiles and currentFiles. the problem is that the
|
|
87
|
-
// sharedDir can be different if the dependencies were changes for example, as a result, it won't
|
|
88
|
-
// be possible to compare between the files as the paths are different.
|
|
89
|
-
// in the previous it was implemented this way and caused a bug, which now has an e2e-test to
|
|
90
|
-
// block it. see https://github.com/teambit/bit/pull/2070 PR.
|
|
91
|
-
// option 2) add sharedOriginallyDir to the fsFiles. we must go with this option.
|
|
92
|
-
// one thing we have to change is the end-of-line, it should be set as LF, same way we do before
|
|
93
|
-
// saving the file as an object.
|
|
94
|
-
const baseFiles: SourceFileModel[] = baseComponent.files;
|
|
95
|
-
const otherFiles: SourceFileModel[] = otherComponent.files;
|
|
96
|
-
const currentFiles: SourceFile[] = currentComponent.cloneFilesWithSharedDir();
|
|
97
|
-
currentFiles.forEach((fsFile) => {
|
|
98
|
-
fsFile.contents = eol.lf(fsFile.contents) as Buffer;
|
|
99
|
-
});
|
|
100
|
-
const results: MergeResultsThreeWay = {
|
|
101
|
-
addFiles: [],
|
|
102
|
-
removeFiles: [],
|
|
103
|
-
remainDeletedFiles: [],
|
|
104
|
-
deletedConflictFiles: [],
|
|
105
|
-
modifiedFiles: [],
|
|
106
|
-
unModifiedFiles: [],
|
|
107
|
-
overrideFiles: [],
|
|
108
|
-
updatedFiles: [],
|
|
109
|
-
hasConflicts: false,
|
|
110
|
-
};
|
|
111
|
-
const getFileResult = async (fsFile: SourceFile, baseFile?: SourceFileModel, otherFile?: SourceFileModel) => {
|
|
112
|
-
const filePath: PathLinux = pathNormalizeToLinux(fsFile.relative);
|
|
113
|
-
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
|
|
114
|
-
const fsFileHash = sha1(fsFile.contents);
|
|
115
|
-
if (!otherFile) {
|
|
116
|
-
// if !otherFile && !baseFile, the file was created after the last tag, no need to do any
|
|
117
|
-
// calculation, the file should be added
|
|
118
|
-
if (!baseFile) {
|
|
119
|
-
results.addFiles.push({ filePath, fsFile });
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
const baseFileHash = baseFile.file.hash;
|
|
123
|
-
if (fsFileHash === baseFileHash) {
|
|
124
|
-
results.removeFiles.push({ filePath });
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
results.deletedConflictFiles.push({ filePath });
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
const otherFileHash = otherFile.file.hash;
|
|
131
|
-
if (fsFileHash === otherFileHash) {
|
|
132
|
-
// if fs === other, no need to take any action (regardless the base)
|
|
133
|
-
results.unModifiedFiles.push({ filePath, fsFile });
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
if (baseFile && fsFileHash === baseFile.file.hash) {
|
|
137
|
-
// the file has no local modification.
|
|
138
|
-
// the file currently in the fs, is not the same as the file we want to write (other).
|
|
139
|
-
// but no need to check whether it has conflicts because we always want to write the other.
|
|
140
|
-
const content = (await otherFile.file.load(scope.objects)) as Source;
|
|
141
|
-
results.updatedFiles.push({ filePath, otherFile, content: content.contents });
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
// it was changed in both, there is a chance for conflict. (regardless the base)
|
|
145
|
-
fsFile.label = currentLabel;
|
|
146
|
-
// @ts-ignore it's a hack to pass the data, version is not a valid attribute.
|
|
147
|
-
otherFile.label = otherLabel;
|
|
148
|
-
results.modifiedFiles.push({ filePath, fsFile, baseFile, otherFile, output: null, conflict: null });
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
await Promise.all(
|
|
152
|
-
currentFiles.map(async (fsFile) => {
|
|
153
|
-
const relativePath = pathNormalizeToLinux(fsFile.relative);
|
|
154
|
-
const baseFile = baseFiles.find((file) => file.relativePath === relativePath);
|
|
155
|
-
const otherFile = otherFiles.find((file) => file.relativePath === relativePath);
|
|
156
|
-
await getFileResult(fsFile, baseFile, otherFile);
|
|
157
|
-
})
|
|
158
|
-
);
|
|
159
|
-
const fsFilesPaths = currentFiles.map((fsFile) => pathNormalizeToLinux(fsFile.relative));
|
|
160
|
-
const baseFilesPaths = baseFiles.map((baseFile) => baseFile.relativePath);
|
|
161
|
-
const isOtherSameAsBase = (otherFile: SourceFileModel) => {
|
|
162
|
-
const baseFile = baseFiles.find((file) => file.relativePath === otherFile.relativePath);
|
|
163
|
-
if (!baseFile) throw new Error('isOtherSameAsBase expect the base to be there');
|
|
164
|
-
return baseFile.file.hash === otherFile.file.hash;
|
|
165
|
-
};
|
|
166
|
-
const deletedFromFs = otherFiles.filter(
|
|
167
|
-
(otherFile) =>
|
|
168
|
-
!fsFilesPaths.includes(otherFile.relativePath) &&
|
|
169
|
-
baseFilesPaths.includes(otherFile.relativePath) &&
|
|
170
|
-
isOtherSameAsBase(otherFile)
|
|
171
|
-
);
|
|
172
|
-
const deletedAndModified = otherFiles.filter(
|
|
173
|
-
(otherFile) =>
|
|
174
|
-
!fsFilesPaths.includes(otherFile.relativePath) &&
|
|
175
|
-
baseFilesPaths.includes(otherFile.relativePath) &&
|
|
176
|
-
!isOtherSameAsBase(otherFile)
|
|
177
|
-
);
|
|
178
|
-
const addedOnOther = otherFiles.filter(
|
|
179
|
-
(otherFile) => !fsFilesPaths.includes(otherFile.relativePath) && !baseFilesPaths.includes(otherFile.relativePath)
|
|
180
|
-
);
|
|
181
|
-
deletedFromFs.forEach((file) => results.remainDeletedFiles.push({ filePath: file.relativePath }));
|
|
182
|
-
deletedAndModified.forEach((file) => results.deletedConflictFiles.push({ filePath: file.relativePath }));
|
|
183
|
-
|
|
184
|
-
await Promise.all(
|
|
185
|
-
addedOnOther.map(async (file) => {
|
|
186
|
-
const fsFile = await SourceFile.loadFromSourceFileModel(file, scope.objects);
|
|
187
|
-
results.addFiles.push({ filePath: file.relativePath, fsFile });
|
|
188
|
-
})
|
|
189
|
-
);
|
|
190
|
-
await Promise.all(
|
|
191
|
-
deletedAndModified.map(async (file) => {
|
|
192
|
-
const fsFile = await SourceFile.loadFromSourceFileModel(file, scope.objects);
|
|
193
|
-
results.deletedConflictFiles.push({ filePath: file.relativePath, fsFile });
|
|
194
|
-
})
|
|
195
|
-
);
|
|
196
|
-
if (isEmpty(results.modifiedFiles)) return results;
|
|
197
|
-
|
|
198
|
-
const conflictResults = await getMergeResults(scope, results.modifiedFiles);
|
|
199
|
-
conflictResults.forEach((conflictResult: MergeFileResult) => {
|
|
200
|
-
const modifiedFile = results.modifiedFiles.find((file) => file.filePath === conflictResult.filePath);
|
|
201
|
-
if (!modifiedFile) throw new BitError(`unable to find ${conflictResult.filePath} in modified files array`);
|
|
202
|
-
modifiedFile.output = conflictResult.output;
|
|
203
|
-
modifiedFile.conflict = conflictResult.conflict;
|
|
204
|
-
modifiedFile.isBinaryConflict = conflictResult.isBinaryConflict;
|
|
205
|
-
if (conflictResult.conflict || conflictResult.isBinaryConflict) results.hasConflicts = true;
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
return results;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
async function getMergeResults(
|
|
212
|
-
scope: Scope,
|
|
213
|
-
modifiedFiles: MergeResultsThreeWay['modifiedFiles']
|
|
214
|
-
): Promise<MergeFileResult[]> {
|
|
215
|
-
const tmp = new Tmp(scope);
|
|
216
|
-
const conflictResultsP = modifiedFiles.map(async (modifiedFile) => {
|
|
217
|
-
// @ts-ignore AUTO-ADDED-AFTER-MIGRATION-PLEASE-FIX!
|
|
218
|
-
const fsFilePathP = tmp.save(modifiedFile.fsFile.contents);
|
|
219
|
-
const writeFile = async (file: SourceFileModel): Promise<PathOsBased> => {
|
|
220
|
-
const content = await file.file.load(scope.objects);
|
|
221
|
-
// @ts-ignore
|
|
222
|
-
return tmp.save(content.contents.toString());
|
|
223
|
-
};
|
|
224
|
-
const baseFilePathP = modifiedFile.baseFile ? writeFile(modifiedFile.baseFile) : tmp.save('');
|
|
225
|
-
const otherFilePathP = writeFile(modifiedFile.otherFile);
|
|
226
|
-
const [fsFilePath, baseFilePath, otherFilePath] = await Promise.all([fsFilePathP, baseFilePathP, otherFilePathP]);
|
|
227
|
-
const mergeFilesParams: MergeFileParams = {
|
|
228
|
-
filePath: modifiedFile.filePath,
|
|
229
|
-
currentFile: {
|
|
230
|
-
label: modifiedFile.fsFile.label,
|
|
231
|
-
path: fsFilePath,
|
|
232
|
-
},
|
|
233
|
-
baseFile: {
|
|
234
|
-
path: baseFilePath,
|
|
235
|
-
},
|
|
236
|
-
otherFile: {
|
|
237
|
-
// @ts-ignore
|
|
238
|
-
label: modifiedFile.otherFile.label,
|
|
239
|
-
path: otherFilePath,
|
|
240
|
-
},
|
|
241
|
-
};
|
|
242
|
-
return mergeFiles(mergeFilesParams);
|
|
243
|
-
});
|
|
244
|
-
return Promise.all(conflictResultsP);
|
|
245
|
-
}
|
|
File without changes
|