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:
Michael Cartner
2024-01-26 11:52:55 +01:00
commit b9c60b07dc
2789 changed files with 868795 additions and 0 deletions

View File

@@ -0,0 +1,193 @@
import { IMergeTreeEntry, MergeTreeResult } from './models/merge';
import { ComputedAction } from './models/computed-action';
interface IBlobSource {
readonly type: string;
readonly path: string;
readonly sha: string;
readonly mode: string;
}
function updateCurrentMergeEntry(
entry: IMergeTreeEntry | undefined,
context: string,
blobSource: IBlobSource
): IMergeTreeEntry {
const currentMergeEntry = entry || {
context,
diff: ''
};
const blob = {
sha: blobSource.sha,
mode: blobSource.mode,
path: blobSource.path
};
switch (blobSource.type) {
case 'base':
return {
...currentMergeEntry,
base: blob
};
case 'result':
return {
...currentMergeEntry,
result: blob
};
case 'our':
return {
...currentMergeEntry,
our: blob
};
case 'their':
return {
...currentMergeEntry,
their: blob
};
default:
return currentMergeEntry;
}
}
// the merge-tree output is a collection of entries like this
//
// changed in both
// base 100644 f69fbc5c40409a1db7a3f8353bfffe46a21d6054 atom/browser/resources/mac/Info.plist
// our 100644 9094f0f7335edf833d51f688851e6a105de60433 atom/browser/resources/mac/Info.plist
// their 100644 2dd8bc646cff3869557549a39477e30022e6cfdd atom/browser/resources/mac/Info.plist
// @@ -17,9 +17,15 @@
// <key>CFBundleIconFile</key>
// <string>electron.icns</string>
// <key>CFBundleVersion</key>
// +<<<<<<< .our
// <string>4.0.0</string>
// <key>CFBundleShortVersionString</key>
// <string>4.0.0</string>
// +=======
// + <string>1.4.16</string>
// + <key>CFBundleShortVersionString</key>
// + <string>1.4.16</string>
// +>>>>>>> .their
// <key>LSApplicationCategoryType</key>
//<string>public.app-category.developer-tools</string>
// <key>LSMinimumSystemVersion</key>
// The first line for each entry is what I'm referring to as the the header
// This regex filters on the known entries that can appear
const contextHeaderRe = /^(merged|added in remote|removed in remote|changed in both|removed in local|added in both)$/;
// the rest of the header is made up of a number of entries formatted like this
//
// base 100644 f69fbc5c40409a1db7a3f8353bfffe46a21d6054 atom/browser/resources/mac/Info.plist
//
// this regex let's us extract the blob details - the filename may also change
// as part of the merge if files are moved or renamed
const blobEntryRe = /^\s{2}(result|our|their|base)\s+(\d{6})\s([0-9a-f]{40})\s(.+)$/;
/**
* Parse the Git output of a merge-tree command to identify whether it
* has detected any conflicts between the branches to be merged
*
* @param text the stdout from a `git merge-tree` command
*
*/
export function parseMergeTreeResult(text: string): MergeTreeResult {
const entries = new Array<IMergeTreeEntry>();
const lines = text.split('\n');
let mergeEntryHeader: string | undefined;
let currentMergeEntry: IMergeTreeEntry | undefined;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const headerMatch = contextHeaderRe.exec(line);
if (headerMatch != null) {
mergeEntryHeader = headerMatch[1];
// push the previous entry, if defined, into the array
if (currentMergeEntry != null) {
entries.push(currentMergeEntry);
currentMergeEntry = undefined;
}
continue;
}
// the next lines are a number of merge result entries
// pointing to blobs representing the source blob
// and the resulting blob generated by the merge
const blobMatch = blobEntryRe.exec(line);
if (blobMatch != null) {
const type = blobMatch[1];
const mode = blobMatch[2];
const sha = blobMatch[3];
const path = blobMatch[4];
const blob = {
type,
mode,
sha,
path
};
if (mergeEntryHeader == null) {
console.warn(`An unknown header was set while trying to parse the blob on line ${i}`);
continue;
}
switch (type) {
case 'base':
case 'result':
case 'our':
case 'their':
currentMergeEntry = updateCurrentMergeEntry(currentMergeEntry, mergeEntryHeader, blob);
break;
default:
throw new Error(`invalid state - unexpected entry ${type} found when parsing rows`);
}
continue;
}
if (currentMergeEntry == null) {
throw new Error(`invalid state - trying to append the diff to a merge entry that isn't defined on line ${i}`);
} else {
const currentDiff = currentMergeEntry.diff;
const newDiff = currentDiff + line + '\n';
currentMergeEntry = {
...currentMergeEntry,
diff: newDiff
};
const lineHasConflictMarker =
line.startsWith('+<<<<<<<') || line.startsWith('+=======') || line.startsWith('+>>>>>>>');
if (lineHasConflictMarker) {
currentMergeEntry = {
...currentMergeEntry,
hasConflicts: true
};
}
}
}
// ensure the last entry is pushed onto the array
if (currentMergeEntry != null) {
entries.push(currentMergeEntry);
currentMergeEntry = undefined;
}
const entriesWithConflicts = entries.filter((e) => e.hasConflicts || false);
if (entriesWithConflicts.length > 0) {
return {
kind: ComputedAction.Conflicts,
conflictedFiles: entriesWithConflicts.length,
conflictedEntries: entriesWithConflicts
};
} else {
return { kind: ComputedAction.Clean, entries };
}
}