Skip to content

Commit 6ae4459

Browse files
authored
Split commits (#393)
Allow splitting changes into multiple commits grouped by rule.
1 parent 9b1219f commit 6ae4459

File tree

5 files changed

+133
-18
lines changed

5 files changed

+133
-18
lines changed

cli.rkt

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@
44
(require fancy-app
55
json
66
racket/cmdline
7-
racket/file
87
racket/format
9-
racket/hash
10-
(except-in racket/list range)
118
racket/logging
129
racket/match
1310
racket/path
@@ -27,22 +24,29 @@
2724
resyntax/default-recommendations
2825
resyntax/private/file-group
2926
resyntax/private/github
30-
resyntax/private/limiting
31-
resyntax/private/line-replacement
3227
resyntax/private/refactoring-result
3328
resyntax/private/source
3429
resyntax/private/string-indent
35-
resyntax/private/syntax-replacement
36-
(only-in racket/list append-map empty? shuffle))
30+
resyntax/private/syntax-replacement)
3731

3832

3933
;@----------------------------------------------------------------------------------------------------
4034

4135

4236
(define-enum-type resyntax-output-format (plain-text github-pull-request-review git-commit-message))
37+
(define-enum-type resyntax-fix-method (modify-files create-multiple-git-commits))
4338
(define-record-type resyntax-analyze-options (targets suite output-format output-destination))
39+
40+
4441
(define-record-type resyntax-fix-options
45-
(targets suite output-format max-fixes max-modified-files max-modified-lines max-pass-count))
42+
(targets
43+
suite
44+
fix-method
45+
output-format
46+
max-fixes
47+
max-modified-files
48+
max-modified-lines
49+
max-pass-count))
4650

4751

