mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-12 23:32:55 +01:00
Initial commit
Co-Authored-By: Eric Tuvesson <eric.tuvesson@gmail.com> Co-Authored-By: mikaeltellhed <2311083+mikaeltellhed@users.noreply.github.com> Co-Authored-By: kotte <14197736+mrtamagotchi@users.noreply.github.com> Co-Authored-By: Anders Larsson <64838990+anders-topp@users.noreply.github.com> Co-Authored-By: Johan <4934465+joolsus@users.noreply.github.com> Co-Authored-By: Tore Knudsen <18231882+torekndsn@users.noreply.github.com> Co-Authored-By: victoratndl <99176179+victoratndl@users.noreply.github.com>
This commit is contained in:
103
packages/noodl-git/src/merge-strategy.ts
Normal file
103
packages/noodl-git/src/merge-strategy.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { IMergeTreeEntry, MergeTreeError } from './core/models/merge';
|
||||
import { getFileContents } from './core/cat-file';
|
||||
import { addAll } from './core/add';
|
||||
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
function tryParseJson(str: string) {
|
||||
try {
|
||||
return JSON.parse(str);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
type JSON_OBJECT = {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
/** Merge the project.json file */
|
||||
export type MergeStrategyFunc = (ancestors: JSON_OBJECT, ours: JSON_OBJECT, theirs: JSON_OBJECT) => JSON_OBJECT;
|
||||
|
||||
/**
|
||||
* Defines the Noodl custom merge strategy.
|
||||
*/
|
||||
export class MergeStrategy {
|
||||
constructor(
|
||||
public readonly repositoryDir: string,
|
||||
private readonly mergeProject: MergeStrategyFunc,
|
||||
public readonly strategy: 'our' | 'their' = 'our'
|
||||
) {}
|
||||
|
||||
public async solveConflicts(tree: MergeTreeError) {
|
||||
const projectJsonFiles = new Set<IMergeTreeEntry>(); //handle repos with multiple noodl projects
|
||||
const otherFiles = new Set<IMergeTreeEntry>();
|
||||
|
||||
for (const entry of tree.conflictedEntries) {
|
||||
if (entry.hasConflicts) {
|
||||
const parent = entry.base || entry.our || entry.their;
|
||||
if (parent.path === 'project.json' || parent.path.slice(-13) === '/project.json') {
|
||||
projectJsonFiles.add(entry);
|
||||
} else {
|
||||
otherFiles.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve conflicts
|
||||
const projectPromises = Array.from(projectJsonFiles).map((x) => this.resolveProjectJsonConflict(x));
|
||||
const otherFilePromises = Array.from(otherFiles).map((x) => this.resolveFileConflict(x));
|
||||
|
||||
const allPromises = [...projectPromises, ...otherFilePromises];
|
||||
await Promise.all(allPromises);
|
||||
|
||||
// Mark all the file as resolved resolution
|
||||
await addAll(this.repositoryDir);
|
||||
|
||||
// Return all files that were resolved
|
||||
return [...projectJsonFiles, ...otherFiles];
|
||||
}
|
||||
|
||||
private async resolveFileConflict(entry: IMergeTreeEntry) {
|
||||
const parent = entry.base || entry.our || entry.their;
|
||||
console.log('resolveFileConflict', parent.path);
|
||||
|
||||
const targetEntry = entry[this.strategy];
|
||||
console.log('vcs: resolving file conflict', targetEntry.path, 'using', this.strategy);
|
||||
const ourBlob = await getFileContents(this.repositoryDir, targetEntry.sha);
|
||||
|
||||
const absolutePath = path.join(this.repositoryDir, parent.path);
|
||||
// Write our version of the file
|
||||
await fs.promises.writeFile(absolutePath, ourBlob);
|
||||
}
|
||||
|
||||
private async resolveProjectJsonConflict(entry: IMergeTreeEntry) {
|
||||
const parent = entry.base || entry.our || entry.their;
|
||||
console.log('resolveProjectJsonConflict', parent.path);
|
||||
|
||||
const results = await Promise.all([
|
||||
entry.base ? getFileContents(this.repositoryDir, entry.base.sha) : Promise.resolve('{}'),
|
||||
entry.our ? getFileContents(this.repositoryDir, entry.our.sha) : Promise.resolve('{}'),
|
||||
entry.their ? getFileContents(this.repositoryDir, entry.their.sha) : Promise.resolve('{}')
|
||||
]);
|
||||
|
||||
const ancestor = tryParseJson(results[0].toString());
|
||||
const ours = tryParseJson(results[1].toString());
|
||||
const theirs = tryParseJson(results[2].toString());
|
||||
|
||||
if (!ancestor) {
|
||||
throw new Error("Failed to solve project.json conflict. Couldn't parse ancestor: " + results[0].toString());
|
||||
}
|
||||
if (!ours) {
|
||||
throw new Error("Failed to solve project.json conflict. Couldn't parse ours: " + results[1].toString());
|
||||
} else if (!theirs) {
|
||||
throw new Error("Failed to solve project.json conflict. Couldn't parse theirs: " + results[2].toString());
|
||||
}
|
||||
|
||||
const mergedResult = this.mergeProject(ancestor, ours, theirs);
|
||||
|
||||
const absolutePath = path.join(this.repositoryDir, parent.path);
|
||||
await fs.promises.writeFile(absolutePath, JSON.stringify(mergedResult));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user