LabHub
배우기 러닝패스 코스

Git in Practice

reset, revert and reflog in Practice

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

reset 세 모드, amend, revert, reflog 를 각각 독립된 저장소에서 실행해 결과를 비교하고, 상황별로 무엇을 써야 하는지 치트시트로 정리합니다. 산출물은 /root/gitx2/ 아래에 둡니다.

왜 중요한가

되돌리기 사고의 대부분은 명령을 몰라서가 아니라, 그 명령이 무엇까지 덮어쓰는지 모르고 실행해서 생깁니다. --soft 는 잃는 것이 없고, 기본값 --mixed 는 스테이징 상태만 잃으며, --hard 는 커밋되지 않은 모든 변경을 잃습니다. 위험도는 명령 이름이 아니라 "이 명령이 커밋되지 않은 변경을 덮어쓰는가"로 매겨야 합니다. 그리고 두 번째 축이 있습니다. 커밋이 이미 다른 사람의 저장소에 있다면 그 커밋을 없애는 모든 방법은 남의 이력을 깨뜨립니다. 그때는 이력을 고치는 대신 덧붙이는 것이 답입니다. 이 실습은 그 차이를 말이 아니라 커밋 개수와 파일 존재 여부로 확인시켜 줍니다.

단계

전역 git 신원이 없습니다. 저장소를 만들 때마다 로컬 user.email / user.name 을 설정해야 커밋이 됩니다.

  1. /root/gitx2/mkrepo.sh 를 만들고 chmod +x 합니다. 첫 인자($1)로 받은 경로에(없으면 만들고) 저장소를 초기화하고, 브랜치는 main, 커밋 5개를 만듭니다. n번째 커밋은 fn.txt(f1.txt … f5.txt)를 추가하고 제목은 c1c5 입니다. 채점기는 임시 디렉터리에 이 스크립트를 실행해 확인하므로, 경로를 하드코딩하지 말고 반드시 "$1" 을 쓰세요.
  2. /root/gitx2/amend 에 스크립트로 저장소를 만들고, 마지막 커밋의 제목만 c5 fixed 바꿉니다. 커밋 수는 5개 그대로여야 하고, reflog 에 amend 기록이 남아야 하며, 바뀌기 전 해시와 현재 해시는 달라야 합니다.
  3. /root/gitx2/soft 에 저장소를 만들고 커밋 하나를 되돌립니다. 결과: 커밋 4개, f5.txt 가 인덱스에 스테이징된 상태, 작업 트리에도 f5.txt 존재.
  4. /root/gitx2/mixed 에 저장소를 만들고 커밋 하나를 되돌립니다. 결과: 커밋 4개, f5.txt스테이징돼 있지 않고, 작업 트리에는 남아 있어야 합니다(git status --porcelain 에서 ?? 또는 M).
  5. /root/gitx2/hard 에 저장소를 만들고 커밋 하나를 되돌립니다. 결과: 커밋 4개, f5.txt 가 사라지고, f4.txt 는 남아 있으며, 작업 트리가 완전히 깨끗해야 합니다.
  6. /root/gitx2/revert 에 저장소를 만들고 c3 커밋의 변경만 상쇄합니다. 결과: 커밋 6개, 제목이 Revert 로 시작하는 커밋 존재, 원래의 c3 커밋도 이력에 그대로 남아 있을 것, f3.txt 는 사라지고 f1/f2/f4/f5.txt 는 남아 있을 것.
  7. /root/gitx2/reflog 에 저장소를 만듭니다. 되돌리기 전에 현재 HEAD 해시를 /root/gitx2/before.txt 에 저장합니다(파일에는 해시 문자열만 있어야 합니다). 그다음 커밋 2개를 강제로 날렸다가, 참조 로그를 보고 원래 위치로 복구합니다. 결과: HEAD 가 기록해 둔 해시와 같고 커밋은 5개, reflog 에 reset 기록이 남아 있어야 합니다.
  8. /root/gitx2/undo.md 에 치트시트를 씁니다. 본문에 --soft, --mixed, --hard, revert, reflog 가 모두 등장해야 하고, 아래 세 줄이 있어야 합니다.
    • pushed= — 이미 푸시해서 다른 사람이 받아 간 커밋을 되돌릴 때 쓸 명령 (한 단어)
    • staging_only= — 스테이징만 풀고 파일은 남기는 reset 옵션
    • worktree_wiped= — 커밋되지 않은 변경까지 날리는 reset 옵션

참고

저장소 생성 스크립트 만들기

/root/gitx2/mkrepo.sh 를 만들고 chmod +x 합니다. 첫 인자($1)로 받은 경로에(없으면 만들고) 저장소를 초기화하고, 브랜치는 main, 커밋 5개를 만듭니다. n번째 커밋은 fn.txt(f1.txt … f5.txt)를 추가하고 제목은 c1c5 입니다. 채점기는 임시 디렉터리에 이 스크립트를 실행해 확인하므로, 경로를 하드코딩하지 말고 반드시 "$1" 을 쓰세요.

