180 lines
8.6 KiB
Racket
180 lines
8.6 KiB
Racket
#lang scribble/manual
|
|
|
|
@(require (for-label racket/base git))
|
|
|
|
@title{git}
|
|
@author{Hans Dijkema}
|
|
|
|
@defmodule[git]
|
|
|
|
The @racketmodname[git] module provides a small command-line-like Git interface implemented on top of the @tt{libgit2} package. It does not invoke the @tt{git} executable.
|
|
|
|
The short form is intended for build scripts and interactive use:
|
|
|
|
@racketblock[
|
|
(require git)
|
|
|
|
(git 'status)
|
|
(git 'diff)
|
|
(git 'diff '--cached)
|
|
(git 'add "main.rkt" "info.rkt")
|
|
(git 'commit "Implement raco support")
|
|
(git 'tag "v0.1")
|
|
(git 'checkout "main")
|
|
]
|
|
|
|
@defproc[(git [command symbol?] [#:quiet quiet any/c #f] [argument any/c] ...) any/c]{
|
|
Dispatches @racket[command] to the corresponding Git procedure. For example, @racket[(git 'status)] calls @racket[git-status], and @racket[(git 'commit "message")] calls @racket[git-commit]. Command names are ordinary symbols, so @racket[git] can safely be used inside other macros and DSLs. For network commands, @racket[#:quiet] suppresses progress output.
|
|
}
|
|
|
|
@defproc[(dgit [command symbol?] [#:quiet quiet any/c #f] [argument any/c] ...) any/c]{
|
|
Calls @racket[git], displays its result in a compact human-readable form, and returns the original result. Status entries are displayed with labels such as @tt{Modified}, @tt{New}, @tt{Deleted}, and @tt{Renamed}. Ignored files remain omitted, just as with @racket[git-status].
|
|
}
|
|
|
|
@racketblock[
|
|
(dgit 'status)
|
|
]
|
|
|
|
@section{Repository}
|
|
|
|
@defproc[(git-repository? [path path-string? (current-directory)]) boolean?]{Returns whether @racket[path] is inside a Git repository.}
|
|
|
|
@defproc[(git-root [path path-string? (current-directory)]) path?]{Returns the repository worktree root.}
|
|
|
|
@defproc[(git-init [path path-string? (current-directory)] [#:bare? bare? any/c #f]) path?]{Initializes a repository.}
|
|
|
|
@defproc*[([(git-clone [url string?] [#:quiet quiet any/c #f]) path?]
|
|
[(git-clone [url string?] [path path-string?] [#:quiet quiet any/c #f]) path?])]{Clones @racket[url]. If @racket[path] is omitted, a directory name is derived from the URL. Progress is written to the current output port unless @racket[quiet] is true.}
|
|
|
|
@section{Status and index}
|
|
|
|
@defstruct*[git-status-entry ([path string?] [code string?] [flags list?])]{Describes one status entry. The @racket[code] field uses the familiar two-character Git status notation.}
|
|
|
|
@defproc[(git-status) (listof git-status-entry?)]{Returns worktree and index status.}
|
|
|
|
@defproc[(git-status-lines [entries (listof git-status-entry?) (git-status)]) (listof string?)]{Formats status entries as short Git-like lines.}
|
|
|
|
@defproc[(git-clean?) boolean?]{Returns @racket[#t] when @racket[git-status] is empty.}
|
|
|
|
@defproc*[([(git-diff) string?]
|
|
[(git-diff [option (or/c '--cached)]) string?])]{Returns a unified patch as a string. With no arguments it compares the index with the worktree, like @tt{git diff}. With @racket['--cached] it compares HEAD with the index, like @tt{git diff --cached}.}
|
|
|
|
|
|
@defproc[(git-add [path path-string?] ...) void?]{Stages the given paths. With no paths, stages the whole repository, including tracked removals.}
|
|
|
|
@section{Configuration and commits}
|
|
|
|
@defproc*[([(git-config [key string?]) string?]
|
|
[(git-config [key string?] [value string?]) string?])]{Reads or writes a repository configuration value. The two-argument form returns @racket[value].}
|
|
|
|
@defproc[(git-head) (or/c string? #f)]{Returns the full OID of HEAD, or @racket[#f] for a repository without commits.}
|
|
|
|
@defproc[(git-commit [message string?]) string?]{Creates a commit from the index and returns its full OID. The author and committer are read from the repository configuration.}
|
|
|
|
@section{Branches, checkout, and tags}
|
|
|
|
@defproc[(git-current-branch) (or/c string? #f)]{Returns the current local branch name, or @racket[#f] for detached HEAD.}
|
|
|
|
@defproc*[([(git-branch) (listof string?)]
|
|
[(git-branch [name string?]) string?])]{Lists local branches, or creates @racket[name] at HEAD.}
|
|
|
|
@defproc[(git-branch-delete [name string?]) void?]{Deletes a local branch.}
|
|
|
|
@defproc[(git-checkout [name string?]) (or/c string? #f)]{Checks out a local branch, tag, or commit. A tag or commit produces detached HEAD.}
|
|
|
|
@defproc[(git-checkout-new [name string?]) string?]{Creates and checks out a new branch.}
|
|
|
|
@defproc*[([(git-tag) (listof string?)]
|
|
[(git-tag [name string?]) string?])]{Lists tags, or creates a lightweight tag at HEAD and returns its OID.}
|
|
|
|
@defproc[(git-tag-delete [name string?]) void?]{Deletes a tag.}
|
|
|
|
@section{Log}
|
|
|
|
@defstruct*[git-log-entry ([id string?] [summary string?] [time integer?])]{Describes one commit returned by @racket[git-log].}
|
|
|
|
@defproc[(git-log [max-count exact-nonnegative-integer? 20]) (listof git-log-entry?)]{Returns commits from HEAD in topological/time order.}
|
|
|
|
@defproc[(git-log-lines [entries (listof git-log-entry?) (git-log)]) (listof string?)]{Formats log entries as short OID plus summary.}
|
|
|
|
@section{Remotes}
|
|
|
|
@defproc[(git-remotes) (listof string?)]{Lists remotes.}
|
|
|
|
@defproc[(git-remote-add [name string?] [url string?]) string?]{Adds a remote.}
|
|
|
|
@defproc[(git-remote-url [name string? "origin"]) string?]{Returns the remote URL.}
|
|
|
|
@defproc[(git-fetch [remote string? "origin"] [#:quiet quiet any/c #f]) void?]{Fetches the configured refspecs from a remote. Progress is written to the current output port unless @racket[quiet] is true.}
|
|
|
|
@defproc[(git-pull [remote string? "origin"] [#:quiet quiet any/c #f]) (or/c string? #f)]{Fetches and performs a fast-forward-only update of the current branch. Returns the new OID, or @racket[#f] when already up to date. A non-fast-forward update raises an exception.}
|
|
|
|
@defproc[(git-push [remote string? "origin"] [branch (or/c string? #f) #f] [#:quiet quiet any/c #f]) void?]{Pushes a branch to a branch with the same name. With no positional arguments, the current branch is pushed to @tt{origin}; with only @racket[remote], the current branch is pushed there. A start and completion message are written to the current output port unless @racket[quiet] is true. Transfer-percentage output is temporarily disabled so that no output is performed from the libgit2 push-progress callback.}
|
|
|
|
@defproc[(git-push-tag [tag string?] [remote string? "origin"] [#:quiet quiet any/c #f]) void?]{Pushes one tag. A start and completion message are written to the current output port unless @racket[quiet] is true. Transfer-percentage output is temporarily disabled so that no output is performed from the libgit2 push-progress callback.}
|
|
|
|
Remote HTTPS operations automatically use credentials from the @tt{racket-git} credential store when an entry exists for the remote host.
|
|
|
|
@section{Command form}
|
|
|
|
The following command-like forms are supported directly:
|
|
|
|
@racketblock[
|
|
(git 'init)
|
|
(git 'clone "https://example/repo.git")
|
|
(git 'status)
|
|
(git 'add "file.rkt")
|
|
(git 'config "user.name" "Name")
|
|
(git 'commit "message")
|
|
(git 'branch)
|
|
(git 'branch "feature")
|
|
(git 'branch '-d "feature")
|
|
(git 'checkout "main")
|
|
(git 'checkout '-b "feature")
|
|
(git 'tag)
|
|
(git 'tag "v0.1")
|
|
(git 'tag '-d "v0.1")
|
|
(git 'log 10)
|
|
(git 'remote)
|
|
(git 'remote 'add "origin" "https://example/repo.git")
|
|
(git 'remote 'get-url "origin")
|
|
(git 'fetch)
|
|
(git 'pull)
|
|
(git 'push)
|
|
(git 'push #:quiet #t)
|
|
(git 'push-tag "v0.1")
|
|
]
|
|
|
|
@section{HTTPS credentials}
|
|
|
|
Git credentials are stored in @tt{racket-git.ini} in the normal Racket
|
|
preferences directory. Tokens are encrypted with AES-GCM. The encryption key is
|
|
derived from the store password with PBKDF2-HMAC-SHA256.
|
|
|
|
@defproc[(git-credentials-init! [password string?]
|
|
[#:unlock-for seconds real? 86400]) void?]{
|
|
Creates the credential store and leaves it unlocked for @racket[seconds].}
|
|
|
|
@defproc[(git-credentials-unlock! [password string?]
|
|
[#:for seconds real? 86400]) void?]{
|
|
Unlocks the credential store. The temporary unlock state is stored separately in
|
|
@tt{racket-git-unlock.ini}, allowing the unlock to survive restarting DrRacket
|
|
or starting another Racket process. Both credential INI files use
|
|
@racket[#:private? #t] storage from @racketmodname[simple-ini], which restricts
|
|
them to mode 0600 on Unix.}
|
|
|
|
@defproc[(git-credentials-lock!) void?]{Locks the credential store immediately.}
|
|
|
|
@defproc[(git-credentials-unlocked?) boolean?]{Returns whether a non-expired
|
|
unlock key is currently available.}
|
|
|
|
@defproc[(git-credentials-set! [remote string?] [username string?] [token string?]) void?]{
|
|
Stores an HTTPS username and token. Credentials are keyed by host.}
|
|
|
|
@defproc[(git-credentials-ref [remote string?]) (or/c #f pair?)]{
|
|
Returns the username/token pair for @racket[remote], or @racket[#f] when none is
|
|
stored. The store must be unlocked when a credential exists.}
|
|
|
|
@defproc[(git-credentials-remove! [remote string?]) void?]{Removes credentials
|
|
for the host represented by @racket[remote].}
|