Added a README file.
[git-skipper.git] / git-skipper.rkt
1 #lang racket/base
2
3 (require racket/system
4          racket/port
5          racket/cmdline
6          racket/function
7          racket/match
8          racket/string
9          racket/list)
10
11 #| String helper functions |#
12 (define (split-prefix str)
13   (define components (string-split str))
14   (define head (car components))
15   (define tail (string-join (rest components)))
16   (cons head tail))
17
18 (define (file-string file) (format "\"~a\"" file))
19 (define non-blank-string? (compose1 non-empty-string? string-trim))
20
21 #| Git commands |#
22 (define (git . args) (string-split (with-output-to-string (λ () (system (string-join (cons "git" args))))) "\n"))
23 (define (ls-files . args) (map split-prefix (git "ls-files" "-v" (string-join args))))
24 (define (update-index . args) (git "update-index" (string-join args)))
25
26 (define (modified-files) (map cdr (ls-files "-m")))
27 (define (skipped-files) (map cdr (ls-files "|" "findstr" "\"^S\"")))
28
29 (define (skip-file file) (update-index "--skip-worktree" (file-string file)))
30 (define (no-skip-file file) (update-index "--no-skip-worktree" (file-string file)))
31 (define (skip-modified) (for-each skip-file (modified-files)))
32 (define (no-skip-all) (for-each no-skip-file (skipped-files)))
33
34 #| Interactive mode helper methods |#
35 (define (find-numbered-list number items) (map cdr (filter (compose1 (curry equal? number) number->string car) items)))
36 (define (numbered-list items) (map cons (inclusive-range 1 (length items)) items))
37 (define (format-pair pair) (format "~a - ~a" (car pair) (cdr pair)))
38 (define (format-numbered-list items) (map format-pair (numbered-list items)))
39
40 (define (parse-input input) (regexp-match* #px"([sn])(\\d+)" input #:match-select rest))
41
42 (define (interpret-input skipped-file-list modified-file-list pair)
43   (match (car pair)
44     ("s" (for-each skip-file (find-numbered-list (cadr pair) (numbered-list modified-file-list))))
45     ("n" (for-each no-skip-file (find-numbered-list (cadr pair) (numbered-list skipped-file-list))))))
46
47 (define (handle-input input skipped-file-list modified-file-list)
48   (if (non-blank-string? input)
49     (for-each (curry interpret-input skipped-file-list modified-file-list) (parse-input input))
50     #f))
51
52 (define (display-files skipped-file-list modified-file-list)
53   (displayln "Skipped Files:")
54   (for-each displayln (format-numbered-list skipped-file-list))
55   (displayln "Modified Files:")
56   (for-each displayln (format-numbered-list modified-file-list)))
57
58 (define (interactive-prompt skipped-file-list modified-file-list)
59   (display-files skipped-file-list modified-file-list)
60   (displayln "Enter 's' or 'n' followed by a file number to skip or unskip a file respectively.")
61   (displayln "e.g. 's3 n12' skips the third file under Modified Files then unskips the twelfth file under Skipped Files.")
62   (displayln "Enter a blank line to quit.")
63   (display "Input: ")
64   (read-line))
65
66 (define (interactive-loop)
67   (define skipped-file-list (skipped-files))
68   (define modified-file-list (modified-files))
69   (if (handle-input (interactive-prompt skipped-file-list modified-file-list) skipped-file-list modified-file-list)
70     (interactive-loop)
71     #f))
72
73 #| Programs |#
74 (define (program-display-file-status) (display-files (skipped-files) (modified-files)))
75
76 (define (program-skip-file file)
77   (displayln (format "Skipping file: '~a'" file))
78   (skip-file file))
79
80 (define (program-no-skip-file file)
81   (displayln (format "Unskipping file: '~a'" file))
82   (no-skip-file file))
83
84 (define (program-no-skip-all)
85   (displayln "Unskipping every file")
86   (no-skip-all))
87
88 (define (program-skip-modified)
89   (displayln "Skipping every modified file")
90   (skip-modified))
91
92 (define (program-interactive)
93   (displayln "Interactive mode")
94   (interactive-loop))
95
96 (command-line
97   #:once-any
98   (("-i" "--interactive") "Interactive mode" (program-interactive))
99   (("-l" "--list") "View skipped and modified file status" (program-display-file-status))
100   (("-s" "--skip") file "Skip single file" (program-skip-file file))
101   (("-n" "--no-skip") file "Unskip single file" (program-no-skip-file file))
102   (("-p" "--no-skip-all") "Unskip every file" (program-no-skip-all))
103   (("-m" "--skip-modified") "Skip every modified file" (program-skip-modified)))