
status 명령은 이번엔 index를 적극적으로 사용해서 저번에 구현할 때 보다 비교적 쉽게 할 수 있었다.
// geet/command/geetStatus.kt
package geet.command
import geet.enums.StageObjectStatus
import geet.exception.BadRequest
import geet.geetobject.GeetBlob
import geet.util.const.*
import geet.util.getRelativePathFromRoot
import java.io.File
data class StatusObject( // status 결과로 나오는 개체들의 상태를 나타내는 data class
val newFiles: MutableList<String> = mutableListOf(),
val modifiedFiles: MutableList<String> = mutableListOf(),
val deletedFiles: MutableList<String> = mutableListOf()
)
data class StatusResult( // status 결과를 나타내는 data class
val staged: StatusObject,
val unstaged: StatusObject,
val untracked: List<String>
)
fun geetStatus(commandLines: Array<String>): Unit {
if (commandLines.size != 1) { // 항상 'geet status'와 같은 식으로 입력 받음
throw BadRequest("status 명령어는 다른 옵션을 가지지 않습니다.: ${red}${commandLines.joinToString(" ")}${resetColor}")
}
val statusResult = getStatusResult()
printStatusResult(statusResult)
}
fun getUntrackedFiles(dir: File): List<String> { // dir 하위 파일들 중 추적하지 않는 파일들 목록을 얻는 함수
val untrackedFiles = mutableListOf<String>()
dir.listFiles()?.forEach {
if (ignoreManager.isIgnored(it)) {
return@forEach
}
if (it.isDirectory) {
untrackedFiles.addAll(getUntrackedFiles(it))
} else {
val filePath = getRelativePathFromRoot(it)
val objectInStage = indexManager.searchObjectFromStage(filePath)
val objectInLastCommit = indexManager.searchObjectFromLastCommit(filePath)
// 스테이지에도 없고 최근 커밋에도 없으면 추적하지 않는 파일
if (objectInStage == null && objectInLastCommit == null) {
untrackedFiles.add(filePath)
}
}
}
return untrackedFiles
}
fun getStatusResult(): StatusResult { // 스테이터스 결과를 반환하는 함수
val statusResult = StatusResult(
staged = StatusObject(),
unstaged = StatusObject(),
untracked = getUntrackedFiles(File("."))
)
// 스테이지 개체들로 루프
indexManager.indexData.stageObjects.forEach { stageObject ->
val file = File(stageObject.blob.filePath)
val blob = GeetBlob(content = file.readText(), filePath = getRelativePathFromRoot(file))
when (stageObject.status) { // 개체 상태에 따라
StageObjectStatus.NEW -> { // 새로운 개체라면
if (stageObject.blob.hash != blob.hash) { // 현재 작업 디렉토리와 다르면 언스테이지 목록에도 추가
statusResult.unstaged.newFiles.add(stageObject.blob.filePath)
}
// 스테이지 목록에 추가
statusResult.staged.newFiles.add(stageObject.blob.filePath)
}
StageObjectStatus.MODIFIED -> { // 수정된 개체라면
if (stageObject.blob.hash != blob.hash) {
statusResult.unstaged.modifiedFiles.add(stageObject.blob.filePath)
}
statusResult.staged.modifiedFiles.add(stageObject.blob.filePath)
}
// 삭제된 개체라면 스테이지만 추가(스테이지에 추가되었는데 언스테이지일 수가 없음)
StageObjectStatus.DELETED -> statusResult.staged.deletedFiles.add(stageObject.blob.filePath)
else -> return@forEach
}
}
// 스테이지에 추가되지 않은 삭제된 파일들 추가
indexManager.indexData.lastCommitObjects.forEach { lastCommitObject ->
val file = File(lastCommitObject.filePath)
val objectInStage = indexManager.searchObjectFromStage(lastCommitObject.filePath)
// 파일이 존재하지 않으면서 스테이지에 같은 경로의 개체가 없거나 그 개체의 상태가 새로운 파일 또는 수정된 파일인 경우
if (!file.exists() && (objectInStage == null || objectInStage.status != DELETED)) {
statusResult.unstaged.deletedFiles.add(lastCommitObject.filePath)
}
}
return statusResult
}
// StatusResult 토대로 결과 출력
fun printStatusResult(statusResult: StatusResult): Unit {
println("** 스테이지된 변경사항 **")
statusResult.staged.newFiles.forEach { println("${green} 새로운 파일: $it${resetColor}") }
statusResult.staged.modifiedFiles.forEach { println("${green} 수정된 파일: $it${resetColor}") }
statusResult.staged.deletedFiles.forEach { println("${green} 삭제된 파일: $it${resetColor}") }
println()
println("** 스테이지되지 않은 변경사항 **")
statusResult.unstaged.newFiles.forEach { println("${yellow} 새로운 파일: $it${resetColor}") }
statusResult.unstaged.modifiedFiles.forEach { println("${yellow} 수정된 파일: $it${resetColor}") }
statusResult.unstaged.deletedFiles.forEach { println("${yellow} 삭제된 파일: $it${resetColor}") }
println()
println("** 추적되지 않는 파일 **")
statusResult.untracked.forEach { println("${red} $it${resetColor}") }
println()
}