yg log --graph --oneline --all

Home Stack Notes AboutRSS

1. add와 commit 사이에 남는 변경

작성 · 3분 읽기 · 조회수 —

Git 학습 기록 · 1 / 6

파일을 고른다는 설명에 남은 빈자리

Commit 노트에는 staging이 이렇게 적혀 있다.

staging : commit 하기 전에 commit 할 파일들을 골라놓는 곳. git add를 통해서 추가할 수 있다.

처음 흐름을 잡기에는 충분한 문장이다. 작업한 파일 중 이번 commit에 넣을 것을 git add로 고른다. 그런데 같은 파일을 add한 뒤 다시 수정하면 어느 쪽이 선택된 것일까? 파일 이름 하나를 골랐다는 설명만으로는 그 상태가 잘 보이지 않는다.

이번에 임시 저장소에서 note.txt를 commit한 뒤 내용을 version 2로 바꾸고 add했다. 그다음 commit하지 않은 채 한 줄을 더 붙였다.

printf 'version 2\n' > note.txt
git add note.txt
printf 'version 2\nworking tree only\n' > note.txt
git status --short

실제 출력은 다음 한 줄이었다.

MM note.txt

왼쪽 M은 HEAD와 index 사이가 바뀌었다는 뜻이고, 오른쪽 M은 index와 working tree 사이가 바뀌었다는 뜻이다. 같은 파일에 “다음 commit에 넣기로 고른 버전”과 “그 뒤에 계속 작업한 버전”이 함께 존재한다.

index의 note.txt
version 2

working tree의 note.txt
version 2
working tree only

이 결과를 보고 나면 staging을 파일 목록보다 다음 commit의 후보 snapshot을 담는 index로 읽는 편이 정확하다. git add note.txt는 그 순간의 파일 내용을 index에 복사한다. 이후 working tree를 다시 고쳐도 index의 내용은 자동으로 따라오지 않는다. Git 공식 add 문서가 commit할 내용을 준비하도록 index를 갱신한다고 설명하는 이유다. (git-add, git-status)

add를 취소해도 작업은 남는다

같은 노트에는 이런 명령도 있다.

git restore --staged 파일명

실험의 note.txt에 적용하자 상태는 다음처럼 바뀌었다.

 M note.txt

왼쪽 칸의 변경은 사라지고 오른쪽 칸만 남았다. restore --staged는 index를 HEAD의 내용으로 되돌렸지만 working tree에서 작성 중인 두 줄은 지우지 않았다. “스테이징 취소”가 곧 “수정 취소”는 아니다. 반대로 옵션 없이 git restore note.txt를 실행하면 working tree를 index에 맞추므로 작성 중인 변경이 사라질 수 있다. 둘은 같은 restore라도 대상이 다르다. (git-restore)

그래서 add 이후에 마음이 바뀌었을 때 먼저 물을 질문도 달라진다.

  • 다음 commit 후보에서만 빼고 작업은 남길 것인가
  • working tree의 수정까지 버릴 것인가

첫 번째라면 git restore --staged, 두 번째라면 현재 index나 지정한 source로부터 복원하는 git restore가 맞다. 실행 전 git status와 git diff --staged를 보는 이유도 여기 있다.

cached라는 말이 가리키는 것

rm 노트는 한 줄뿐이다.

git rm -r –cached . : 스테이징에 등록된 모든 파일들 캐시 제거

이 표현은 restore --staged와 비슷하게 들리지만 결과는 다르다. commit된 note.txt에 git rm --cached note.txt를 실행한 결과는 이랬다.

D  note.txt
?? note.txt

working tree의 실제 파일은 그대로 남았다. 대신 index에서는 추적 중인 파일을 제거했기 때문에 삭제가 staged됐고, 디스크에 남은 같은 파일은 untracked로 보였다. 이 상태를 commit하면 이후 snapshot에서 그 경로의 추적이 빠진다. 이미 추적한 파일을 뒤늦게 .gitignore에 넣을 때 이 명령을 함께 쓰는 이유다. (git-rm, gitignore)

git rm -r --cached .은 저장소 전체를 index에서 제거하는 넓은 명령이다. 단순히 add를 취소하려는 상황에 그대로 사용할 명령은 아니다. 특정 파일의 staging만 취소하려면 restore --staged가 의도에 가깝고, 추적 자체를 끝내려면 rm --cached가 맞다.

노트의 “파일을 골라놓는 곳”이라는 문장은 이제 조금 더 구체적으로 읽힌다. add는 파일 이름에 표시만 붙이는 일이 아니라 그 시점의 내용을 index에 준비하는 일이다. working tree, index, HEAD 중 어디를 바꾸려는지 먼저 정하면 add, restore --staged, rm --cached가 서로 닮은 명령으로 섞이지 않는다.

이 글의 실험 전체는 01-staging.sh에서 다시 실행할 수 있다. 스크립트는 mktemp로 만든 새 저장소 안에서만 파일과 index를 변경한다.