Something went wrong. Try again.
A Tangled knot on CF Workers, backed by CF Artifacts
Something went wrong. Try again.
6.3 kB · 158 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159import { describe, it, expect } from 'vitest';import { execFileSync } from 'node:child_process';import { mkdtempSync, rmSync, writeFileSync, readFileSync } from 'node:fs';import { tmpdir } from 'node:os';import { join } from 'node:path';import { gitObject, gitTree, type GitObject } from '../src/git-objects';import { treePatches, renderPatches, fileWire, formatPatch, comparisonGraph,} from '../src/git-diff';function graph() { const objects = new Map<string, GitObject>(); const add = (o: GitObject) => (objects.set(o.oid, o), o.oid); const blob = (text: string | Uint8Array) => add(gitObject('blob', typeof text === 'string' ? Buffer.from(text) : text)); const tree = (entries: { name: string; mode: string; oid: string }[]) => add(gitTree(entries)); const commit = (root: string, parents: string[] = [], seconds = 1700000000) => add( gitObject( 'commit', Buffer.from( `tree ${root}\n${parents.map((p) => 'parent ' + p + '\n').join('')}author A <a@example.com> ${seconds} +0530\ncommitter A <a@example.com> ${seconds} +0530\n\nSubject\n\nBody\n`, ), ), ); return { objects, blob, tree, commit };}describe('bounded raw Git diff and comparison', () => { it('renders an applyable full-index patch with newline, additions, deletions and executable mode changes', () => { const g = graph(), oldBlob = g.blob('first\nsecond\n'), newBlob = g.blob('first\nchanged'), deleted = g.blob('delete\n'), added = g.blob('new\n'); const base = g.commit( g.tree([ { name: 'file.txt', mode: '100644', oid: oldBlob }, { name: 'deleted', mode: '100644', oid: deleted }, ]), ); const head = g.commit( g.tree([ { name: 'file.txt', mode: '100755', oid: newBlob }, { name: 'added', mode: '100644', oid: added }, ]), [base], ); const patches = treePatches(g.objects, base, head), raw = renderPatches(patches); expect(raw).toContain('\\ No newline at end of file'); expect(fileWire(patches.find((p) => p.path === 'file.txt')!).NewMode).toBe( 0o100755, ); const dir = mkdtempSync(join(tmpdir(), 'tangledart-diff-')); try { execFileSync('git', ['init', '--quiet', dir]); writeFileSync(join(dir, 'file.txt'), 'first\nsecond\n'); writeFileSync(join(dir, 'deleted'), 'delete\n'); execFileSync('git', ['-C', dir, 'add', '.']); execFileSync('git', ['-C', dir, 'apply', '--check'], { input: raw }); execFileSync('git', ['-C', dir, 'apply'], { input: raw }); expect( execFileSync('git', ['-C', dir, 'diff', '--numstat']).toString(), ).toContain('1\t1\tfile.txt'); } finally { rmSync(dir, { recursive: true, force: true }); } }); it('reports binary omission and gitlink changes without reading nonexistent gitlink objects', () => { const g = graph(), binary = g.blob(new Uint8Array([0, 255])), base = g.commit(g.tree([])), head = g.commit( g.tree([ { name: 'binary', mode: '100644', oid: binary }, { name: 'submodule', mode: '160000', oid: 'ab'.repeat(20) }, ]), [base], ); const patches = treePatches(g.objects, base, head); expect(fileWire(patches[0]).IsBinary).toBe(true); expect(renderPatches(patches)).toContain( 'Binary files /dev/null and b/binary differ', ); expect(renderPatches(patches)).toContain( 'Subproject commit ' + 'ab'.repeat(20), ); }); it('finds the non-tip common ancestor and emits only head-side non-merge commits', () => { const g = graph(), tree = g.tree([]), root = g.commit(tree, [], 1700000000), left = g.commit(tree, [root], 1700000001), right = g.commit(tree, [root], 1700000002), right2 = g.commit(tree, [right], 1700000003), merge = g.commit(tree, [right2, left], 1700000004); expect(comparisonGraph(g.objects, left, right2)).toEqual({ mergeBase: root, commits: [right, right2], }); expect(comparisonGraph(g.objects, left, merge)).toEqual({ mergeBase: left, commits: [right, right2], }); }); it('renders format-patch identity, RFC2822 offset and structured headers', () => { const g = graph(), root = g.commit(g.tree([])), result = formatPatch(g.objects, root, []); expect(result.wire.AuthorDate).toBe('2023-11-15T03:43:20+05:30'); expect(result.wire.RawHeaders.Date).toEqual([ 'Wed, 15 Nov 2023 03:43:20 +0530', ]); expect(result.wire.Title).toBe('Subject'); expect(result.wire.Body).toBe('Body'); expect(result.raw).toContain(`From ${root} Mon Sep 17 00:00:00 2001`); }); it('encodes reversible Git binary literal patches and respects the binary wire budget', () => { const g = graph(), before = new Uint8Array([0, 255, 1, 128]), after = new Uint8Array([0, 128, 2, 255, 3]), base = g.commit( g.tree([{ name: 'binary', mode: '100644', oid: g.blob(before) }]), ), head = g.commit( g.tree([{ name: 'binary', mode: '100644', oid: g.blob(after) }]), [base], ); const budget = { remaining: 10000, omitted: false }, patches = treePatches(g.objects, base, head, budget), raw = renderPatches(patches); expect(raw).toContain('GIT binary patch'); expect(budget.omitted).toBe(false); const dir = mkdtempSync(join(tmpdir(), 'tangledart-binary-')); try { execFileSync('git', ['init', '--quiet', dir]); writeFileSync(join(dir, 'binary'), before); execFileSync('git', ['-C', dir, 'add', '.']); execFileSync('git', ['-C', dir, 'apply', '--index'], { input: raw }); expect(new Uint8Array(readFileSync(join(dir, 'binary')))).toEqual(after); execFileSync('git', ['-C', dir, 'apply', '--index', '--reverse'], { input: raw, }); expect(new Uint8Array(readFileSync(join(dir, 'binary')))).toEqual(before); } finally { rmSync(dir, { recursive: true, force: true }); } const tiny = { remaining: 1, omitted: false }; expect(renderPatches(treePatches(g.objects, base, head, tiny))).toContain( 'Binary files', ); expect(tiny.omitted).toBe(true); });});