Skip to content

Commit 8ca12e5

Browse files
committed
don't create slices with multiple heads or initial commits
1 parent 6710e3f commit 8ca12e5

File tree

4 files changed

+138
-19
lines changed

4 files changed

+138
-19
lines changed

chop.rkt

+39-3
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,49 @@
4848
[drop-oldest?
4949
(define-values (commits head-commit commit->parents commit->children)
5050
(extract-commits))
51+
52+
(define cs (hash-ref commit->children oldest-now))
53+
(define new-initials (make-hash))
54+
(let loop ([cs cs])
55+
(for ([c (in-list cs)])
56+
(define ps (remove oldest-now (hash-ref commit->parents c)))
57+
(if (null? ps)
58+
(hash-set! new-initials c #t)
59+
(loop ps))))
60+
(define new-root
61+
(cond
62+
[((hash-count new-initials) . > . 1)
63+
;; Dropping `oldest-now` might would multiple initial
64+
;; commits, which is potentially confusing (to `git-slice`
65+
;; itself, for example). Add an empty commit to serve
66+
;; as the root.
67+
(printf "\n# Adding commit to serve as new initial commit\n")
68+
(-system* git-exe "checkout" "--orphan" "newroot")
69+
(-system* git-exe "rm" "-rf" ".")
70+
(-system* git-exe "commit" "--allow-empty" "-m" "create slice")
71+
(define new-root
72+
(car
73+
(filter-input (lambda (l)
74+
(cond
75+
[(regexp-match #rx"^commit (.*)$" l)
76+
=> (lambda (m) (cadr m))]
77+
[else #f]))
78+
git-exe
79+
"log")))
80+
(-system* git-exe "checkout" "master")
81+
(-system* git-exe "branch" "-D" "newroot")
82+
new-root]
83+
[else #f]))
5184
(with-output-to-file ".git/info/grafts"
5285
(lambda ()
53-
(for ([c (in-list (hash-ref commit->children oldest-now))])
86+
(for ([c (in-list cs)])
5487
(displayln (apply ~a c
5588
#:separator " "
56-
(remove oldest-now
57-
(hash-ref commit->parents c)))))))]
89+
(let ([ps (remove oldest-now
90+
(hash-ref commit->parents c))])
91+
(if new-root
92+
(cons new-root ps)
93+
ps)))))))]
5894
[else
5995
(with-output-to-file ".git/info/grafts"
6096
(lambda ()

compute.rkt

+29-7
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,11 @@
7676
(and lt
7777
(let ([old-start-commit (car lt)])
7878
(define m (member old-start-commit main-line-commits))
79-
(unless m (error 'slide "start commit not found in main line: ~a" old-start-commit))
79+
(unless m (error 'git-slice "start commit not found in main line: ~a" old-start-commit))
8080
(or (member start-commit m)
8181
(begin
8282
(unless (member start-commit main-line-commits)
83-
(error 'slide "new start commit not found in main line: ~a" start-commit))
83+
(error 'git-slice "new start commit not found in main line: ~a" start-commit))
8484
#f)))))
8585

8686
;; Determine the commit range that applies to a file.
@@ -116,11 +116,11 @@
116116
(lambda (m)
117117
(define old-name (cadr m))
118118
(unless (equal? old-name prev-name)
119-
(error 'slice (~a "confused by rename\n"
120-
" current: ~a\n"
121-
" from: ~a\n"
122-
" previous: ~a\n"
123-
" starting name: ~a")
119+
(error 'git-slice (~a "confused by rename\n"
120+
" current: ~a\n"
121+
" from: ~a\n"
122+
" previous: ~a\n"
123+
" starting name: ~a")
124124
current-name
125125
old-name
126126
prev-name
@@ -195,6 +195,28 @@
195195
(define drop-oldest? (not (hash-ref relevants oldest-relevant #f)))
196196
(hash-set! relevants oldest-relevant #t)
197197

198+
(printf "Finding descendents of relevant commits\n")
199+
(define commit->descendents (closure (hash-keys relevants) commit->children))
200+
(define common-descendents
201+
(apply set-intersect
202+
(list->set (hash-keys relevants))
203+
(for/list ([r (in-hash-keys relevants)])
204+
(hash-ref commit->descendents r))))
205+
206+
(when (zero? (set-count common-descendents))
207+
(printf "Adding new commit to serve as common descendant for new HEAD\n")
208+
(-system* git-exe "commit" "--allow-empty" "-m" "merge slice")
209+
(define new-root
210+
(car
211+
(filter-input (lambda (l)
212+
(cond
213+
[(regexp-match #rx"^commit (.*)$" l)
214+
=> (lambda (m) (cadr m))]
215+
[else #f]))
216+
git-exe
217+
"log")))
218+
(hash-set! relevants new-root #t))
219+
198220
(with-output-to-file (build-path dest-dir "relevants.rktd")
199221
#:exists exists-flag
200222
(lambda ()

git.rkt

+23-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
filter-input
55
-system*
66
-system*/print
7-
extract-commits)
7+
extract-commits
8+
closure)
89

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

@@ -49,4 +50,25 @@
4950
[(c) (in-list v)])
5051
(hash-update ht c (lambda (p) (cons k p)) null)))
5152

53+
(let ([num-without-parents (for/sum ([v (in-hash-values commit->parents)])
54+
(if (null? v)
55+
1
56+
0))])
57+
(unless (= 1 num-without-parents)
58+
(error 'git-slice
59+
"expect 1 initial commit, found ~a commits without parents"
60+
num-without-parents)))
61+
5262
(values commits head-commit commit->parents commit->children))
63+
64+
65+
(define (closure start-commits commit->next)
66+
(let ([ht (make-hash)])
67+
(for ([start-commit (in-list start-commits)])
68+
(let loop ([a start-commit])
69+
(or (hash-ref ht a #f)
70+
(let ([s (for/fold ([s (set a)]) ([p (in-list (hash-ref commit->next a null))])
71+
(set-union s (loop p)))])
72+
(hash-set! ht a s)
73+
s))))
74+
ht))

tests/test.rkt

+47-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#lang racket
22
(require racket/runtime-path
3-
rackunit)
3+
rackunit
4+
"../git.rkt")
45

56
(define-runtime-module-path-index main "../main.rkt")
67

@@ -89,33 +90,61 @@
8990
;; etc., and each of those directories corresponds to a slice to
9091
;; try. Each `commit` names the end slices that should include
9192
;; the commit.
93+
;;
94+
;; Gamma is created in one branch, and Delta in another
95+
;; Epsilon is created in two branches, which would give it
96+
;; multiple initial commits without special handling
97+
;; Zeta has modifications in two branches, and none
98+
;; after the branch
9299

93100
(create "a/x_Alpha")
94101
(commit "Alpha")
102+
95103
(modify "a/x_Alpha")
96104
(create "a/y_Alpha")
97105
(commit "Alpha")
106+
98107
(create "b/x_Beta")
99108
(create "b/y_Beta")
100109
(commit "Beta")
110+
101111
(move "a/x_Alpha" "a/z_Alpha")
102112
(move "a/y_Alpha" "c/y_Alpha")
103113
(commit "Alpha")
114+
104115
(copy "b/x_Beta" "b/z_Beta")
105116
(commit "Beta")
106117

118+
(create "z/x_Zeta")
119+
(create "z/y_Zeta")
120+
(commit "Zeta")
121+
107122
(fork
108123
(lambda ()
109124
(create "c/x_Gamma")
110125
(commit "Gamma")
126+
111127
(modify "a/z_Alpha")
112-
(commit "Alpha"))
128+
(commit "Alpha")
129+
130+
(create "e/x_Epsilon")
131+
(commit "Epsilon")
132+
133+
(move "z/x_Zeta" "zeta/x_Zeta")
134+
(commit "Zeta"))
113135
(lambda ()
114136
(create "d/x_Delta")
115137
(commit "Delta")
138+
116139
(modify "b/z_Beta")
117140
(move "b/y_Beta" "c/y_Beta")
118-
(commit "Beta")))
141+
(commit "Beta")
142+
143+
(create "e/y_Epsilon")
144+
(commit "Epsilon")
145+
146+
(move "z/y_Zeta" "zeta/y_Zeta")
147+
(commit "Zeta")))
119148

120149
;; Move all into place:
121150
(move "a/z_Alpha" "alpha/x_Alpha")
@@ -125,12 +154,15 @@
125154
(move "b/z_Beta" "beta/z_Beta")
126155
(move "c/x_Gamma" "gamma/x_Gamma")
127156
(move "d/x_Delta" "delta/x_Delta")
128-
(commit "Alpha" "Beta" "Gamma" "Delta"))
157+
(move "e/x_Epsilon" "epsilon/x_Epsilon")
158+
(move "e/y_Epsilon" "epsilon/y_Epsilon")
159+
(commit "Alpha" "Beta" "Gamma" "Delta" "Epsilon"))
129160

130161
;; ----------------------------------------
131162
;; Extract and check slices
132163

133-
(define (slice slice)
164+
(define (slice slice [extra-commit? #f])
165+
(printf "~a\nSLICING ~a...\n" (make-string 60 #\=) slice)
134166
(define slice-dir (build-path work-dir "slice"))
135167
(reset-dir slice-dir)
136168
(parameterize ([current-directory work-dir])
@@ -160,15 +192,22 @@
160192
[(regexp-match? #rx"^CHANGED: " l)
161193
(check-true (member slice (string-split l)))]
162194
[else count])))
163-
(check-equal? commit-count (hash-ref commit-counts slice))
164-
(unless (equal? commit-count (hash-ref commit-counts slice))
195+
(define expected-count (+ (hash-ref commit-counts slice)
196+
(if extra-commit? 1 0)))
197+
(check-equal? commit-count expected-count)
198+
(unless (equal? commit-count expected-count)
165199
(exit))
166-
))
200+
201+
;; Checks that we didn't create a repo with multiple
202+
;; initial commits (which is a danger with Epsilon):
203+
(call-with-values extract-commits void)))
167204

168205
(slice "Alpha")
169206
(slice "Beta")
170207
(slice "Gamma")
171208
(slice "Delta")
209+
(slice "Epsilon" #t) ; extra commit is a new initial commit
210+
(slice "Zeta" #t) ; extra commit is a new head to join branches
172211

173212
;; ----------------------------------------
174213

0 commit comments

Comments
 (0)