@@ -17,6 +17,7 @@ import org.eclipse.jgit.diff.DiffFormatter
1717import org.eclipse.jgit.diff.DiffEntry
1818import org.eclipse.jgit.diff.RawText
1919import org.eclipse.jgit.api.Git
20+ import org.eclipse.jgit.lib.AnyObjectId
2021import org.eclipse.jgit.lib.Repository
2122import org.eclipse.jgit.revwalk.RevCommit
2223import org.eclipse.jgit.revwalk.RevWalk
@@ -29,15 +30,21 @@ import java.util.Date
2930/* *
3031 * Represents a code line in a file revision.
3132 */
32- class RevCommitLine (val commit : RevCommit , val file : String , val line : Int )
33+ class RevCommitLine (val commit : RevCommit , val fileId : AnyObjectId ,
34+ val file : String , val line : Int ,
35+ val isDeleted : Boolean ) {
36+
37+ val id : String
38+ get() = " ${fileId.getName()} :$line "
39+ }
3340
3441/* *
3542 * Represents a code line in repo's history.
3643 *
3744 * TODO(Alex): the text arg is solely for testing proposes (remove it)
3845 */
39- class CodeLine (val from : RevCommitLine , val to : RevCommitLine ,
40- val text : String ) {
46+ class CodeLine (val repo : Repository ,
47+ val from : RevCommitLine , val to : RevCommitLine ) {
4148
4249 // TODO(alex): oldId and newId may be computed as a hash built from commit,
4350 // file name and line number, if we are going to send the data outside a
@@ -48,18 +55,39 @@ class CodeLine(val from: RevCommitLine, val to: RevCommitLine,
4855 * identify a line and update its lifetime computed at the previous
4956 * iteration.
5057 */
51- val oldId: String = " "
58+ val oldId : String
59+ get() = from.id
5260
5361 /* *
5462 * Id of the code line in a revision, where the line was deleted, or a head
5563 * revision, if the line is alive.
5664 */
57- val newId: String = " "
65+ val newId : String
66+ get() = to.id
5867
5968 /* *
6069 * The code line's age in seconds.
6170 */
62- val age = to.commit.getCommitTime() - from.commit.getCommitTime()
71+ val age : Long
72+ get() = (to.commit.getCommitTime() - from.commit.getCommitTime()).toLong()
73+
74+ /* *
75+ * The code line text.
76+ */
77+ val text : String
78+ get() = RawText (repo.open(from.fileId).getBytes()).getString(from.line)
79+
80+ /* *
81+ * Email address of the line's author.
82+ */
83+ val email : String
84+ get() = to.commit.authorIdent.emailAddress
85+
86+ /* *
87+ * True if the line is deleted.
88+ */
89+ val isDeleted : Boolean
90+ get() = to.isDeleted
6391
6492 /* *
6593 * A pretty print of a code line; debugging.
@@ -70,8 +98,9 @@ class CodeLine(val from: RevCommitLine, val to: RevCommitLine,
7098 val td = df.format(Date (to.commit.getCommitTime().toLong() * 1000 ))
7199 val fc = " ${from.commit.getName()} '${from.commit.getShortMessage()} '"
72100 val tc = " ${to.commit.getName()} '${to.commit.getShortMessage()} '"
101+ val state = if (isDeleted) " deleted in" else " last known as"
73102 return " Line '$text ' - '${from.file} :${from.line} ' added in $fc $fd \n " +
74- " last known as '${to.file} :${to.line} ' in $tc $td "
103+ " ${state} '${to.file} :${to.line} ' in $tc $td "
75104 }
76105}
77106
@@ -81,13 +110,10 @@ class CodeLine(val from: RevCommitLine, val to: RevCommitLine,
81110class CodeLongevity (private val localRepo : LocalRepo ,
82111 private val serverRepo : Repo ,
83112 private val api : Api ,
84- private val git : Git , tailRev : String = " " ) {
113+ private val git : Git ) {
85114 val repo: Repository = git.repository
86115 val head: RevCommit =
87116 RevWalk (repo).parseCommit(repo.resolve(RepoHelper .MASTER_BRANCH ))
88- val tail: RevCommit ? =
89- if (tailRev != " " ) RevWalk (repo).parseCommit(repo.resolve(tailRev))
90- else null
91117 val df = DiffFormatter (DisabledOutputStream .INSTANCE )
92118
93119 init {
@@ -154,9 +180,9 @@ class CodeLongevity(private val localRepo: LocalRepo,
154180 * Returns a list of code lines, both alive and deleted, between
155181 * the revisions of the repo.
156182 */
157- fun getLinesList () : List <CodeLine > {
183+ fun getLinesList (tail : RevCommit ? = null ) : List <CodeLine > {
158184 val codeLines: MutableList <CodeLine > = mutableListOf ()
159- getLinesObservable().blockingSubscribe { line ->
185+ getLinesObservable(tail ).blockingSubscribe { line ->
160186 codeLines.add(line)
161187 }
162188 return codeLines
@@ -166,30 +192,31 @@ class CodeLongevity(private val localRepo: LocalRepo,
166192 * Returns an observable for for code lines, both alive and deleted, between
167193 * the revisions of the repo.
168194 */
169- private fun getLinesObservable () : Observable <CodeLine > =
195+ fun getLinesObservable (tail : RevCommit ? = null) : Observable <CodeLine > =
170196 Observable .create { subscriber ->
171197
172- val treeWalk = TreeWalk (repo)
173- treeWalk .setRecursive(true )
174- treeWalk .addTree(head.getTree())
198+ val headWalk = TreeWalk (repo)
199+ headWalk .setRecursive(true )
200+ headWalk .addTree(head.getTree())
175201
176202 val files: MutableMap <String , ArrayList <RevCommitLine >> = mutableMapOf ()
177203
178204 // Build a map of file names and their code lines.
179- while (treeWalk.next()) {
180- val path = treeWalk.getPathString()
181- val fileLoader = repo.open(treeWalk.getObjectId(0 ))
205+ while (headWalk.next()) {
206+ val path = headWalk.getPathString()
207+ val fileId = headWalk.getObjectId(0 )
208+ val fileLoader = repo.open(fileId)
182209 if (! RawText .isBinary(fileLoader.openStream())) {
183210 val fileText = RawText (fileLoader.getBytes())
184211 var lines = ArrayList <RevCommitLine >(fileText.size())
185212 for (idx in 0 .. fileText.size() - 1 ) {
186- lines.add(RevCommitLine (head, path, idx))
213+ lines.add(RevCommitLine (head, fileId, path, idx, false ))
187214 }
188215 files.put(path, lines)
189216 }
190217 }
191-
192- getDiffsObservable().blockingSubscribe { (commit, diffs) ->
218+
219+ getDiffsObservable(tail ).blockingSubscribe { (commit, diffs) ->
193220 // A step back in commits history. Update the files map according
194221 // to the diff.
195222 for (diff in diffs) {
@@ -210,12 +237,12 @@ class CodeLongevity(private val localRepo: LocalRepo,
210237 continue
211238 }
212239
213- // File was deleted, put its lines into the files map.
240+ // File was deleted, initialize the line array in the files map.
214241 if (diff.changeType == DiffEntry .ChangeType .DELETE ) {
215242 val fileLoader = repo.open(oldId)
216243 val fileText = RawText (fileLoader.getBytes())
217- val lines = ArrayList < RevCommitLine >(fileText.size())
218- files.put(oldPath, lines )
244+ files.put(oldPath,
245+ ArrayList < RevCommitLine >(fileText.size()) )
219246 }
220247
221248 // If a file was deleted, then the new path is /dev/null.
@@ -238,15 +265,13 @@ class CodeLongevity(private val localRepo: LocalRepo,
238265 var insEnd = edit.getEndB()
239266 Logger .debug(" ins ($insStart , $insEnd )" )
240267
241- val fileLoader = repo.open(newId)
242- val fileText = RawText (fileLoader.getBytes())
243-
244268 for (idx in insStart .. insEnd - 1 ) {
245- val from = RevCommitLine (commit, newPath, idx)
269+ val from = RevCommitLine (commit, newId,
270+ newPath, idx, false )
246271 var to = lines.get(idx)
247- val cl = CodeLine (from, to, fileText.getString(idx))
248- subscriber.onNext(cl)
272+ val cl = CodeLine (repo, from, to)
249273 Logger .debug(" Collected: ${cl.toString()} " )
274+ subscriber.onNext(cl)
250275 }
251276 lines.subList(insStart, insEnd).clear()
252277 }
@@ -264,7 +289,8 @@ class CodeLongevity(private val localRepo: LocalRepo,
264289
265290 var tmpLines = ArrayList <RevCommitLine >(delCount)
266291 for (idx in delStart .. delEnd - 1 ) {
267- tmpLines.add(RevCommitLine (commit, oldPath, idx))
292+ tmpLines.add(RevCommitLine (commit, oldId,
293+ oldPath, idx, true ))
268294 }
269295 lines.addAll(delStart, tmpLines)
270296 }
@@ -282,12 +308,22 @@ class CodeLongevity(private val localRepo: LocalRepo,
282308 // them all into the result lines list, so the caller can update their
283309 // ages properly.
284310 if (tail != null ) {
285- for ((file, lines) in files) {
286- for (idx in 0 .. lines.size - 1 ) {
287- val from = RevCommitLine (tail, file, idx)
288- val cl = CodeLine (from, lines[idx],
289- " no data (too lazy to compute)" )
290- subscriber.onNext(cl)
311+ val tailWalk = TreeWalk (repo)
312+ tailWalk.setRecursive(true )
313+ tailWalk.addTree(tail.getTree())
314+
315+ while (tailWalk.next()) {
316+ val filePath = tailWalk.getPathString()
317+ val lines = files.get(filePath)
318+ if (lines != null ) {
319+ val fileId = tailWalk.getObjectId(0 )
320+ for (idx in 0 .. lines.size - 1 ) {
321+ val from = RevCommitLine (tail, fileId,
322+ filePath, idx, false )
323+ val cl = CodeLine (repo, from, lines[idx])
324+ Logger .debug(" Collected (tail): ${cl.toString()} " )
325+ subscriber.onNext(cl)
326+ }
291327 }
292328 }
293329 }
@@ -298,7 +334,8 @@ class CodeLongevity(private val localRepo: LocalRepo,
298334 /* *
299335 * Iterates over the diffs between commits in the repo's history.
300336 */
301- private fun getDiffsObservable (): Observable <Pair <RevCommit , List <DiffEntry >>> =
337+ private fun getDiffsObservable (tail : RevCommit ? ) :
338+ Observable <Pair <RevCommit , List <DiffEntry >>> =
302339 Observable .create { subscriber ->
303340
304341 val revWalk = RevWalk (repo)
0 commit comments