/root/gitx2/mkrepo.sh 는 첫 인자로 받은 경로에 저장소를 새로 만듭니다. 경로가 아직 없을 수도 있으니 먼저 만들어야 하고, 전역 신원이 없으므로 저장소마다 로컬 신원을 설정해야 커밋이 됩니다. chmod +x 를 잊지 마세요.

마지막 커밋 메시지 고치기

/root/gitx2/amend 에 스크립트로 저장소를 만들고, 마지막 커밋의 제목만 c5 fixed 바꿉니다. 커밋 수는 5개 그대로여야 하고, reflog 에 amend 기록이 남아야 하며, 바뀌기 전 해시와 현재 해시는 달라야 합니다.

/root/gitx2/amend 에 저장소를 찍어 내고 마지막 커밋의 제목만 바꿉니다. amend 는 커밋 수를 늘리지 않지만 새 커밋 객체를 만들기 때문에 해시가 달라지고, 이전 해시는 reflog 에 남습니다.

커밋만 풀고 스테이징은 남기기

/root/gitx2/soft 에 저장소를 만들고 커밋 하나를 되돌립니다. 결과: 커밋 4개, f5.txt 가 인덱스에 스테이징된 상태, 작업 트리에도 f5.txt 존재.

/root/gitx2/soft 에서 커밋 하나를 되돌리되 변경 내용이 인덱스에 그대로 남아야 합니다. 세 모드 중 잃는 것이 아무것도 없는 모드입니다.

인덱스까지 되돌리기

/root/gitx2/mixed 에 저장소를 만들고 커밋 하나를 되돌립니다. 결과: 커밋 4개, f5.txt스테이징돼 있지 않고, 작업 트리에는 남아 있어야 합니다(git status --porcelain 에서 ?? 또는 M).

/root/gitx2/mixed 에서 커밋을 되돌리되 스테이징은 풀리고 파일은 작업 트리에 남아야 합니다. 옵션을 붙이지 않았을 때의 기본 동작이 바로 이것입니다.

작업 트리까지 되돌리기

/root/gitx2/hard 에 저장소를 만들고 커밋 하나를 되돌립니다. 결과: 커밋 4개, f5.txt 가 사라지고, f4.txt 는 남아 있으며, 작업 트리가 완전히 깨끗해야 합니다.

/root/gitx2/hard 에서 커밋과 함께 파일도 사라져야 합니다. 커밋되지 않은 변경을 덮어쓰는 유일한 모드이므로 실행 전에 항상 한 번 더 생각해야 합니다.

이력을 남기며 되돌리기

/root/gitx2/revert 에 저장소를 만들고 c3 커밋의 변경만 상쇄합니다. 결과: 커밋 6개, 제목이 Revert 로 시작하는 커밋 존재, 원래의 c3 커밋도 이력에 그대로 남아 있을 것, f3.txt 는 사라지고 f1/f2/f4/f5.txt 는 남아 있을 것.

/root/gitx2/revert 에서 중간 커밋 하나의 변경만 상쇄합니다. 이력이 줄어들면 안 되고 오히려 커밋이 하나 늘어야 합니다. 이미 다른 사람에게 간 커밋에는 이 방법만 안전합니다.

잃어버린 커밋 되찾기

/root/gitx2/reflog 에 저장소를 만듭니다. 되돌리기 전에 현재 HEAD 해시를 /root/gitx2/before.txt 에 저장합니다(파일에는 해시 문자열만 있어야 합니다). 그다음 커밋 2개를 강제로 날렸다가, 참조 로그를 보고 원래 위치로 복구합니다. 결과: HEAD 가 기록해 둔 해시와 같고 커밋은 5개, reflog 에 reset 기록이 남아 있어야 합니다.

/root/gitx2/reflog 에서 되돌리기 전 해시를 먼저 파일에 적어 두고, 일부러 커밋을 날린 뒤 참조 로그를 보고 그 위치로 되돌아옵니다. before.txt 에는 해시 문자열만 있어야 합니다.

상황별 치트시트 정리

/root/gitx2/undo.md 에 치트시트를 씁니다. 본문에 --soft, --mixed, --hard, revert, reflog 가 모두 등장해야 하고, 아래 세 줄이 있어야 합니다.

/root/gitx2/undo.md 에 세 모드와 revert, reflog 를 모두 언급하고 pushed= / staging_only= / worktree_wiped= 세 줄로 상황별 정답을 적습니다. 이미 푸시한 커밋에는 이력을 고치는 명령을 쓰지 않습니다.