From 76f71f0cfe511be4d68c866d46c4e5fbdfb3fab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A1nos=20Veres?= Date: Tue, 20 Feb 2024 20:15:42 +0100 Subject: [PATCH] feat: reset --- packages/ogre/src/repository.test.ts | 12 +++-- packages/ogre/src/repository.ts | 65 +++++++++++++++++++++++----- 2 files changed, 63 insertions(+), 14 deletions(-) diff --git a/packages/ogre/src/repository.test.ts b/packages/ogre/src/repository.test.ts index ee5c007..d3f1a14 100644 --- a/packages/ogre/src/repository.test.ts +++ b/packages/ogre/src/repository.test.ts @@ -24,7 +24,7 @@ test("reconstruction", async (t) => { t.is( repo.ref("refs/heads/main"), firstStep, - "main is pointing at wrong commit" + "main is pointing at wrong commit", ); t.is(history.commits.length, 2, "incorrect # of commits"); @@ -37,7 +37,7 @@ test("reconstruction", async (t) => { t.is( sumChanges(history2.commits), changeEntries, - "incorrect # of changelog entries" + "incorrect # of changelog entries", ); }); @@ -72,7 +72,7 @@ test("reconstruct with 2 commits", async (t) => { t.is( sumChanges(history2.commits), changeEntries, - "incorrect # of changelog entries" + "incorrect # of changelog entries", ); }); @@ -107,6 +107,10 @@ test("diff is ok", async (t) => { t.is( diff.length, changeEntries, - `invalid # of change entries: ${JSON.stringify(diff)}` + `invalid # of change entries: ${JSON.stringify(diff)}`, ); }); + +// test("reset hard", async (t) => { +// // TODO: test reset feature +// }); diff --git a/packages/ogre/src/repository.ts b/packages/ogre/src/repository.ts index 16b3c5e..46baa07 100644 --- a/packages/ogre/src/repository.ts +++ b/packages/ogre/src/repository.ts @@ -59,6 +59,13 @@ export interface RepositoryObject { branch(): string; tag(tag: string): string; + + /** + * Moves the HEAD and the branch to a specific shaish (commit or tag) + * @param mode hard - discard changes + * @param shaish + */ + reset(mode?: "soft" | "hard", shaish?: string): void; } /** @@ -89,7 +96,9 @@ export class Repository private readonly original: T; data: T; + private observer: Observer; + private readonly refs: Map; private readonly commits: Commit[]; @@ -104,6 +113,32 @@ export class Repository this.observer = observe(this.data); } + reset( + mode: "soft" | "hard" | undefined = "hard", + shaish: string | undefined = REFS_HEAD_KEY, + ): void { + if (mode === "hard") { + unobserve(this.data, this.observer); + } + + const [commit] = shaishToCommit(shaish, this.refs, this.commits); + this.moveTo(commit); + + const refs = refsAtCommit(this.refs, commit); + // reset only moves heads and not tags + const moveableRefs = refs.filter((r) => + r.name.startsWith(headsRefPathPrefix), + ); + + for (const ref of moveableRefs) { + this.moveRef(ref.name, commit); + } + + if (mode === "hard") { + this.observer = observe(this.data); + } + } + branch(): string { const currentHeadRef = this.refs.get(REFS_HEAD_KEY); if (!currentHeadRef) { @@ -145,12 +180,12 @@ export class Repository const [commit, isRef, refKey] = shaishToCommit( shaish, this.refs, - this.commits + this.commits, ); this.moveTo(commit); this.moveRef( REFS_HEAD_KEY, - isRef && refKey !== undefined ? refKey : commit + isRef && refKey !== undefined ? refKey : commit, ); } } @@ -158,7 +193,7 @@ export class Repository async commit( message: string, author: string, - amend?: boolean + amend?: boolean, ): Promise { let parent = this.commitAtHead(); if (amend && !parent) { @@ -196,7 +231,7 @@ export class Repository }); const treeHash = Buffer.from( - compressSync(strToU8(JSON.stringify(this.data)), { level: 6, mem: 8 }) + compressSync(strToU8(JSON.stringify(this.data)), { level: 6, mem: 8 }), ).toString("base64"); const commit = { hash: sha, @@ -307,7 +342,7 @@ export class Repository throw new Error( `fatal: source type (${ source instanceof Repository ? "Repository" : "History" - }) not implemented` + }) not implemented`, ); } @@ -374,7 +409,7 @@ export class Repository throw new Error(`unreachable: HEAD not present`); } throw new Error( - `fatal: not a valid object name: '${getLastItem(headRef)}'` + `fatal: not a valid object name: '${getLastItem(headRef)}'`, ); } this.refs.set(refKey, { name: name, value: headCommit.hash }); @@ -430,7 +465,7 @@ const traverseAndCollectChangelog = (commit: Commit, commitsList: Commit[]) => { const mapPath = ( from: Commit, to: Commit, - commits: Commit[] + commits: Commit[], ): [isAncestor: boolean] => { let c: Commit | undefined = to; while (c !== undefined) { @@ -451,7 +486,7 @@ const mapPath = ( const commitAtRefIn = ( ref: string, references: Map, - commitsList: Commit[] + commitsList: Commit[], ) => { const reference = references.get(ref); if (!reference) { @@ -477,6 +512,16 @@ const commitAtRefIn = ( return undefined; }; +const refsAtCommit = (references: Map, commit: Commit) => { + const list: Array = []; + for (const [name, ref] of references.entries()) { + if (ref.value === commit.hash) { + list.push(ref); + } + } + return list; +}; + /** * Accepts a shaish expression (e.g. refs (branches, tags), commitSha) and returns * - a commit of type Commit @@ -486,7 +531,7 @@ const commitAtRefIn = ( const shaishToCommit = ( shaish: string, references: Map, - commitsList: Commit[] + commitsList: Commit[], ): [commit: Commit, isRef: boolean, ref: string | undefined] => { let sha = shaish; let isRef = false; @@ -557,7 +602,7 @@ export const validateRef = (name: string, oneLevel: boolean = true) => { * @param repository */ export const printChangeLog = ( - repository: RepositoryObject + repository: RepositoryObject, ) => { console.log("----------------------------------------------------------"); console.log(`Changelog at ${repository.head()}`);