
restore는 reset과 달리 HEAD를 건드리지 않고 파일 그 자체만 특정 상태로 복원하는 역할을 한다.
—worktree, —staged, —source 세 가지 옵션을 구현할 예정으로, 다음과 같이 입력된 커맨드 라인으로부터 옵션들을 뽑아왔다.
// geet/commands/porcelain/geetRestore.kt
package geet.commands.porcelain
import geet.exceptions.BadRequest
import geet.utils.commandutil.porcelainutil.getSourceCommitHash
import geet.utils.commandutil.porcelainutil.isSourceOption
data class GeetRestoreOptions(
var worktree: Boolean = true,
var staged: Boolean = false,
var sourceCommitHash: String? = null,
val fileName: String,
)
fun geetRestore(commandLines: Array<String>): Unit {
val geetRestoreOptions = getGeetRestoreOptions(commandLines)
println(geetRestoreOptions)
}
fun getGeetRestoreOptions(commandLines: Array<String>): GeetRestoreOptions {
if (commandLines.size == 1) {
throw BadRequest("파일 명이 입력되지 않았습니다.")
}
if (commandLines.size == 2) {
return GeetRestoreOptions(fileName = commandLines[1])
}
if (commandLines.size == 3) {
val fileName = commandLines[2]
val option = commandLines[1]
if (isSourceOption(option)) {
return GeetRestoreOptions(worktree = false, sourceCommitHash = getSourceCommitHash(option), fileName = fileName)
}
when (option) {
"--worktree" -> return GeetRestoreOptions(fileName = fileName)
"--staged" -> return GeetRestoreOptions(worktree = false, staged = true, fileName = fileName)
else -> throw BadRequest("옵션이 올바르지 않습니다.: ${commandLines[1]}")
}
}
if (commandLines.size == 4) {
if ((commandLines[1] == "--worktree" && commandLines[2] == "--staged") ||
(commandLines[1] == "--staged" && commandLines[2] == "--worktree")) {
return GeetRestoreOptions(worktree = true, staged = true, fileName = commandLines[3])
}
}
throw BadRequest("옵션이 올바르지 않습니다.")
}
// geet/utils/commandutil/porcelainutil/restoreUtil.kt
package geet.utils.commandutil.porcelainutil
fun isSourceOption(option: String): Boolean { // 정규표현식으로 source 옵션인지 확인
val regex = Regex("^--source=HEAD~[0-9]+")
if (!regex.matches(option)) {
return false
}
return true
}
fun getSourceCommitHash(option: String): String { // 입력된 source의 커밋 해시값 가져오기
val index = option.indexOf("~")
val carrotCount = option.substring(index + 1).toInt()
var commitHash = getCurrentRefCommitHash()
for (i in 0 until carrotCount) {
commitHash = getParentCommitFromCommitHash(commitHash)
}
return commitHash
}
입력될 수 있는 옵션은 다음과 같다.
원래 파일명에 ‘.’와 같이 폴더를 넣을수도 있으나 목적은 restore 명령어 자체를 공부하는 것이므로 해주지 않았다.
또한 여러 옵션을 섞어서 쓸 수도 있지만 그것도 고려해주지 않았다.
구현한 restore 함수는 아래와 같다.
// geet/utils/commandutil/porcelainutil/restoreUtil.kt
fun restore(geetRestoreOptions: GeetRestoreOptions) {
// worktree, staged 동시 요청인 경우
if (geetRestoreOptions.worktree && geetRestoreOptions.staged) {
restoreToWorktree(geetRestoreOptions.fileName)
restoreStage(geetRestoreOptions.fileName)
}
// worktree 요청인 경우
if (geetRestoreOptions.worktree && !geetRestoreOptions.staged) {
restoreToWorktree(geetRestoreOptions.fileName)
}
// staged 요청인 경우
if (geetRestoreOptions.staged && !geetRestoreOptions.worktree) {
restoreStage(geetRestoreOptions.fileName)
}
// source 요청인 경우
if (geetRestoreOptions.sourceCommitHash != null) {
restoreToWorktree(geetRestoreOptions.fileName, geetRestoreOptions.sourceCommitHash!!)
}
}
//
fun restoreToWorktree(fileName: String, commitHash: String = getCurrentRefCommitHash()) {
getObjectsFromCommit(commitHash).forEach { // 해당 커밋으로 개체들을 복원, 해당 파일 내용으로 수정
if (getRelativePath(it.path) == getRelativePath(fileName)) {
val file = File(it.path)
file.writeText(it.content)
}
}
}
fun restoreStage(fileName: String) { // 스테이지에서 제거
indexManager.removeObjectFromStagingArea(GeetBlob(fileName, "removed"))
indexManager.writeIndexFile()
}