diff --git a/docs/ko/reference/Note/Methods/author.md b/docs/ko/reference/Note/Methods/author.md new file mode 100644 index 0000000..8d57cb7 --- /dev/null +++ b/docs/ko/reference/Note/Methods/author.md @@ -0,0 +1,38 @@ +# author + +노트 작성자를 가져와요 + +## 시그니처 + +```ts +class Note { + author(): Signature; +} +``` + +### 반환 값 + + \ No newline at end of file diff --git a/docs/ko/reference/Note/Methods/committer.md b/docs/ko/reference/Note/Methods/committer.md new file mode 100644 index 0000000..e47feba --- /dev/null +++ b/docs/ko/reference/Note/Methods/committer.md @@ -0,0 +1,38 @@ +# committer + +노트 커미터를 가져와요 + +## 시그니처 + +```ts +class Note { + committer(): Signature; +} +``` + +### 반환 값 + + \ No newline at end of file diff --git a/docs/ko/reference/Note/Methods/id.md b/docs/ko/reference/Note/Methods/id.md new file mode 100644 index 0000000..562328f --- /dev/null +++ b/docs/ko/reference/Note/Methods/id.md @@ -0,0 +1,21 @@ +# id + +노트 개체의 id를 가져와요 + +## 시그니처 + +```ts +class Note { + id(): string; +} +``` + +### 반환 값 + + \ No newline at end of file diff --git a/docs/ko/reference/Note/Methods/message.md b/docs/ko/reference/Note/Methods/message.md new file mode 100644 index 0000000..84d78f5 --- /dev/null +++ b/docs/ko/reference/Note/Methods/message.md @@ -0,0 +1,31 @@ +# message + +노트 메시지를 문자열로 가져와요. + +## 시그니처 + +```ts +class Note { + message(): string; +} +``` + +### 반환 값 + + + +### 에러 + + \ No newline at end of file diff --git a/docs/ko/reference/Repository/Methods/deleteNote.md b/docs/ko/reference/Repository/Methods/deleteNote.md new file mode 100644 index 0000000..92ee49f --- /dev/null +++ b/docs/ko/reference/Repository/Methods/deleteNote.md @@ -0,0 +1,104 @@ +# deleteNote + +개체에 대한 노트를 제거해요. + +`notesRef` 인수는 사용할 참조의 정규 이름이에요. +기본값은 "refs/notes/commits"예요. + +지정된 id는 노트를 제거할 Git 개체의 Oid예요. + +## 시그니처 + +```ts +class Repository { + deleteNote(id: string, options?: DeleteNoteOptions | null | undefined): void; +} +``` + +### 파라미터 + + \ No newline at end of file diff --git a/docs/ko/reference/Repository/Methods/findNote.md b/docs/ko/reference/Repository/Methods/findNote.md new file mode 100644 index 0000000..21fb701 --- /dev/null +++ b/docs/ko/reference/Repository/Methods/findNote.md @@ -0,0 +1,46 @@ +# findNote + +개체에 대한 노트를 읽어요. + +`notesRef` 인수는 사용할 기준 참조 이름이고, 기본값은 "refs/notes/commits"예요. + +지정된 id는 노트를 읽을 Git 개체의 Oid예요. + +## 시그니처 + +```ts +class Repository { + findNote(id: string, options?: FindNoteOptions | null | undefined): Note | null; +} +``` + +### 파라미터 + + + +### 반환 값 + + \ No newline at end of file diff --git a/docs/ko/reference/Repository/Methods/getNote.md b/docs/ko/reference/Repository/Methods/getNote.md new file mode 100644 index 0000000..63e0429 --- /dev/null +++ b/docs/ko/reference/Repository/Methods/getNote.md @@ -0,0 +1,57 @@ +# getNote + +개체에 대한 노트를 읽어요. + +`notesRef` 인수는 사용할 참조의 정식 이름이에요. +기본값은 "refs/notes/commits"예요. + +지정된 id는 노트를 읽을 Git 개체의 Oid예요. + +## 시그니처 + +```ts +class Repository { + getNote(id: string, options?: FindNoteOptions | null | undefined): Note; +} +``` + +### 파라미터 + + + +### 반환 값 + + + +### 에러 + + \ No newline at end of file diff --git a/docs/ko/reference/Repository/Methods/note.md b/docs/ko/reference/Repository/Methods/note.md new file mode 100644 index 0000000..6b16ca4 --- /dev/null +++ b/docs/ko/reference/Repository/Methods/note.md @@ -0,0 +1,123 @@ +# note + +개체에 대한 노트를 추가해요 + +`notesRef` 인수는 사용할 참조의 정규 이름이에요. +지정하지 않으면 기본값은 "refs/notes/commits"예요. `force`가 지정되면 +이전에 존재하던 노트가 덮어써져요. + +## 시그니처 + +```ts +class Repository { + note(oid: string, note: string, options?: CreateNoteOptions | null | undefined): string; +} +``` + +### 파라미터 + + + +### 반환 값 + + \ No newline at end of file diff --git a/docs/ko/reference/Repository/Methods/noteDefaultRef.md b/docs/ko/reference/Repository/Methods/noteDefaultRef.md new file mode 100644 index 0000000..eef4000 --- /dev/null +++ b/docs/ko/reference/Repository/Methods/noteDefaultRef.md @@ -0,0 +1,21 @@ +# noteDefaultRef + +이 리포지토리의 기본 노트 참조를 가져와요 + +## 시그니처 + +```ts +class Repository { + noteDefaultRef(): string; +} +``` + +### 반환 값 + + \ No newline at end of file diff --git a/docs/ko/reference/Repository/Methods/notes.md b/docs/ko/reference/Repository/Methods/notes.md new file mode 100644 index 0000000..bac156e --- /dev/null +++ b/docs/ko/reference/Repository/Methods/notes.md @@ -0,0 +1,46 @@ +# notes + +이 리포지토리에서 노트용 새 이터레이터를 만들어요. + +`notesRef` 인수는 사용할 참조의 표준 이름이고, +기본값은 "refs/notes/commits"예요. + +## 시그니처 + +```ts +class Repository { + notes(notesRef?: string | null | undefined): Notes; +} +``` + +### 파라미터 + + + +### 반환 값 + + + +## 예제 + +```ts +import { openRepository } from 'es-git'; + +const repo = await openRepository('.'); +for (const { noteId, annotatedId } of repo.notes()) { + const note = repo.getNote(noteId); + const commit = repo.getCommit(annotatedId); +} +``` \ No newline at end of file diff --git a/docs/reference/Note/Methods/author.md b/docs/reference/Note/Methods/author.md new file mode 100644 index 0000000..c14a98c --- /dev/null +++ b/docs/reference/Note/Methods/author.md @@ -0,0 +1,38 @@ +# author + +Get the note author + +## Signature + +```ts +class Note { + author(): Signature; +} +``` + +### Returns + + \ No newline at end of file diff --git a/docs/reference/Note/Methods/committer.md b/docs/reference/Note/Methods/committer.md new file mode 100644 index 0000000..eb56008 --- /dev/null +++ b/docs/reference/Note/Methods/committer.md @@ -0,0 +1,38 @@ +# committer + +Get the note committer + +## Signature + +```ts +class Note { + committer(): Signature; +} +``` + +### Returns + + \ No newline at end of file diff --git a/docs/reference/Note/Methods/id.md b/docs/reference/Note/Methods/id.md new file mode 100644 index 0000000..3870abe --- /dev/null +++ b/docs/reference/Note/Methods/id.md @@ -0,0 +1,21 @@ +# id + +Get the note object's id + +## Signature + +```ts +class Note { + id(): string; +} +``` + +### Returns + + \ No newline at end of file diff --git a/docs/reference/Note/Methods/message.md b/docs/reference/Note/Methods/message.md new file mode 100644 index 0000000..fb43e9d --- /dev/null +++ b/docs/reference/Note/Methods/message.md @@ -0,0 +1,31 @@ +# message + +Get the note message as a string. + +## Signature + +```ts +class Note { + message(): string; +} +``` + +### Returns + + + +### Errors + + \ No newline at end of file diff --git a/docs/reference/Repository/Methods/deleteNote.md b/docs/reference/Repository/Methods/deleteNote.md new file mode 100644 index 0000000..7c21630 --- /dev/null +++ b/docs/reference/Repository/Methods/deleteNote.md @@ -0,0 +1,104 @@ +# deleteNote + +Remove the note for an object. + +The `notesRef` argument is the canonical name of the reference to use, +defaulting to "refs/notes/commits". + +The id specified is the Oid of the git object to remove the note from. + +## Signature + +```ts +class Repository { + deleteNote(id: string, options?: DeleteNoteOptions | null | undefined): void; +} +``` + +### Parameters + + \ No newline at end of file diff --git a/docs/reference/Repository/Methods/findNote.md b/docs/reference/Repository/Methods/findNote.md new file mode 100644 index 0000000..30c2c27 --- /dev/null +++ b/docs/reference/Repository/Methods/findNote.md @@ -0,0 +1,47 @@ +# findNote + +Read the note for an object. + +The `notesRef` argument is the canonical name of the reference to use, +defaulting to "refs/notes/commits". + +The id specified is the Oid of the git object to read the note from. + +## Signature + +```ts +class Repository { + findNote(id: string, options?: FindNoteOptions | null | undefined): Note | null; +} +``` + +### Parameters + + + +### Returns + + \ No newline at end of file diff --git a/docs/reference/Repository/Methods/getNote.md b/docs/reference/Repository/Methods/getNote.md new file mode 100644 index 0000000..a7a4c97 --- /dev/null +++ b/docs/reference/Repository/Methods/getNote.md @@ -0,0 +1,57 @@ +# getNote + +Read the note for an object. + +The `notesRef` argument is the canonical name of the reference to use, +defaulting to "refs/notes/commits". + +The id specified is the Oid of the git object to read the note from. + +## Signature + +```ts +class Repository { + getNote(id: string, options?: FindNoteOptions | null | undefined): Note; +} +``` + +### Parameters + + + +### Returns + + + +### Errors + + \ No newline at end of file diff --git a/docs/reference/Repository/Methods/note.md b/docs/reference/Repository/Methods/note.md new file mode 100644 index 0000000..84261dd --- /dev/null +++ b/docs/reference/Repository/Methods/note.md @@ -0,0 +1,123 @@ +# note + +Add a note for an object + +The `notesRef` argument is the canonical name of the reference to use, +defaulting to "refs/notes/commits". If `force` is specified then +previous notes are overwritten. + +## Signature + +```ts +class Repository { + note(oid: string, note: string, options?: CreateNoteOptions | null | undefined): string; +} +``` + +### Parameters + + + +### Returns + + \ No newline at end of file diff --git a/docs/reference/Repository/Methods/noteDefaultRef.md b/docs/reference/Repository/Methods/noteDefaultRef.md new file mode 100644 index 0000000..7e29023 --- /dev/null +++ b/docs/reference/Repository/Methods/noteDefaultRef.md @@ -0,0 +1,21 @@ +# noteDefaultRef + +Get the default notes reference for this repository + +## Signature + +```ts +class Repository { + noteDefaultRef(): string; +} +``` + +### Returns + + \ No newline at end of file diff --git a/docs/reference/Repository/Methods/notes.md b/docs/reference/Repository/Methods/notes.md new file mode 100644 index 0000000..b35ae59 --- /dev/null +++ b/docs/reference/Repository/Methods/notes.md @@ -0,0 +1,46 @@ +# notes + +Creates a new iterator for notes in this repository. + +The `notesRef` argument is the canonical name of the reference to use, +defaulting to "refs/notes/commits". + +## Signature + +```ts +class Repository { + notes(noteRef?: string | null | undefined): Notes; +} +``` + +### Parameters + + + +### Returns + + + +## Examples + +```ts +import { openRepository } from 'es-git'; + +const repo = await openRepository('.'); +for (const value of repo.notes()) { + const [noteId, annotatedId] = value; + const note = repo.getNote(noteId); + const commit = repo.getCommit(annotatedId); +} +``` \ No newline at end of file diff --git a/index.d.ts b/index.d.ts index 8dd323f..4411307 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1895,6 +1895,85 @@ export declare class Mailmap { resolveSignature(signature: SignaturePayload): Signature } +/** + * A structure representing a [note][1] in git. + * + * [1]: http://alblue.bandlem.com/2011/11/git-tip-of-week-git-notes.html + */ +export declare class Note { + /** + * Get the note object's id + * + * @category Note/Methods + * @signature + * ```ts + * class Note { + * id(): string; + * } + * ``` + * + * @returns The note object's id. + */ + id(): string + /** + * Get the note author + * + * @category Note/Methods + * @signature + * ```ts + * class Note { + * author(): Signature; + * } + * ``` + * + * @returns The note author signature. + */ + author(): Signature + /** + * Get the note committer + * + * @category Note/Methods + * @signature + * ```ts + * class Note { + * committer(): Signature; + * } + * ``` + * + * @returns The note committer signature. + */ + committer(): Signature + /** + * Get the note message as a string. + * + * @category Note/Methods + * @signature + * ```ts + * class Note { + * message(): string; + * } + * ``` + * + * @returns The note message as a string + * @throws Throws error if message is not utf-8. + */ + message(): string +} + +/** + * An iterator over all of the notes within a repository. + * + * This type extends JavaScript's `Iterator`, and so has the iterator helper + * methods. It may extend the upcoming TypeScript `Iterator` class in the future. + * + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods + * @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods + */ +export declare class Notes extends Iterator { + + next(value?: void): IteratorResult +} + /** * Representation of a rebase * Begin the rebase by iterating the returned `Rebase` @@ -3218,6 +3297,134 @@ export declare class Repository { * @returns Merge analysis result. */ analyzeMergeForRef(ourRef: Reference, theirHeads: Array): MergeAnalysisResult + /** + * Add a note for an object + * + * The `notesRef` argument is the canonical name of the reference to use, + * defaulting to "refs/notes/commits". If `force` is specified then + * previous notes are overwritten. + * + * @category Repository/Methods + * @signature + * ```ts + * class Repository { + * note(oid: string, note: string, options?: CreateNoteOptions | null | undefined): string; + * } + * ``` + * + * @param {string} oid - OID of the git object to decorate. + * @param {string} note - Content of the note to add for object oid. + * @param {CreateNoteOptions} [options] - Options for creating note. + * @returns OID for the note. + */ + note(oid: string, note: string, options?: CreateNoteOptions | undefined | null): string + /** + * Get the default notes reference for this repository + * + * @category Repository/Methods + * @signature + * ```ts + * class Repository { + * noteDefaultRef(): string; + * } + * ``` + * + * @returns The default notes reference. + */ + noteDefaultRef(): string + /** + * Creates a new iterator for notes in this repository. + * + * The `notesRef` argument is the canonical name of the reference to use, + * defaulting to "refs/notes/commits". + * + * @category Repository/Methods + * @signature + * ```ts + * class Repository { + * notes(notesRef?: string | null | undefined): Notes; + * } + * ``` + * + * @param {string} [notesRef] - The canonical name of the reference to use. + * @returns Iterator of all notes. The iterator returned yields pairs of `[string, string]` + * where first element is the id of the note and the second id is the id the note is annotating. + * + * @example + * ```ts + * import { openRepository } from 'es-git'; + * + * const repo = await openRepository('.'); + * for (const { noteId, annotatedId } of repo.notes()) { + * const note = repo.getNote(noteId); + * const commit = repo.getCommit(annotatedId); + * } + * ``` + */ + notes(notesRef?: string | undefined | null): Notes + /** + * Read the note for an object. + * + * The `notesRef` argument is the canonical name of the reference to use, + * defaulting to "refs/notes/commits". + * + * The id specified is the Oid of the git object to read the note from. + * + * @category Repository/Methods + * @signature + * ```ts + * class Repository { + * getNote(id: string, options?: FindNoteOptions | null | undefined): Note; + * } + * ``` + * + * @param {string} id - OID of the git object to read the note from. + * @param {FindNoteOptions} [options] - Options for finding note. + * @returns Instance of the note. + * @throws Throws error if note does not exists. + */ + getNote(id: string, options?: FindNoteOptions | undefined | null): Note + /** + * Read the note for an object. + * + * The `notesRef` argument is the canonical name of the reference to use, + * defaulting to "refs/notes/commits". + * + * The id specified is the Oid of the git object to read the note from. + * + * @category Repository/Methods + * @signature + * ```ts + * class Repository { + * findNote(id: string, options?: FindNoteOptions | null | undefined): Note | null; + * } + * ``` + * + * @param {string} id - OID of the git object to read the note from. + * @param {FindNoteOptions} [options] - Options for finding note. + * @returns Instance of the note. If does not exists, returns `null`. + */ + findNote(id: string, options?: FindNoteOptions | undefined | null): Note | null + /** + * Remove the note for an object. + * + * The `notesRef` argument is the canonical name of the reference to use, + * defaulting to "refs/notes/commits". + * + * The id specified is the Oid of the git object to remove the note from. + * + * @category Repository/Methods + * @signature + * ```ts + * class Repository { + * deleteNote(id: string, options?: DeleteNoteOptions | null | undefined): void; + * } + * ``` + * + * @param {string} id - OID of the git object to remove the note from. + * @param {DeleteNoteOptions} [options] - Options for deleting note. + */ + deleteNote(id: string, options?: DeleteNoteOptions | undefined | null): void /** * Lookup a reference to one of the objects in a repository. * @@ -5734,6 +5941,31 @@ export interface CreateLightweightTagOptions { */ export declare function createMailmapFromBuffer(content: string): Mailmap +export interface CreateNoteOptions { + /** + * Signature of the notes commit author. + * + * If not provided, the default signature of the repository will be used. + * If there is no default signature set for the repository, an error will occur. + */ + author?: SignaturePayload + /** + * Signature of the notes commit commiter. + * + * If not provided, the default signature of the repository will be used. + * If there is no default signature set for the repository, an error will occur. + */ + committer?: SignaturePayload + /** + * canonical name of the reference to use. + * + * Defaults to "refs/notes/commits". + */ + notesRef?: string + /** Overwrite existing note. */ + force?: boolean +} + export interface CreateRemoteOptions { fetchRefspec?: string } @@ -5811,6 +6043,29 @@ export type CredentialType = 'Default'| 'SSHKey'| 'Plain'; +export interface DeleteNoteOptions { + /** + * Signature of the notes commit author. + * + * If not provided, the default signature of the repository will be used. + * If there is no default signature set for the repository, an error will occur. + */ + author?: SignaturePayload + /** + * Signature of the notes commit commiter. + * + * If not provided, the default signature of the repository will be used. + * If there is no default signature set for the repository, an error will occur. + */ + committer?: SignaturePayload + /** + * canonical name of the reference to use. + * + * Defaults to "refs/notes/commits". + */ + notesRef?: string +} + /** * - `Unmodified` : No changes. * - `Added` : Entry does not exist in an old version. @@ -6250,6 +6505,10 @@ export type FileMode = 'Unreadable'| */ export declare function findGlobalConfigPath(): string | null +export interface FindNoteOptions { + notesRef?: string +} + /** * Locate the path to the system configuration file. * @@ -6722,6 +6981,11 @@ export interface MergePreference { */ export declare function normalizeReferenceName(refname: string, format?: number | undefined | null): string | null +export interface NoteIterItem { + noteId: string + annotatedId: string +} + /** * - `Any` : Any kind of git object * - `Commit` : An object which corresponds to a git commit diff --git a/index.js b/index.js index 6aed265..360638c 100644 --- a/index.js +++ b/index.js @@ -591,6 +591,8 @@ module.exports.GitObject = nativeBinding.GitObject module.exports.Index = nativeBinding.Index module.exports.IndexEntries = nativeBinding.IndexEntries module.exports.Mailmap = nativeBinding.Mailmap +module.exports.Note = nativeBinding.Note +module.exports.Notes = nativeBinding.Notes module.exports.Rebase = nativeBinding.Rebase module.exports.Reference = nativeBinding.Reference module.exports.Remote = nativeBinding.Remote diff --git a/src/lib.rs b/src/lib.rs index 354efe6..9e9afe0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ pub mod ignore; pub mod index; pub mod mailmap; pub mod merge; +pub mod note; pub mod object; pub mod oid; pub mod rebase; diff --git a/src/note.rs b/src/note.rs new file mode 100644 index 0000000..5d22ea3 --- /dev/null +++ b/src/note.rs @@ -0,0 +1,366 @@ +use crate::repository::Repository; +use crate::signature::{Signature, SignaturePayload}; +use git2::Oid; +use napi::bindgen_prelude::*; +use napi_derive::napi; + +#[napi] +/// A structure representing a [note][1] in git. +/// +/// [1]: http://alblue.bandlem.com/2011/11/git-tip-of-week-git-notes.html +pub struct Note { + pub(crate) inner: SharedReference>, +} + +#[napi] +impl Note { + #[napi] + /// Get the note object's id + /// + /// @category Note/Methods + /// @signature + /// ```ts + /// class Note { + /// id(): string; + /// } + /// ``` + /// + /// @returns The note object's id. + pub fn id(&self) -> String { + self.inner.id().to_string() + } + + #[napi] + /// Get the note author + /// + /// @category Note/Methods + /// @signature + /// ```ts + /// class Note { + /// author(): Signature; + /// } + /// ``` + /// + /// @returns The note author signature. + pub fn author(&self) -> crate::Result { + let sig = self.inner.author(); + Signature::try_from(sig) + } + + #[napi] + /// Get the note committer + /// + /// @category Note/Methods + /// @signature + /// ```ts + /// class Note { + /// committer(): Signature; + /// } + /// ``` + /// + /// @returns The note committer signature. + pub fn committer(&self) -> crate::Result { + let sig = self.inner.committer(); + Signature::try_from(sig) + } + + #[napi] + /// Get the note message as a string. + /// + /// @category Note/Methods + /// @signature + /// ```ts + /// class Note { + /// message(): string; + /// } + /// ``` + /// + /// @returns The note message as a string + /// @throws Throws error if message is not utf-8. + pub fn message(&self) -> crate::Result { + let message = std::str::from_utf8(self.inner.message_bytes())?; + Ok(message.to_string()) + } +} + +#[napi(object)] +#[derive(Default)] +pub struct FindNoteOptions { + pub notes_ref: Option, +} + +#[napi(object)] +#[derive(Default)] +pub struct CreateNoteOptions { + /// Signature of the notes commit author. + /// + /// If not provided, the default signature of the repository will be used. + /// If there is no default signature set for the repository, an error will occur. + pub author: Option, + /// Signature of the notes commit commiter. + /// + /// If not provided, the default signature of the repository will be used. + /// If there is no default signature set for the repository, an error will occur. + pub committer: Option, + /// canonical name of the reference to use. + /// + /// Defaults to "refs/notes/commits". + pub notes_ref: Option, + /// Overwrite existing note. + pub force: Option, +} + +#[napi(object)] +#[derive(Default)] +pub struct DeleteNoteOptions { + /// Signature of the notes commit author. + /// + /// If not provided, the default signature of the repository will be used. + /// If there is no default signature set for the repository, an error will occur. + pub author: Option, + /// Signature of the notes commit commiter. + /// + /// If not provided, the default signature of the repository will be used. + /// If there is no default signature set for the repository, an error will occur. + pub committer: Option, + /// canonical name of the reference to use. + /// + /// Defaults to "refs/notes/commits". + pub notes_ref: Option, +} + +#[napi(iterator)] +/// An iterator over all of the notes within a repository. +pub struct Notes { + pub(crate) inner: SharedReference>, +} + +#[napi(object)] +pub struct NoteIterItem { + pub note_id: String, + pub annotated_id: String, +} + +#[napi] +impl Generator for Notes { + type Yield = NoteIterItem; + type Next = (); + type Return = (); + + fn next(&mut self, _value: Option) -> Option { + self.inner.next().and_then(|x| { + x.ok().map(|(note_id, annotated_id)| NoteIterItem { + note_id: note_id.to_string(), + annotated_id: annotated_id.to_string(), + }) + }) + } +} + +#[napi] +impl Repository { + #[napi] + /// Add a note for an object + /// + /// The `notesRef` argument is the canonical name of the reference to use, + /// defaulting to "refs/notes/commits". If `force` is specified then + /// previous notes are overwritten. + /// + /// @category Repository/Methods + /// @signature + /// ```ts + /// class Repository { + /// note(oid: string, note: string, options?: CreateNoteOptions | null | undefined): string; + /// } + /// ``` + /// + /// @param {string} oid - OID of the git object to decorate. + /// @param {string} note - Content of the note to add for object oid. + /// @param {CreateNoteOptions} [options] - Options for creating note. + /// @returns OID for the note. + pub fn note(&self, oid: String, note: String, options: Option) -> crate::Result { + let opts = options.unwrap_or_default(); + let oid = Oid::from_str(&oid)?; + let author = opts + .author + .and_then(|x| Signature::try_from(x).ok()) + .and_then(|x| git2::Signature::try_from(x).ok()) + .or_else(|| self.inner.signature().ok()) + .ok_or(crate::Error::SignatureNotFound)?; + let committer = opts + .committer + .and_then(|x| Signature::try_from(x).ok()) + .and_then(|x| git2::Signature::try_from(x).ok()) + .or_else(|| self.inner.signature().ok()) + .ok_or(crate::Error::SignatureNotFound)?; + let notes_ref = opts.notes_ref; + let force = opts.force.unwrap_or_default(); + let note_oid = self + .inner + .note(&author, &committer, notes_ref.as_deref(), oid, ¬e, force)?; + Ok(note_oid.to_string()) + } + + #[napi] + /// Get the default notes reference for this repository + /// + /// @category Repository/Methods + /// @signature + /// ```ts + /// class Repository { + /// noteDefaultRef(): string; + /// } + /// ``` + /// + /// @returns The default notes reference. + pub fn note_default_ref(&self) -> crate::Result { + let default_ref = self.inner.note_default_ref()?; + Ok(default_ref) + } + + #[napi] + /// Creates a new iterator for notes in this repository. + /// + /// The `notesRef` argument is the canonical name of the reference to use, + /// defaulting to "refs/notes/commits". + /// + /// @category Repository/Methods + /// @signature + /// ```ts + /// class Repository { + /// notes(notesRef?: string | null | undefined): Notes; + /// } + /// ``` + /// + /// @param {string} [notesRef] - The canonical name of the reference to use. + /// @returns Iterator of all notes. The iterator returned yields pairs of `[string, string]` + /// where first element is the id of the note and the second id is the id the note is annotating. + /// + /// @example + /// ```ts + /// import { openRepository } from 'es-git'; + /// + /// const repo = await openRepository('.'); + /// for (const { noteId, annotatedId } of repo.notes()) { + /// const note = repo.getNote(noteId); + /// const commit = repo.getCommit(annotatedId); + /// } + /// ``` + pub fn notes(&self, this: Reference, env: Env, notes_ref: Option) -> crate::Result { + let inner = this.share_with(env, |repo| { + repo + .inner + .notes(notes_ref.as_deref()) + .map_err(crate::Error::from) + .map_err(|e| e.into()) + })?; + Ok(Notes { inner }) + } + + #[napi] + /// Read the note for an object. + /// + /// The `notesRef` argument is the canonical name of the reference to use, + /// defaulting to "refs/notes/commits". + /// + /// The id specified is the Oid of the git object to read the note from. + /// + /// @category Repository/Methods + /// @signature + /// ```ts + /// class Repository { + /// getNote(id: string, options?: FindNoteOptions | null | undefined): Note; + /// } + /// ``` + /// + /// @param {string} id - OID of the git object to read the note from. + /// @param {FindNoteOptions} [options] - Options for finding note. + /// @returns Instance of the note. + /// @throws Throws error if note does not exists. + pub fn get_note( + &self, + this: Reference, + env: Env, + id: String, + options: Option, + ) -> crate::Result { + let opts = options.unwrap_or_default(); + let oid = Oid::from_str(&id)?; + let notes_ref = opts.notes_ref; + let inner = this.share_with(env, |repo| { + repo + .inner + .find_note(notes_ref.as_deref(), oid) + .map_err(crate::Error::from) + .map_err(|e| e.into()) + })?; + Ok(Note { inner }) + } + + #[napi] + /// Read the note for an object. + /// + /// The `notesRef` argument is the canonical name of the reference to use, + /// defaulting to "refs/notes/commits". + /// + /// The id specified is the Oid of the git object to read the note from. + /// + /// @category Repository/Methods + /// @signature + /// ```ts + /// class Repository { + /// findNote(id: string, options?: FindNoteOptions | null | undefined): Note | null; + /// } + /// ``` + /// + /// @param {string} id - OID of the git object to read the note from. + /// @param {FindNoteOptions} [options] - Options for finding note. + /// @returns Instance of the note. If does not exists, returns `null`. + pub fn find_note( + &self, + this: Reference, + env: Env, + id: String, + options: Option, + ) -> Option { + self.get_note(this, env, id, options).ok() + } + + #[napi] + /// Remove the note for an object. + /// + /// The `notesRef` argument is the canonical name of the reference to use, + /// defaulting to "refs/notes/commits". + /// + /// The id specified is the Oid of the git object to remove the note from. + /// + /// @category Repository/Methods + /// @signature + /// ```ts + /// class Repository { + /// deleteNote(id: string, options?: DeleteNoteOptions | null | undefined): void; + /// } + /// ``` + /// + /// @param {string} id - OID of the git object to remove the note from. + /// @param {DeleteNoteOptions} [options] - Options for deleting note. + pub fn delete_note(&self, id: String, options: Option) -> crate::Result<()> { + let opts = options.unwrap_or_default(); + let id = Oid::from_str(&id)?; + let author = opts + .author + .and_then(|x| Signature::try_from(x).ok()) + .and_then(|x| git2::Signature::try_from(x).ok()) + .or_else(|| self.inner.signature().ok()) + .ok_or(crate::Error::SignatureNotFound)?; + let committer = opts + .committer + .and_then(|x| Signature::try_from(x).ok()) + .and_then(|x| git2::Signature::try_from(x).ok()) + .or_else(|| self.inner.signature().ok()) + .ok_or(crate::Error::SignatureNotFound)?; + let notes_ref = opts.notes_ref; + self.inner.note_delete(id, notes_ref.as_deref(), &author, &committer)?; + Ok(()) + } +} diff --git a/tests/note.spec.ts b/tests/note.spec.ts new file mode 100644 index 0000000..cd26a91 --- /dev/null +++ b/tests/note.spec.ts @@ -0,0 +1,59 @@ +import { describe, expect, it } from 'vitest'; +import { openRepository } from '../index'; +import { useFixture } from './fixtures'; + +describe('note', () => { + const signature = { + name: 'Seokju Na', + email: 'seokju.me@toss.im', + }; + + it('get default note ref', async () => { + const p = await useFixture('commits'); + const repo = await openRepository(p); + const ref = repo.noteDefaultRef(); + expect(ref).toEqual('refs/notes/commits'); + }); + + it('create note', async () => { + const p = await useFixture('commits'); + const repo = await openRepository(p); + const noteId = repo.note('a01e9888e46729ef4aa68953ba19b02a7a64eb82', 'this is note', { + author: signature, + committer: signature, + }); + const note = repo.getNote('a01e9888e46729ef4aa68953ba19b02a7a64eb82'); + expect(note.id()).toEqual(noteId); + expect(note.message()).toEqual('this is note'); + }); + + it('delete note', async () => { + const p = await useFixture('commits'); + const repo = await openRepository(p); + repo.note('a01e9888e46729ef4aa68953ba19b02a7a64eb82', 'this is note', { + author: signature, + committer: signature, + }); + expect(repo.findNote('a01e9888e46729ef4aa68953ba19b02a7a64eb82')).not.toBeNull(); + repo.deleteNote('a01e9888e46729ef4aa68953ba19b02a7a64eb82', { + author: signature, + committer: signature, + }); + expect(repo.findNote('a01e9888e46729ef4aa68953ba19b02a7a64eb82')).toBeNull(); + }); + + it('iterate notes', async () => { + const p = await useFixture('commits'); + const repo = await openRepository(p); + repo.note('a01e9888e46729ef4aa68953ba19b02a7a64eb82', 'note1', { + author: signature, + committer: signature, + }); + repo.note('b33e0101b828225f77eeff4dfa31259dcf379002', 'note2', { + author: signature, + committer: signature, + }); + const result = [...repo.notes()]; + expect(result).toHaveLength(2); + }); +});