Skip to content

Commit 6b55411

Browse files
authored
chore(longevity): use observable to iterate over lines (APP-100) (#33)
chore(longevity): use observable to iterate over lines (APP-100)
1 parent 7757b6a commit 6b55411

File tree

2 files changed

+71
-47
lines changed

2 files changed

+71
-47
lines changed

src/main/kotlin/app/hashers/CodeLongevity.kt

Lines changed: 65 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import app.model.LocalRepo
1212
import app.model.Repo
1313
import app.model.Fact
1414
import app.utils.RepoHelper
15+
import io.reactivex.Observable
1516
import org.eclipse.jgit.diff.DiffFormatter
1617
import org.eclipse.jgit.diff.DiffEntry
1718
import org.eclipse.jgit.diff.RawText
@@ -87,10 +88,14 @@ class CodeLongevity(private val localRepo: LocalRepo,
8788
val tail: RevCommit? =
8889
if (tailRev != "") RevWalk(repo).parseCommit(repo.resolve(tailRev))
8990
else null
91+
val df = DiffFormatter(DisabledOutputStream.INSTANCE)
9092

91-
fun update() {
92-
val codeLines = compute()
93+
init {
94+
df.setRepository(repo)
95+
df.setDetectRenames(true)
96+
}
9397

98+
fun update() {
9499
// TODO(anatoly): Add emails from server or hashAll.
95100
val emails = hashSetOf(localRepo.author.email)
96101

@@ -99,20 +104,20 @@ class CodeLongevity(private val localRepo: LocalRepo,
99104
val totals: MutableMap<String, Int> = emails.associate { Pair(it, 0) }
100105
.toMutableMap()
101106

102-
val repoTotal: Int = codeLines.size
107+
var repoTotal: Int = 0
103108
var repoSum: Long = 0
104-
for (line in codeLines) {
109+
getLinesObservable().blockingSubscribe { line ->
110+
repoTotal++
105111
repoSum += line.age
106112

107113
val email = line.from.commit.authorIdent.emailAddress
108-
if (!emails.contains(email)) {
109-
continue
110-
}
111-
Logger.debug(line.toString())
112-
Logger.debug("Age: ${line.age} secs")
114+
if (emails.contains(email)) {
115+
Logger.debug(line.toString())
116+
Logger.debug("Age: ${line.age} secs")
113117

114-
sums[email] = sums[email]!! + line.age
115-
totals[email] = totals[email]!! + 1
118+
sums[email] = sums[email]!! + line.age
119+
totals[email] = totals[email]!! + 1
120+
}
116121
}
117122

118123
val secondsInDay = 86400
@@ -146,12 +151,23 @@ class CodeLongevity(private val localRepo: LocalRepo,
146151
}
147152

148153
/**
149-
* Scans through the repo for alive and deleted code lines and returns
150-
* a list of all code lines, both alive and deleted, between the given
151-
* revisions.
154+
* Returns a list of code lines, both alive and deleted, between
155+
* the revisions of the repo.
152156
*/
153-
fun compute() : List<CodeLine> {
157+
fun getLinesList() : List<CodeLine> {
154158
val codeLines: MutableList<CodeLine> = mutableListOf()
159+
getLinesObservable().blockingSubscribe { line ->
160+
codeLines.add(line)
161+
}
162+
return codeLines
163+
}
164+
165+
/**
166+
* Returns an observable for for code lines, both alive and deleted, between
167+
* the revisions of the repo.
168+
*/
169+
private fun getLinesObservable(): Observable<CodeLine> =
170+
Observable.create { subscriber ->
155171

156172
val treeWalk = TreeWalk(repo)
157173
treeWalk.setRecursive(true)
@@ -173,30 +189,9 @@ class CodeLongevity(private val localRepo: LocalRepo,
173189
}
174190
}
175191

176-
val df = DiffFormatter(DisabledOutputStream.INSTANCE)
177-
df.setRepository(repo)
178-
df.setDetectRenames(true)
179-
180-
val revWalk = RevWalk(repo)
181-
revWalk.markStart(head)
182-
183-
var commit: RevCommit? = revWalk.next() // move the walker to the head
184-
while (commit != null && commit != tail) {
185-
val parentCommit: RevCommit? = revWalk.next()
186-
187-
Logger.debug("commit: ${commit.getName()}; " +
188-
"'${commit.getShortMessage()}'")
189-
if (parentCommit != null) {
190-
Logger.debug("parent commit: ${parentCommit.getName()}; "
191-
+ "'${parentCommit.getShortMessage()}'")
192-
}
193-
else {
194-
Logger.debug("parent commit: null")
195-
}
196-
192+
getDiffsObservable().blockingSubscribe { (commit, diffs) ->
197193
// A step back in commits history. Update the files map according
198194
// to the diff.
199-
val diffs = df.scan(parentCommit, commit)
200195
for (diff in diffs) {
201196
val oldPath = diff.getOldPath()
202197
val oldId = diff.getOldId().toObjectId()
@@ -250,7 +245,7 @@ class CodeLongevity(private val localRepo: LocalRepo,
250245
val from = RevCommitLine(commit, newPath, idx)
251246
var to = lines.get(idx)
252247
val cl = CodeLine(from, to, fileText.getString(idx))
253-
codeLines.add(cl)
248+
subscriber.onNext(cl)
254249
Logger.debug("Collected: ${cl.toString()}")
255250
}
256251
lines.subList(insStart, insEnd).clear()
@@ -280,7 +275,6 @@ class CodeLongevity(private val localRepo: LocalRepo,
280275
files.set(oldPath, files.remove(newPath)!!)
281276
}
282277
}
283-
commit = parentCommit
284278
}
285279

286280
// If a tail revision was given then the map has to contain unclaimed
@@ -293,11 +287,41 @@ class CodeLongevity(private val localRepo: LocalRepo,
293287
val from = RevCommitLine(tail, file, idx)
294288
val cl = CodeLine(from, lines[idx],
295289
"no data (too lazy to compute)")
296-
codeLines.add(cl)
290+
subscriber.onNext(cl)
297291
}
298292
}
299293
}
300294

301-
return codeLines
295+
subscriber.onComplete()
296+
}
297+
298+
/**
299+
* Iterates over the diffs between commits in the repo's history.
300+
*/
301+
private fun getDiffsObservable(): Observable<Pair<RevCommit, List<DiffEntry>>> =
302+
Observable.create { subscriber ->
303+
304+
val revWalk = RevWalk(repo)
305+
revWalk.markStart(head)
306+
307+
var commit: RevCommit? = revWalk.next() // move the walker to the head
308+
while (commit != null && commit != tail) {
309+
val parentCommit: RevCommit? = revWalk.next()
310+
311+
Logger.debug("commit: ${commit.getName()}; " +
312+
"'${commit.getShortMessage()}'")
313+
if (parentCommit != null) {
314+
Logger.debug("parent commit: ${parentCommit.getName()}; "
315+
+ "'${parentCommit.getShortMessage()}'")
316+
}
317+
else {
318+
Logger.debug("parent commit: null")
319+
}
320+
321+
subscriber.onNext(Pair(commit, df.scan(parentCommit, commit)))
322+
commit = parentCommit
323+
}
324+
325+
subscriber.onComplete()
302326
}
303327
}

src/test/kotlin/test/tests/hashers/CodeLongevityTest.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class CodeLongevityTest : Spek({
6262
testRepo.createFile(fileName, listOf("line1", "line2"))
6363
val rev1 = testRepo.commit("inital commit")
6464
val lines1 = CodeLongevity(
65-
LocalRepo(testRepoPath), Repo(), MockApi(), testRepo.git).compute()
65+
LocalRepo(testRepoPath), Repo(), MockApi(), testRepo.git).getLinesList()
6666

6767
it("'t1: initial insertion'") {
6868
assertEquals(2, lines1.size)
@@ -80,7 +80,7 @@ class CodeLongevityTest : Spek({
8080
testRepo.insertLines(fileName, 1, listOf("line in the middle"))
8181
val rev2 = testRepo.commit("insert line")
8282
val lines2 = CodeLongevity(
83-
LocalRepo(testRepoPath), Repo(), MockApi(), testRepo.git).compute()
83+
LocalRepo(testRepoPath), Repo(), MockApi(), testRepo.git).getLinesList()
8484

8585
it("'t2: subsequent insertion'") {
8686
assertEquals(3, lines2.size)
@@ -102,7 +102,7 @@ class CodeLongevityTest : Spek({
102102
testRepo.deleteLines(fileName, 2, 2)
103103
val rev3 = testRepo.commit("delete line")
104104
val lines3 = CodeLongevity(LocalRepo(testRepoPath), Repo(),
105-
MockApi(), testRepo.git).compute()
105+
MockApi(), testRepo.git).getLinesList()
106106

107107
it("'t3: subsequent deletion'") {
108108
assertEquals(3, lines3.size)
@@ -124,7 +124,7 @@ class CodeLongevityTest : Spek({
124124
testRepo.deleteFile(fileName)
125125
val rev4 = testRepo.commit("delete file")
126126
val lines4 = CodeLongevity(LocalRepo(testRepoPath), Repo(),
127-
MockApi(), testRepo.git).compute()
127+
MockApi(), testRepo.git).getLinesList()
128128

129129
it("'t4: file deletion'") {
130130
assertEquals(3, lines4.size)
@@ -179,7 +179,7 @@ class CodeLongevityTest : Spek({
179179
testRepo.createFile(fileName, fileContent)
180180
val rev1 = testRepo.commit("inital commit")
181181
val lines1 = CodeLongevity(
182-
LocalRepo(testRepoPath), Repo(), MockApi(), testRepo.git).compute()
182+
LocalRepo(testRepoPath), Repo(), MockApi(), testRepo.git).getLinesList()
183183

184184
it("'t2.1: initial insertion'") {
185185
assertEquals(fileContent.size, lines1.size)
@@ -201,7 +201,7 @@ class CodeLongevityTest : Spek({
201201
val rev2 = testRepo.commit("insert+delete")
202202

203203
val lines2 = CodeLongevity(
204-
LocalRepo(testRepoPath), Repo(), MockApi(), testRepo.git).compute()
204+
LocalRepo(testRepoPath), Repo(), MockApi(), testRepo.git).getLinesList()
205205

206206
it("'t2.2: ins+del'") {
207207
assertEquals(22, lines2.size)

0 commit comments

Comments
 (0)