From fea7fb7a59882ade8d50b2314a17afb7d6dd4ccb Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Tue, 11 Aug 2026 21:41:52 +0200 Subject: [PATCH] a complete git commandline --- Makefile.rkt | 3 +-- README.md | 4 ++++ info.rkt | 2 +- main.rkt | 5 ++++- scribblings/git.scrbl | 4 ++-- tests/grep-reader.rkt | 32 ++++++++++++++++++++++++++++++++ 6 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 tests/grep-reader.rkt diff --git a/Makefile.rkt b/Makefile.rkt index f2ea218..de04d4e 100644 --- a/Makefile.rkt +++ b/Makefile.rkt @@ -12,8 +12,7 @@ (target clean (for-each (λ (f) (displayln f) (rm-f f)) (list-files "." #px"([.]bak|~)$" #:recursive #t)) (for-each (λ (d) (displayln d) (rm-rf d)) (list-dirs "." #px"(compiled|doc)$" #:recursive #t)) - (when (directory-exists? "scribblings") - (for-each (λ (f) (displayln f) (rm-f f)) (list-files "scribblings" #px"[.](css|js|html)$"))) + (for-each (λ (f) (displayln f) (rm-f f)) (list-files "scribblings" #px"[.](css|js|html)$")) ) (target package diff --git a/README.md b/README.md index 737b3c8..d988980 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,10 @@ A small command-line-like Git module for Racket, implemented directly on top of `git` is an ordinary procedure; command names are symbols. `dgit` performs the same operation, displays a compact human-readable result, and returns that result. +For `grep`, the natural `(git 'grep '-i "pattern")` spelling is supported even +though Racket's reader represents `-i` as the complex number `0-1i`; in grep +option position that value is interpreted as Git's `-i` flag. + The same operations are available as normal procedures such as `git-status`, `git-add`, `git-commit`, `git-tag`, `git-current-branch`, `git-switch`, and `git-checkout`. ## HTTPS credentials diff --git a/info.rkt b/info.rkt index 92c3906..9499620 100644 --- a/info.rkt +++ b/info.rkt @@ -2,7 +2,7 @@ (define collection "git") (define pkg-desc "Command-line-like Git operations for Racket, implemented with libgit2") -(define version "0.2.12") +(define version "0.2.13") (define pkg-authors '("Hans Dijkema")) (define license 'MIT) diff --git a/main.rkt b/main.rkt index 11db546..784a694 100644 --- a/main.rkt +++ b/main.rkt @@ -871,7 +871,10 @@ (define (grep-flag? x flag) - (and (symbol? x) (eq? x flag))) + ;; Racket reads -i as the complex number 0-1i before quote is applied. + ;; In git-grep option position, accept that reader form as the Git -i flag. + (or (and (symbol? x) (eq? x flag)) + (and (equal? flag 0-1i) (equal? x 0-1i)))) (define (parse-grep-arguments args) (let loop ([rest args] [ignore-case? #f] [invert? #f] diff --git a/scribblings/git.scrbl b/scribblings/git.scrbl index 58bf6a6..4f396dc 100644 --- a/scribblings/git.scrbl +++ b/scribblings/git.scrbl @@ -43,7 +43,7 @@ Calls @racket[git], displays its result in a compact human-readable form, and re @defproc[(git-version) string?]{Returns the package version from @tt{info.rkt}. The command form is @racket[(git 'version)]. The version is not duplicated in @tt{main.rkt}; @tt{info.rkt} is the single source of truth.} @racketblock[ -(git 'version) ; => "0.2.12" +(git 'version) ; => "0.2.13" ] @section{Repository} @@ -98,7 +98,7 @@ Calls @racket[git], displays its result in a compact human-readable form, and re @defstruct*[git-grep-entry ([path string?] [line-number exact-positive-integer?] [line string?])]{Describes one line selected by @racket[git-grep]. Results are always structured this way, regardless of display-oriented flags such as @racket['-n], @racket['-l], or @racket['-c].} -@defproc[(git-grep [argument any/c] ...) (listof git-grep-entry?)]{Searches tracked files in the current worktree, or in an optional revision supplied after the pattern. String patterns are regular expressions. @racket['-i] makes matching case-insensitive and @racket['-v] inverts the match. The flags @racket['-n], @racket['-l], and @racket['-c] do not change the structured result; they control how @racket[dgit] displays it. Binary files are skipped.} +@defproc[(git-grep [argument any/c] ...) (listof git-grep-entry?)]{Searches tracked files in the current worktree, or in an optional revision supplied after the pattern. String patterns are regular expressions. @racket['-i] makes matching case-insensitive and @racket['-v] inverts the match. The flags @racket['-n], @racket['-l], and @racket['-c] do not change the structured result; they control how @racket[dgit] displays it. Binary files are skipped. Racket's reader normally reads @tt{-i} as the complex number @racket[0-1i]; @racket[git-grep] deliberately recognizes that value in option position as Git's @tt{-i} flag, so the natural @racket['-i] spelling works.} @racketblock[ (git 'grep "TODO") diff --git a/tests/grep-reader.rkt b/tests/grep-reader.rkt new file mode 100644 index 0000000..81f4151 --- /dev/null +++ b/tests/grep-reader.rkt @@ -0,0 +1,32 @@ +#lang racket/base + +(require rackunit + "../main.rkt") + +;; The Racket reader parses '-i as the complex number 0-1i. git-grep treats +;; that value as the Git -i option when it appears in option position. +(check-equal? '-i 0-1i) + +;; Exercise the public argument parser indirectly in a temporary repository. +(require racket/file) + +(define tmp (make-temporary-file "git-command-grep-reader~a" 'directory)) + +(dynamic-wind + void + (lambda () + (parameterize ([current-directory tmp]) + (git 'init) + (call-with-output-file "sample.txt" + #:exists 'truncate/replace + (lambda (out) + (displayln "Todo" out) + (displayln "other" out))) + (git 'add "sample.txt") + (define entries (git 'grep '-i "todo")) + (check-equal? (length entries) 1) + (check-equal? (git-grep-entry-path (car entries)) "sample.txt") + (check-equal? (git-grep-entry-line-number (car entries)) 1) + (check-equal? (git-grep-entry-line (car entries)) "Todo"))) + (lambda () + (delete-directory/files tmp #:must-exist? #f)))