저번 시간에는 커밋을 다루는 방법으로 긴 커맨드에 alias 지정하기와 커밋 간 차이 알아보기, 그리고 HEAD와 git reset에 대해 알아봤습니다.
이번 시간에는 git reset의 세 가지 옵션과 Head로 git reset하기, 커밋에 tag 달기를 배워보겠습니다.
git reset에는 hard 옵션 외에도 --soft
와 --mixed
가 있습니다. 이들을 이해하기 위해서는 Git의 세 가지 작업 영역인 working directory와 staging area, repository에 대한 이해가 선행되어야 합니다.
이 내용은 앞서 'Git의 세 가지 작업 영역'에서 다룬 내용입니다. 잊어버렸다면 한 번 더 확인해주세요.
이들을 먼저 알아야 하는 이유는 git reset의 세 가지 옵션에 따라 리셋되는 작업 영역이 다르기 때문입니다. 옵션별로 달라지는 영역은 아래 표를 통해 확인할 수 있습니다.
먼저, hard 옵션부터 보겠습니다. hard 옵션을 적용하면 모든 작업 영역의 내용이 바뀝니다. HEAD가 레포지토리 내부의 옵션 뒤 아이디를 가진 커밋을 가리키고 staging area와 working directory의 내용이 해당 커밋의 내용으로 바뀝니다. 또한, working directory에서 했던 작업 중 해당 커밋을 기준으로 뒤에 했던 모든 작업들이 사라져버립니다.
다음으로 mixed 옵션에서는 HEAD가 옵션 뒤 아이디를 가진 커밋을 가리키고 staging area의 내용을 바꾸는 것까지는 hard 옵션과 같습니다. 그러나 working directory의 모습은 바뀌지 않습니다. 즉, 가장 최근에 작업했던 모습이 그래도 남아있다는 뜻이죠. 이는 아주 중요한 부분이므로 꼭 기억해두세요.
마지막으로 soft 옵션에서는 HEAD가 옵션 뒤 아이디를 가진 커밋을 가리키는 것은 동일하지만 working directory의 내용이나 staging area의 내용이 바뀌진 않습니다.
정리하자면, hard, mixed, soft 옵션은 Git의 세 가지 작업 영역 중 어느 영역까지 리셋을 하느냐로 구분이 가능합니다. 단어가 가진 의미에 맞게 hard는 모든 영역을 바꾸고 soft는 HEAD가 가리키는 커밋만을 바꾸며 mixed는 이들의 중간 즉, working directory를 제외한 나머지 두 작업 영역만을 리셋합니다.
이들 중 상황에 따라 적합한 옵션을 사용하면 되는데요. 다만, hard 옵션은 권장드리지 않습니다. hard 옵션을 적용하면 working directory에서 작업한 내용들조차 모두 리셋되기 때문에 복구가 불가능합니다. 따라서, 웬만하면 mixed와 soft 위주의 사용을 권장해 드립니다.
세 가지 옵션을 실습해보기에 앞서, Sublime Text에 새로운 함수를 추가해보겠습니다. 나눗셈을 수행하는 divide 함수를 작성하겠습니다.
작성을 완료했으면 파일을 저장하고 git bash에서 git add까지 진행합니다. git add까지 마치면 working directory의 내용이 바뀌고 staging area에 변경 사항 저장된 상태가 됩니다.
status를 확인해보면 수정된 calculator.py가 반영되었습니다. 이 상태에서 잠깐 커밋 히스토리를 살펴보겠습니다.
지난 시간에는 HEAD가 'Make README.md'를 가리키고 있었죠? 그때 그대로이네요.
이제 git reset을 통해 HEAD가 이전 커밋인 'Update README.md'를 가리키게 하겠습니다. 옵션 중에는 soft 옵션을 먼저 사용해볼게요.
git reset --soft 9292
그런 다음 repository를 살펴보겠습니다.
그럼 HEAD가 'Make README.md'를 가리키고 있는 것을 확인할 수 있습니다. 그렇다면 working directory의 모습은 어떨까요?
working directory 상에는 앞서 추가했던 divide 함수가 잘 출력됩니다. 이는 working directory가 reset의 영향을 받지 않았다는 것을 의미합니다. staging area도 봅시다.
staging area도 reset의 영향을 받지 않아서 calculator.py 파일이 추가된 상태 그대로 있습니다.
이렇듯 soft 옵션을 주면 HEAD가 가리키는 최신 커밋만 바뀔 뿐, working directory와 staging area의 내용은 바뀌지 않습니다.
여기서 잠깐! 우리가 수정한 내용은 calculator.py뿐인데 staging area에는 README.md 파일도 같이 올라와 있습니다. 왜 그런 걸까요?
앞서 'git reset 옵션 개요' 부분에서 staging area에 올라간 파일이 커밋 이후 staging area에서 사라지는 것은 아니라고 했었는데요. reset 하기 전 커밋 히스토리를 다시 한 번 보겠습니다.
reset 전 HEAD는 'Make README.md'를 가리키고 있습니다. 이는 README 파일을 수정한 내용을 staging area에 반영했다는 것입니다. 이렇게 한 번 staging area에 올려진 파일은 그대로 남아 있습니다.
reset을 하기 전에는 커밋의 README 파일과 staging area의 README 파일이 동일했습니다. 따라서, calculator.py 파일 수정 후에 staging area를 확인했을 때에는 README 파일이 출력되지 않은 겁니다.
그러나 reset을 한 후에는 HEAD가 가리키는 커밋의 README 파일과 staging area의 README 파일 사이에 차이가 생겼기 때문에 README 파일도 staging area에서 보이는 것입니다. modified 된 것이죠.
이번에는 mixed 옵션을 봅시다. HEAD가 'Update README.md' 아래인 'Create README.md'를 가리키게 하겠습니다.
git reset --mixed 309f
mixed 옵션은 HEAD와 더불어 staging area의 모습도 바꾼다고 했습니다. 확인해볼까요?
먼저 repository를 확인하면 HEAD가 'Create README.md'를 가리키고 있는 것을 볼 수 있습니다.
여전히 working directory의 내용은 변하지 않았습니다.
하지만 staging area의 내용은 변했습니다. soft와는 다르게 modified 부분이 빨간색 글씨로 출력되었죠?
여기서 Unstaged changes after reset
이라는 문구의 의미는 staging area에 있던 최신 파일들이 reset 때문에 staging area에서 내려왔다는 것입니다. 이는 즉, staging area가 현재 HEAD가 가리키고 있는 커밋의 모습으로 변했다는 뜻입니다.
마지막으로 hard 옵션을 써보겠습니다. 이번에는 HEAD가 'Add title' 부분을 가리키도록 할 겁니다.
git reset --hard cd73
repository부터 확인해봅시다.
HEAD가 'Add title'을 잘 가리키고 있네요. working directory도 확인해보도록 하죠.
이번에는 추가해줬던 divide 함수가 출력되지 않습니다. 즉, working directory의 내용도 변한 것이죠.
아무 내용도 올라오지 않았는데요. 이는 staging area의 모습이 HEAD가 가리키고 있는 커밋의 모습으로 변했기 때문입니다.
이렇게 hard 옵션을 주면 모든 작업 영역의 내용들이 바뀝니다. 사실, staging area의 모습이 변하는 것은 위험한 일이 아닙니다. git add를 하면 다시 staging area에 변경 사항이 올라올 테니까요. 하지만 working directory는 다릅니다. 리셋 한 번으로 커밋 이후에 작업했던 모든 내용이 날아가 버리기 때문이죠. 따라서, hard 옵션을 사용할 때는 아주 신중히 생각한 후 진행해야 합니다.
물론 복구할 수 있는 방법이 아예 없진 않습니다. 현재 우리는 반복된 리셋으로 인해 작업 초기로 돌아왔습니다. 다시 가장 마지막으로 진행했던 작업으로 돌아오려면 리모트 레포지토리를 활용하면 됩니다. 방법도 아주 간단합니다. git pull 커맨드를 입력하기만 하면 되니까요.
git pull 커맨드를 입력하고 커밋 히스토리를 확인해보니 다시 최신 커밋으로 돌아왔네요. 이런 식으로 리모트 레포지토리에 push만 잘 해두면 실수로 hard 옵션을 사용했더라도, git bash에서의 작업이 엉망이 되었더라도 원하는 지점으로 복구가 가능합니다. 그러니 평소에 꼭 push를 잘 사용해주세요.
git reset [옵션] [커밋 아이디]
보통 git reset 커맨드는 위와 같이 작성합니다. 그런데 이렇게 커밋 아이디를 쓰려면 매번 커밋 아이디를 찾아야 한다는 불편함이 있습니다. 이를 위해 커밋 아이디 대신 적을 수 있는 것을 알려드리겠습니다.
git reset --hard HEAD^
HEAD^
는 현재 HEAD가 가리키고 있는 커밋의 바로 이전 커밋을 나타냅니다. 실제로 실행해봅시다.
처음 HEAD는 'Change into multiply'라는 커밋을 가리키고 있었습니다. 이후, git reset --hard HEAD^
를 실행하니 HEAD가 이전 커밋인 'Add multiply function'을 가리키고 있는 것을 확인해볼 수 있습니다.
만약 이보다 더 전에 있는 커밋을 나타내고 싶다면 ~[숫자]
를 활용하면 됩니다. 현재 HEAD가 가리키는 커밋보다 두 단계 전에 있는 커밋을 가리켜봅시다.
git reset --hard HEAD~2
결과를 확인해보면 HEAD가 'Add multiply function'으로부터 두 단계 전인 'Make README.md look nice'를 가리키고 있습니다.
앞으로 git reset을 할 때 커밋 아이디를 적기가 번거롭다면 HEAD가 현재 가리키는 커밋을 기준으로 한 상대적인 표현법인 HEAD^
와 HEAD~x
를 대신 활용해주세요.
앞서 커밋을 할 때, 커밋에 관한 정보를 커밋 메시지로 남긴다고 배웠습니다. 그런데 커밋 중에는 다른 것들보다 좀 더 중요한 의미가 있는 커밋들이 있습니다. 이런 중요한 커밋에는 커밋 메시지와 더불어 태그(tag)라는 것을 추가적으로 달기도 합니다.
일반적으로 프로젝트에서 주요 버전의 시작점이 되는 커밋에 태그를 답니다.
현재 HEAD는 'Change into multiply'를 가리키고 있습니다. 이 커밋에는 version2
라는 태그를 달고 가장 마지막 커밋에는 version1
이라는 태그를 달고 싶다고 가정해봅시다. 그럼 다음과 같이 커맨드를 작성하면 됩니다.
git tag [태그 이름][커밋 아이디]
생성한 모든 tag를 확인해보겠습니다.
git tag
각 tag와 연결된 커밋도 확인해봅시다.
git show [태그 이름]
그럼 Version_1 태그에 연결된 커밋의 정보가 잘 출력됩니다.
이렇게 새 버전의 시작점이 되는 커밋처럼 중요한 커밋들은 태그를 달아줌으로써 추후 프로젝트 이력을 파악할 때 유용하게 쓰일 수 있습니다.
참고로 태그를 삭제하고 싶다면 옵션 -d
를 달아주면 됩니다.
git tag -d [태그 이름]
git log
는 커밋 히스토리를 출력합니다. --pretty
옵션을 사용하면 커밋 히스토리를 다양한 방식으로 출력할 수 있습니다. 여기에 =oneline
이라는 값을 주면 커밋 하나당 한 줄씩 출력해줍니다.
git show [커밋 아이디]
는 특정 커밋에서 어떤 변경 사항이 있었는지 확인해줍니다.
git commit --amend
는 최신 커밋을 다시 수정해서 새로운 커밋으로 만듭니다.
git config alias.[별명][커맨드]
는 길이가 긴 커맨드에 별명을 붙여서 이후로 별명으로 해당 커맨드를 실행할 수 있도록 설정합니다.
git diff [커밋 a의 아이디] [커밋 b의 아이디]
는 두 커밋 간의 차이를 비교합니다.
git reset [옵션] [커밋 아이디]
는 옵션에 따라 하는 작업이 달라집니다. 옵션을 생략했을 때, 기본값은 --mixed
옵션입니다.
단계별로 나누면,
1단계. HEAD가 특정 커밋을 가리키도록 이동
2단계. staging area도 특정 커밋처럼 리셋
3단계. working directory도 특정 커밋처럼 리셋
이 있는데요. --soft
는 1단계만, --mixed
는 2단계까지, --hard
는 모든 단계를 수행합니다. 이때, 커밋 아이디 대신 HEAD의 위치를 기준으로 한 표기법(HEAD^
, HEAD~3
)을 사용해도 됩니다.
git tag [태그 이름] [커밋 아이디]
는 특정 커밋에 태그를 붙입니다.
이번 시간에는 git reset의 세 가지 옵션과 HEAD를 기준으로 git reset하는 방법, 그리고 커밋에 tag를 다는 방법까지 배웠습니다.
커밋은 Git에서 매우 중요한 활동입니다. 변경 사항을 하나의 버전으로 남기는 과정이기 때문이죠. 따라서, 커밋을 잘 다루는 것이 Git을 잘 다루는 길입니다. 여러 가지 커맨드를 익히고 연습하면서 커밋에 익숙해지시길 바랍니다.
* 이 자료는 CODEIT의 'Git으로 배우는 버전 관리' 강의를 기반으로 작성되었습니다.