Skip to content

Commit 6710e3f

Browse files
committed
drop extra commit that can be included as a cut point
When a node is treated as the "oldest relevant" only because it's the closest node on all paths from the head to initial commit, then drop out that commit as the last step. Also, add a test suite, finally.
1 parent 27ac121 commit 6710e3f

File tree

5 files changed

+227
-28
lines changed

5 files changed

+227
-28
lines changed

chop.rkt

+22-8
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
(define (go dest-dir tmp-dir dry-run?)
66

7-
(define-values (oldest-relevant start-at-commit)
7+
(define-values (oldest-relevant start-at-commit drop-oldest?)
88
(apply values
99
(call-with-input-file (build-path dest-dir "oldest.rktd")
1010
read)))
@@ -37,14 +37,28 @@
3737
(error 'chop
3838
"could not find new commit for ~a"
3939
oldest-relevant))
40+
41+
(define oldest-now (car starts))
4042

41-
(if dry-run?
42-
(printf "grafting from ~a\n" (car starts))
43-
(with-output-to-file
44-
".git/info/grafts"
45-
(lambda ()
46-
(displayln (car starts)))))
47-
43+
(cond
44+
[dry-run?
45+
(printf "grafting from ~a~a\n"
46+
(if drop-oldest? "children of " "")
47+
oldest-now)]
48+
[drop-oldest?
49+
(define-values (commits head-commit commit->parents commit->children)
50+
(extract-commits))
51+
(with-output-to-file ".git/info/grafts"
52+
(lambda ()
53+
(for ([c (in-list (hash-ref commit->children oldest-now))])
54+
(displayln (apply ~a c
55+
#:separator " "
56+
(remove oldest-now
57+
(hash-ref commit->parents c)))))))]
58+
[else
59+
(with-output-to-file ".git/info/grafts"
60+
(lambda ()
61+
(displayln oldest-now)))])
4862

4963
((if dry-run? -system*/print -system*)
5064
git-exe

compute.rkt

+5-18
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,8 @@
1313

1414
(define exists-flag (if dry-run? 'error 'truncate))
1515

16-
(define commits
17-
(filter-input
18-
string-split
19-
git-exe
20-
"log"
21-
"--pretty=%H %P"))
22-
23-
(define head-commit (caar commits))
24-
25-
(define commit->parents
26-
(for/hash ([cs (in-list commits)])
27-
(values (car cs) (cdr cs))))
28-
29-
(define commit->children
30-
(for*/fold ([ht (hash)]) ([(k v) (in-hash commit->parents)]
31-
[(c) (in-list v)])
32-
(hash-update ht c (lambda (p) (cons k p)) null)))
16+
(define-values (commits head-commit commit->parents commit->children)
17+
(extract-commits))
3318

3419
;; One particular path that we sync forks to:
3520
(define main-line-commits
@@ -207,6 +192,7 @@
207192
(find-newest-older-cut oldest-relevant-commit))
208193

209194
(printf "relevant commits bounded by ~a\n" oldest-relevant)
195+
(define drop-oldest? (not (hash-ref relevants oldest-relevant #f)))
210196
(hash-set! relevants oldest-relevant #t)
211197

212198
(with-output-to-file (build-path dest-dir "relevants.rktd")
@@ -220,7 +206,8 @@
220206
(lambda ()
221207
(write (list oldest-relevant
222208
(let ([parents (hash-ref commit->parents oldest-relevant)])
223-
(and (pair? parents) (car parents)))))))
209+
(and (pair? parents) (car parents)))
210+
drop-oldest?))))
224211

225212
(define how-many-relevant? (hash-count relevants))
226213
(define how-many-filtered? (for/sum ([i (in-list commits)]

filter.rkt

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
(printf "\n# git-slice: filtering relevant commits ...\n\n")
1010
(define start-time (current-milliseconds))
1111

12-
(define-values (oldest-relevant start-at-commit)
12+
(define-values (oldest-relevant start-at-commit drop-oldest?)
1313
(apply values
1414
(call-with-input-file (build-path dest-dir "oldest.rktd")
1515
read)))

git.rkt

+24-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
(provide git-exe
44
filter-input
55
-system*
6-
-system*/print)
6+
-system*/print
7+
extract-commits)
78

89
(define git-exe (find-executable-path "git"))
910

@@ -27,3 +28,25 @@
2728

2829
(define (-system* cmd . args) (apply system* cmd (filter values args)))
2930
(define (-system*/print . args) (displayln (apply ~a (add-between (filter values args) " "))))
31+
32+
33+
(define (extract-commits)
34+
(define commits
35+
(filter-input
36+
string-split
37+
git-exe
38+
"log"
39+
"--pretty=%H %P"))
40+
41+
(define head-commit (caar commits))
42+
43+
(define commit->parents
44+
(for/hash ([cs (in-list commits)])
45+
(values (car cs) (cdr cs))))
46+
47+
(define commit->children
48+
(for*/fold ([ht (hash)]) ([(k v) (in-hash commit->parents)]
49+
[(c) (in-list v)])
50+
(hash-update ht c (lambda (p) (cons k p)) null)))
51+
52+
(values commits head-commit commit->parents commit->children))

tests/test.rkt

+175
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
#lang racket
2+
(require racket/runtime-path
3+
rackunit)
4+
5+
(define-runtime-module-path-index main "../main.rkt")
6+
7+
(define work-dir (make-temporary-file "~a-git-slice"
8+
'directory))
9+
10+
(define (reset-dir dir)
11+
(delete-directory/files #:must-exist? #f dir)
12+
(make-directory* dir))
13+
14+
(define repo-dir (build-path work-dir "repo"))
15+
(reset-dir repo-dir)
16+
17+
(define commit-counts (hash))
18+
19+
;; ----------------------------------------
20+
;; Script a repository with interesting moves, branching, etc.
21+
22+
(parameterize ([current-directory repo-dir])
23+
(system "git init")
24+
25+
(define (slices->msg line l)
26+
(~s (apply ~a #:separator " " "CHANGED:" line l)))
27+
(define (merge-counts a b)
28+
(for/fold ([a a]) ([(k v) (in-hash b)])
29+
(hash-update a k (lambda (n) (+ n v)) 0)))
30+
(define (increment-counts counts slices)
31+
(for/fold ([ht counts]) ([slice (in-list slices)])
32+
(hash-update ht slice add1 0)))
33+
34+
(define n 32)
35+
(define (create p)
36+
;; Create a file that will not be considered a copy
37+
;; of any other file.
38+
(make-directory* (path-only p))
39+
(call-with-output-file*
40+
p
41+
(lambda (o)
42+
(for ([i 100])
43+
(display (random 100) o)
44+
(displayln (make-bytes n 60) o))
45+
(set! n (add1 n)))))
46+
(define (modify p)
47+
(define s (file->bytes p))
48+
(call-with-output-file*
49+
#:exists 'update
50+
p
51+
(lambda (o)
52+
(displayln (bytes-append
53+
(subbytes s 0 n)
54+
(make-bytes 100 n)
55+
(subbytes s n))
56+
o)
57+
(set! n (add1 n)))))
58+
(define (move s d)
59+
(make-directory* (path-only d))
60+
(system (~a "git mv " s " " d)))
61+
(define (copy s d)
62+
(make-directory* (path-only d))
63+
(copy-file s d))
64+
(define (do-commit line . slices)
65+
(set! commit-counts (increment-counts commit-counts slices))
66+
(system (~a "git add . && git commit -m " (slices->msg line slices))))
67+
(define-syntax (commit stx)
68+
(syntax-case stx ()
69+
[(_ slice ...)
70+
#`(do-commit #,(syntax-line stx) slice ...)]))
71+
(define (fork a b)
72+
(define old-counts commit-counts)
73+
(set! commit-counts (hash))
74+
(system "git branch left && git checkout left")
75+
(a)
76+
(define left-counts commit-counts)
77+
(set! commit-counts (hash))
78+
(system "git checkout master")
79+
(b)
80+
(define right-counts commit-counts)
81+
(define both-counts (merge-counts left-counts right-counts))
82+
(define slices (hash-keys both-counts))
83+
(system (~a "git merge -m " (slices->msg 0 slices) " left"
84+
" && git branch -d left"))
85+
(set! commit-counts (merge-counts old-counts both-counts)))
86+
87+
;; ------------------------------------------------------------
88+
;; Files with names ending in "..._Alpha" will end up in "alpha",
89+
;; etc., and each of those directories corresponds to a slice to
90+
;; try. Each `commit` names the end slices that should include
91+
;; the commit.
92+
93+
(create "a/x_Alpha")
94+
(commit "Alpha")
95+
(modify "a/x_Alpha")
96+
(create "a/y_Alpha")
97+
(commit "Alpha")
98+
(create "b/x_Beta")
99+
(create "b/y_Beta")
100+
(commit "Beta")
101+
(move "a/x_Alpha" "a/z_Alpha")
102+
(move "a/y_Alpha" "c/y_Alpha")
103+
(commit "Alpha")
104+
(copy "b/x_Beta" "b/z_Beta")
105+
(commit "Beta")
106+
107+
(fork
108+
(lambda ()
109+
(create "c/x_Gamma")
110+
(commit "Gamma")
111+
(modify "a/z_Alpha")
112+
(commit "Alpha"))
113+
(lambda ()
114+
(create "d/x_Delta")
115+
(commit "Delta")
116+
(modify "b/z_Beta")
117+
(move "b/y_Beta" "c/y_Beta")
118+
(commit "Beta")))
119+
120+
;; Move all into place:
121+
(move "a/z_Alpha" "alpha/x_Alpha")
122+
(move "c/y_Alpha" "alpha/y_Alpha")
123+
(move "b/x_Beta" "beta/x_Beta")
124+
(move "c/y_Beta" "beta/y_Beta")
125+
(move "b/z_Beta" "beta/z_Beta")
126+
(move "c/x_Gamma" "gamma/x_Gamma")
127+
(move "d/x_Delta" "delta/x_Delta")
128+
(commit "Alpha" "Beta" "Gamma" "Delta"))
129+
130+
;; ----------------------------------------
131+
;; Extract and check slices
132+
133+
(define (slice slice)
134+
(define slice-dir (build-path work-dir "slice"))
135+
(reset-dir slice-dir)
136+
(parameterize ([current-directory work-dir])
137+
(system "git clone repo slice"))
138+
139+
(define dir (string-foldcase slice))
140+
141+
(parameterize ([current-directory slice-dir]
142+
[current-command-line-arguments (vector dir)]
143+
[current-namespace (make-base-namespace)])
144+
(define-values (name base) (module-path-index-split main))
145+
(dynamic-require (module-path-index-join name base) #f))
146+
147+
(check-equal? (directory-list slice-dir)
148+
(map string->path (list ".git" dir)))
149+
150+
(parameterize ([current-directory slice-dir])
151+
(define o (open-output-bytes))
152+
(parameterize ([current-output-port o])
153+
(system "git log"))
154+
(define commit-count
155+
(for/fold ([count 0]) ([l (in-lines (open-input-string
156+
(get-output-string o)))])
157+
(cond
158+
[(regexp-match? #rx"^commit " l)
159+
(add1 count)]
160+
[(regexp-match? #rx"^CHANGED: " l)
161+
(check-true (member slice (string-split l)))]
162+
[else count])))
163+
(check-equal? commit-count (hash-ref commit-counts slice))
164+
(unless (equal? commit-count (hash-ref commit-counts slice))
165+
(exit))
166+
))
167+
168+
(slice "Alpha")
169+
(slice "Beta")
170+
(slice "Gamma")
171+
(slice "Delta")
172+
173+
;; ----------------------------------------
174+
175+
(delete-directory/files work-dir)

0 commit comments

Comments
 (0)