복구와 안전

복구와 reflog

git은 웬만해서는 무언가를 잃어버리지 않아요. 어려운 건 그걸 다시 찾는 일이죠.

Reflog

HEAD의 모든 이동과 각 브랜치의 모든 이동이, 무엇 때문에 일어났는지와 함께 기록돼 있어요. 체크아웃, 리셋, 리베이스, amend, 강제로 들어온 페치까지요. 과거의 어느 항목에서든 체크아웃하거나, 거기서 브랜치를 만들거나, 거기로 hard 리셋할 수 있어요.

Reflog 뷰어

"방금 엉뚱한 브랜치를 리셋했어요" 버튼이 바로 이거예요.

WIP 스냅숏

커밋하지 않은 작업은 reflog가 구해 줄 수 없는 유일한 것이라서, Gitcito가 대신 스냅숏을 찍어 둬요. 작업 트리 전체 — 수정된 파일, 스테이징된 파일, 추적되지 않는 파일까지 — 일회용 인덱스를 거쳐 커밋으로 만들고 refs/gitcito/wip 아래에 붙들어 놓아요. 진짜 인덱스도, 스태시 목록도 전혀 건드리지 않아요.

WIP 스냅숏

스냅숏은 세 가지가 찍어요:

트리거 언제
가드 파괴적인 동작 — 버리기, 정리, hard 리셋, 커밋에서 복원 — 직전에 자동으로. 기본으로 켜져 있고, 스냅숏 대화상자에서 켜고 끌 수 있어요.
타이머 저장소가 열려 있는 동안 5 / 15 / 30분마다.
직접 지금 스냅숏 찍기 버튼으로.

정말 중요한 건 가드예요. 작업이 영영 사라지는 순간은 대개 실수로 버리기를 누른 바로 다음이거든요. 가드가 켜져 있으면 그 상태가 스냅숏으로 남아 있어요 — 목록을 열고, 복원을 누르고, 다시 숨을 쉬면 돼요.

스냅숏을 선택하면 담긴 파일들을 보고, 어느 파일이든 변경 내용을 미리 보고, 파일 하나만 복원하거나 트리 전체를 복원할 수 있어요. 복원은 스냅숏에서 파일을 꺼내 지금 있는 사본 위에 덮어써요 — 그 전에 가드 스냅숏을 먼저 찍어 두니까, 복원 자체도 되돌릴 수 있어요.

알아 둘 만한 한계. 타이머나 가드가 돌았을 때 새로운 게 없으면 아무것도 기록하지 않아요. 복원은 파일을 덮어쓰고 다시 만들지만, 스냅숏 이후에 새로 만든 파일을 지우지는 않아요. 무시되는 파일은 담기지 않아요. 스냅숏은 로컬의 숨은 레퍼런스라서, 절대 푸시되지 않고, git gc로부터 안전하며, 최신 50개만 보관돼요.

안내형 bisect

커밋에 좋음과 나쁨을 표시하고, 범위가 좁아지는 것을 지켜보다가, 첫 번째 나쁜 커밋에 도착하세요. Gitcito가 몇 단계 남았는지 세어 주니까, 답까지 두 번 남았는지 열 번 남았는지 알 수 있어요.

안내형 bisect

명령에게 판단을 맡기기

범위를 잡아 놓고 나면 명령에게 판단 맡기기로 탐색 전체를 git bisect run에 넘길 수 있어요. git이 후보를 하나씩 체크아웃하고, 여러분의 명령을 실행하고, 그 종료 코드를 읽어요.

종료 코드 뜻
0 좋음 — 버그는 여기 없어요
125 이건 시험할 수 없어요. 건너뛰세요
그 밖의 값 나쁨

테스트 스위트는 이미 그 언어를 쓰고 있어요. npm test 하나로 대개 끝나는 게 그 때문이에요. Gitcito는 이 프로젝트가 가진 스크립트를 클릭 한 번으로 채워 넣게 해 주고, 실행하는 동안 출력을 흘려보내 주며, 질문 하나 답하지 않아도 첫 번째 나쁜 커밋에 도착해요.

탐색을 테스트 스위트에 넘길 준비가 된 명령 입력 상자

주의할 점. 이 명령은 git이 시험하는 모든 커밋에서 실행돼요. 그러니 배포하거나, 퍼블리시하거나, 저장소 바깥에 무언가를 쓰는 명령은 그 일을 여러 번 하게 돼요. 읽고 보고만 하는 것으로 한정하세요. 중지는 실행을 죽이되 세션은 열어 두므로 손으로 계속 표시해 나갈 수 있고, 중단은 bisect를 완전히 끝내요.

엉뚱한 이유로 실패하는 명령 — 이를테면 히스토리의 그 지점에는 의존성이 없다든가 — 은 멀쩡한 커밋을 나쁨으로 표시해서 탐색을 엉뚱한 곳으로 보내요. 래퍼 스크립트에서 125로 종료하는 것이 git이 마련해 둔 탈출구예요.

남아 있는 잠금 파일

git은 곧 쓸 대상 옆에 .lock 파일을 두고, 쓰기가 끝나면 지웁니다. 그 파일을 쥔 채 죽은 프로세스 — 죽어버린 편집기, git commit 도중 닫힌 터미널, 원격 참조를 정리하다 죽은 fetch — 는 잠금을 남기고, 그때부터 모든 쓰기가 같은 줄로 실패합니다:

error: could not delete references: cannot lock ref 'refs/remotes/origin/x':
Unable to create '…/refs/remotes/origin/x.lock': File exists.

저장소가 망가진 게 아닙니다. 그냥 파일 하나가 길을 막고 있을 뿐입니다.

Gitcito는 먼저 몇 번 다시 시도합니다. 실행 중인 git이 쥔 잠금은 보통 밀리초 안에 풀리기 때문입니다. 그래도 풀리지 않으면, 실패가 텍스트 벽 대신 대화상자를 엽니다. 디스크에 남은 잠금 전부와 각각의 나이, 그리고 그것들을 지우고 실패한 작업을 다시 실행하는 버튼 하나입니다.

나이가 논거의 전부입니다. 30초보다 어린 잠금은 아직 일하는 git의 것으로 보고 Gitcito는 삭제를 거부합니다 — 대신 기다렸다 다시 시도하자고 제안합니다. 더 오래된 것은 오래된 순서로 삭제 후보가 되며, 동의하기 전에 무엇을 확인해야 하는지 대화상자가 분명히 말합니다. 지금 이 저장소에서 편집기도 터미널도 다른 Git 클라이언트도 작업하고 있지 않다는 것. 살아 있는 쓰기 밑에서 잠금을 빼는 것이 인덱스가 찢어지는 경로입니다.

훑는 범위는 저장소 자신의 git 디렉터리와 공용 디렉터리라서, 연결된 worktree의 잠금도 찾습니다. 서브모듈은 건너뜁니다 — 다른 저장소의 것이고, 그 저장소를 열어 정리하면 됩니다.

되돌리기 / 다시 하기

대부분의 작업은 되돌리기 스택에 항목을 하나 쌓아 두기 때문에, git이 허용하는 한에서는 ⌘Z가 마지막 작업을 되돌려요.

함께 보기: 그동안 바뀐 것 · 스태시

GitHub에서 이 번역 개선하기