4852
(define all-lines (range-set (unbounded-range #:comparator natural<=>)))
@@ -114,6 +118,7 @@ determined by the GITHUB_REPOSITORY and GITHUB_REF environment variables."
114118
(define suite default-recommendations)
115119
(define (add-target! target)
116120
(vector-builder-add targets target))
121+
(define fix-method modify-files)
117122
(define output-format plain-text)
118123
(define max-fixes +inf.0)
119124
(define max-pass-count 10)
@@ -137,10 +142,6 @@ determined by the GITHUB_REPOSITORY and GITHUB_REF environment variables."
137142
"An installed package to fix."
138143
(add-target! (package-file-group pkgname)))
139144

140-
("--output-as-commit-message"
141-
"Report results in the form of a Git commit message printed to stdout."
142-
(set! output-format git-commit-message))
143-
144145
("--local-git-repository"
145146
repopath baseref
146147
"A Git repository to search for modified files to fix. The repopath argument is a directory
@@ -151,6 +152,14 @@ changed relative to baseref are analyzed and fixed."
151152

152153
#:once-each
153154

155+
("--create-multiple-commits"
156+
"Modify files by creating a series of individual Git commits."
157+
(set! fix-method create-multiple-git-commits))
158+
159+
("--output-as-commit-message"
160+
"Report results in the form of a Git commit message printed to stdout."
161+
(set! output-format git-commit-message))
162+
154163
("--refactoring-suite"
155164
modpath
156165
suite-name
@@ -183,6 +192,7 @@ are needed when applying a fix unlocks further fixes."
183192

184193
(resyntax-fix-options #:targets (build-vector targets)
185194
#:suite suite
195+
#:fix-method fix-method
186196
#:output-format output-format
187197
#:max-fixes max-fixes
188198
#:max-modified-files max-modified-files
@@ -261,6 +271,7 @@ For help on these, use 'analyze --help' or 'fix --help'."
261271

262272
(define (resyntax-fix-run)
263273
(define options (resyntax-fix-parse-command-line))
274+
(define fix-method (resyntax-fix-options-fix-method options))
264275
(define output-format (resyntax-fix-options-output-format options))
265276
(define sources (file-groups-resolve (resyntax-fix-options-targets options)))
266277
(define max-modified-files (resyntax-fix-options-max-modified-files options))
@@ -272,7 +283,11 @@ For help on these, use 'analyze --help' or 'fix --help'."
272283
#:max-passes (resyntax-fix-options-max-pass-count options)
273284
#:max-modified-sources max-modified-files
274285
#:max-modified-lines max-modified-lines))
275-
(resyntax-analysis-write-file-changes! analysis)
286+
(match fix-method
287+
[(== modify-files)
288+
(resyntax-analysis-write-file-changes! analysis)]
289+
[(== create-multiple-git-commits)
290+
(resyntax-analysis-commit-fixes! analysis)])
276291
(match output-format
277292
[(== git-commit-message)
278293
(resyntax-fix-print-git-commit-message analysis)]

main.rkt

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
[resyntax-analysis-total-sources-modified (-> resyntax-analysis? exact-nonnegative-integer?)]
1616
[resyntax-analysis-rules-applied (-> resyntax-analysis? multiset?)]
1717
[resyntax-analysis-write-file-changes! (-> resyntax-analysis? void?)]
18+
[resyntax-analysis-commit-fixes! (-> resyntax-analysis? void?)]
1819
[resyntax-analyze
1920
(->* (source?) (#:suite refactoring-suite? #:lines range-set?) refactoring-result-set?)]
2021
[resyntax-analyze-all
@@ -40,13 +41,15 @@
4041
rebellion/collection/hash
4142
rebellion/collection/list
4243
rebellion/collection/multiset
44+
resyntax/private/commit
4345
rebellion/collection/range-set
4446
rebellion/streaming/reducer
4547
rebellion/streaming/transducer
4648
rebellion/type/record
4749
resyntax/base
4850
resyntax/default-recommendations
4951
resyntax/private/comment-reader
52+
resyntax/private/git
5053
resyntax/private/limiting
5154
resyntax/private/line-replacement
5255
resyntax/private/logger
@@ -105,15 +108,40 @@
105108
(refactoring-result-rule-name result)))
106109

107110

111+
(define (analysis-pass-fix-commits pass-results)
112+
(append-map refactoring-result-map-commits pass-results))
113+
114+
115+
(define (resyntax-analysis-fix-commits analysis)
116+
(append-map refactoring-result-map-commits (resyntax-analysis-all-results analysis)))
117+
118+
108119
(define (resyntax-analysis-write-file-changes! analysis)
109-
(log-resyntax-info "--- fixing code ---")
110-
(for ([source (in-list (resyntax-analysis-final-sources analysis))]
120+
(define sources (resyntax-analysis-final-sources analysis))
121+
(unless (empty? sources)
122+
(log-resyntax-info "--- fixing code ---"))
123+
(for ([source (in-list sources)]
111124
#:when (source-path source))
112125
(log-resyntax-info "fixing ~a" (source-path source))
113126
(display-to-file (modified-source-contents source) (source-path source)
114127
#:mode 'text #:exists 'replace)))
115128

116129

130+
(define (resyntax-analysis-commit-fixes! analysis)
131+
(define commits (resyntax-analysis-fix-commits analysis))
132+
(unless (empty? commits)
133+
(log-resyntax-info "--- fixing code ---"))
134+
(for ([commit (in-list commits)]
135+
[i (in-naturals 1)])
136+
(log-resyntax-info "--- commit ~a ---" i)
137+
(match-define (resyntax-commit message changes) commit)
138+
(for ([(path new-contents) (in-hash changes)])
139+
(log-resyntax-info "fixing ~a" path)
140+
(display-to-file new-contents path #:mode 'text #:exists 'replace))
141+
(log-resyntax-info "commiting pass fixes")
142+
(git-commit! message)))
143+
144+
117145
(define (resyntax-analyze source
118146
#:suite [suite default-recommendations]
119147
#:lines [lines (range-set (unbounded-range #:comparator natural<=>))])
@@ -308,7 +336,7 @@
308336
(log-resyntax-info
309337
(string-append "~a: suggestion discarded because it's outside the analyzed line range\n"
310338
" analyzed lines: ~a\n"
311-
" lines modified by result: ~a\n")
339+
" lines modified by result: ~a")
312340
(refactoring-result-rule-name result)
313341
lines
314342
modified-lines))

private/commit.rkt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#lang racket/base
2+
3+
4+
(provide (struct-out resyntax-commit))
5+
6+
7+
8+
(struct resyntax-commit (message changes) #:transparent)

private/git.rkt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66

77
(provide
88
(contract-out
9-
[git-diff-modified-lines (-> string? (hash/c path? immutable-range-set?))]))
9+
[git-diff-modified-lines (-> string? (hash/c path? immutable-range-set?))]
10+
[git-commit! (-> string? void?)]))
1011

1112

1213
(require fancy-app
@@ -64,3 +65,8 @@
6465
'lex-line
6566
"a git file name line (starting with '+++ b/') or a hunk range line (starting with '@@')"
6667
line)]))
68+
69+
70+
(define (git-commit! message)
71+
(unless (system (format "git commit --all --message='~a'" message))
72+
(raise-arguments-error 'git-commit-modified-files "committing files to Git failed")))

private/refactoring-result.rkt

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,29 @@
2929
[refactoring-result-set-base-source (-> refactoring-result-set? source?)]
3030
[refactoring-result-set-updated-source (-> refactoring-result-set? modified-source?)]
3131
[refactoring-result-set-results (-> refactoring-result-set? (listof refactoring-result?))]
32-
[refactoring-result-set-modified-lines (-> refactoring-result-set? immutable-range-set?)]))
32+
[refactoring-result-set-modified-lines (-> refactoring-result-set? immutable-range-set?)]
33+
[refactoring-result-map-commits
34+
(-> (hash/c source? refactoring-result-set?) (listof resyntax-commit?))]))
3335

3436

3537
(require racket/sequence
38+
racket/hash
39+
resyntax/private/logger
3640
rebellion/base/comparator
3741
rebellion/base/immutable-string
3842
rebellion/base/range
43+
(only-in racket/list first)
3944
rebellion/base/symbol
4045
rebellion/collection/list
4146
rebellion/collection/range-set
47+
resyntax/private/commit
4248
rebellion/streaming/transducer
4349
rebellion/type/record
4450
resyntax/private/code-snippet
4551
resyntax/private/line-replacement
4652
resyntax/private/linemap
4753
resyntax/private/source
54+
rebellion/collection/sorted-set
4855
resyntax/private/string-replacement
4956
resyntax/private/syntax-replacement)
5057

@@ -117,6 +124,57 @@
117124
#:into (into-range-set natural<=>)))
118125

119126

127+
(define string-replacement<=> (comparator-map natural<=> string-replacement-start))
128+
129+
130+
(define (refactoring-result-map-commits result-map)
131+
(define rule-names
132+
(transduce (in-hash-values result-map)
133+
(append-mapping refactoring-result-set-results)
134+
(mapping refactoring-result-rule-name)
135+
(deduplicating)
136+
#:into into-list))
137+
(define source-contents
138+
(for/hash ([source (in-hash-keys result-map)])
139+
(values source (source->string source))))
140+
(for/fold ([committed-replacements (hash)]
141+
[commits '()]
142+
#:result (reverse commits))
143+
([rule (in-list rule-names)])
144+
(define rule-results
145+
(for*/list ([results (in-hash-values result-map)]
146+
[result (in-list (refactoring-result-set-results results))]
147+
#:when (equal? (refactoring-result-rule-name result) rule))
148+
result))
149+
(define replacements
150+
(for/hash ([(source results) (in-hash result-map)])
151+
(define source-replacements
152+
(transduce (refactoring-result-set-results results)
153+
(filtering (λ (r) (equal? (refactoring-result-rule-name r) rule)))
154+
(mapping refactoring-result-string-replacement)
155+
#:into (into-sorted-set string-replacement<=>)))
156+
(values source source-replacements)))
157+
(define new-committed-replacements
158+
(hash-union committed-replacements replacements #:combine sorted-set-add-all))
159+
(define new-contents
160+
(for/hash ([(source old-contents) (in-hash source-contents)])
161+
(define replacement
162+
(transduce (hash-ref new-committed-replacements source '())
163+
#:into union-into-string-replacement))
164+
(values (source-path source) (string-apply-replacement old-contents replacement))))
165+
(define description
166+
(refactoring-result-message (first rule-results)))
167+
(define num-fixes (length rule-results))
168+
(define message
169+
(format "Fix ~a occurrence~a of `~a`\n\n~a"
170+
num-fixes
171+
(if (equal? num-fixes 1) "" "s")
172+
rule
173+
description))
174+
(define commit (resyntax-commit message new-contents))
175+
(values new-committed-replacements (cons commit commits))))
176+
177+
120178
(define (refactoring-result-original-code result)
121179
(define replacement (refactoring-result-string-replacement result))
122180
(define full-orig-code

0 commit comments

Comments
 (0)