#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?] [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. } @defproc[(dgit [command symbol?] [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?]) path?] [(git-clone [url string?] [path path-string?]) path?])]{Clones @racket[url]. If @racket[path] is omitted, a directory name is derived from the URL.} @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"]) void?]{Fetches the configured refspecs from a remote.} @defproc[(git-pull [remote string? "origin"]) (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) void?] [(git-push [remote string?]) void?] [(git-push [remote string?] [branch string?]) void?])]{Pushes a branch to a branch with the same name. With no arguments, the current branch is pushed to @tt{origin}; with only @racket[remote], the current branch is pushed there.} @defproc[(git-push-tag [tag string?] [remote string? "origin"]) void?]{Pushes one tag.} 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-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].}