1.1. 변경 취소하기
변경 사항을 커밋하고 나서 공유해서는 안 되는 비밀번호가 포함된 사실을 깨달을 수도 있다.
모든 변경사항을 중앙 저장소에 보내야 하는, 중앙 집중식 저장소를 사용하고 있다면 정말 운이 없는 경우다. 관리자 권한을 가졌다면 어떻게든 처리할 수 있겠지만 잘못하면 데이터가 깨질 수 있다.
반면 Git은 이런 종류의 실수에도 대비하고 있다. 모든 변경은 자신의 컴퓨터에서 일어나며 공용저장소에 푸싱했을 때만 공유된다. 자신만이 지역 저장소와 동기화하고 있으므로 이력을 원하는 대로 고쳐 쓸 수 있다.
1.1.1. 이력 고쳐 쓰기의 위험성
주의) Git을 중앙 집중식 저장소처럼 모든 커밋을 상위 저장소에 보내고 있다면, 이 절의 명령어를 실행할 때 주의해야 한다. 이미 다른 사람이 이전에 올려둔 내용을 가지고 있다면 무언가 공유한 이후에 이력을 변경하면 큰 골치거리가 되기 쉽다.
6.6절 ‘변경 취소하기’에서 다루는 git revert를 제외한, 이 절에서 배우는 모든 명령어는 저장소의 이력을 변경한다. 중앙집중식 저장소에서는 저장소의 이력을 변경하는 동작은 매우 위험하다. 그렇다면 저장소에서 커밋이 이리저리 옮겨지고, 이름을 바꾸거나 심지어는 없어져 버린 경우에는 어떻게 이력을 추적할 수 있을까?
완벽한 분산 개발의 장점 중 하나는 준비가 된 경우에만 공유한다는 점이다.
푸싱하기 전에 먼저 변경 사항을 공유할 준비가 됐는지부터 확실해 해야 한다.
공유할 준비가 되기 전까지 모든 지역 변경 사항을 지역 저장소에 유지하면,
저장소의 이력을 고쳐 쓸 때, 다른 사람에게 발생할 수 있는 문제를 걱정하지 않아도 된다.
변경 사항을 푸싱하면 이런 유연성을 어느 정도 잃게 된다. 자칫 잘못하면 팀원 모두가 변경사항 때문에 문제가 발생했다고 우리를 귀찮게 할 수도 있다.
일단 변경 사항을 푸싱했다면, 다시 이력을 변경해서 이전과 다른 내용을 푸싱하면, 이전 내용을 이미 받아둔 사람들에게 문제가 생길 수 있다.
변경사항 푸싱하기는 7.4 참고.
1.1.2. 커밋 수정하기
익숙하지 않은 새로운 언어로 코드를 작성할 때, 줄의 끝에 구두점이나 세미콜론을 빠뜨리는 상황은 흔히 겪는 일이다.
Git에서 이러한 사소한 오류를 고치는 일은 쉽다.
제대로 수정하고, 변경 사항을 스테이징한 다음, 커밋할 때 --ammend를 추가하면 된다.
이 기능을 확인할 수 있도록 자신의 블로그나, 자주 방문하는 사이트의 링크를 contact.html 파일에 추가하고 일부러 오타를 내보자.
$ git commit –m “add link to blog”–a |
이제 URL을 제대로 수정하고 커밋 명령어에 --amend를 추가하고 변경사항을 다시 커밋한다.
$ git commit –C HEAD –a --amend |
이번에는 아직까지 사용하지 않았던, 새로운 옵션인 –C를 사용했다.
-C 옵션을 추가하면 새로운 메시지, 대신 지정한 커밋의 로그 메시지를 이용한다.
이번 경우에는 HEAD의 로그 메시지를 사용했지만, 모든 유효한 커밋명을 사용할 수 있다.
대문자 대신 소문자로 –c를 사용하면 커밋을 마치기 전에, 기존의 메시지를 수정할 수 있도록 편집기를 열어준다.
커밋 수정하기는, 마지막으로 실행한 커밋에만 사용해야 한다.
실수한 이후에 여러 번 커밋했고, 이를 바로 잡고자 할 경우, 다음 절에서 알아볼 git revert를 사용한다.
1.1.3. 커밋 돌려놓기
때로는 어떤 이유로 다른 사람의 컴퓨터에서는 내가 작성한 코드가 동작하지 않을 수 있다.
특정 아키텍처에 종속적이거나 다른 팀원들이 사용하지 않는 외부 소프트웨어에 의존하는 경우, 다시 변경해서 커밋 하거나 커밋을 돌려놓아야 한다.
가장 간편한 방법으로 기존 커밋을 돌려놓으려면 git revert 를 이용하면 된다.
git revert는 원본 커밋에서 변경한 모든 내용을 거꾸로 변경해서 저장소에 새로 커밋 함으로써 원본 커밋을 돌려놓는다.
보통 돌려놓는 내용을 바로 커밋하지만 –n 매개변수를 추가하면 커밋하지 않는다.
이는 다수의 커밋을 돌려놓으려고 할 때 유용하다.
그냥 –n 매개변수를 지정해서 git revert 명령어를 여러 번 실행하면, Git이 모든 변경 사항을 스테이징하고 커밋하기를 기다린다.
git revert를 사용할 때는 돌려놓을 대상을 알 수 있도록 커밋명을 지정해야 한다.
예를 들어, 540ecb7와 HEAD 커밋을 돌려놓고자 한다면 다음 명령어를 이용한다.
이때, 항상 역순으로 돌려놓아야 한다. 가장 최신 커밋을 먼저 돌려놓자. 그래야만 다수의 커밋을 돌려놓을 때 처리해야 할 불필요한 충돌을 피할 수 있다.
$ git revert –n HEAD … $ git revert –n 540ecb7 … $ git commit –m “revert 45eaf98 and 540ecb7” … |
기본적으로 git revert는 커밋 메시지를 작성하도록 편집기를 실행시킨다.
실행된 편집기에는
‘Revert <원본 로그 메시지>’(<원본 로그 메시지>를 돌려놓는다) 라는 텍스트 다음에
‘This reverts commit <커밋명>’(<커밋명> 커밋을 돌려둔다) 가 기본 메시지로 추가되어 있다.
그냥 기본 메시지를 이용하고자 한다면 git revert에 --no-edit를 추가한다.
일반적으로 커밋할 때와 같이 커밋하는 이유에 대해서 잘 설명해야 한다.
1.1.4. 변경 사항 재설정하기
엔터키를 입력하자마자 개인 비밀번호가 들어있는 구성 파일을 커밋 했음을 깨달았다고 하자. 중앙집중식 버전 관리 시스템에서는 커밋의 모든 흔적을 마법처럼 지우려면 시스템 관리자에게 요청해야 한다.
Git은 사용자가 이와 같은 종류의 실수를 하리라고 가정하고 있다.
따라서 원하는 상태로 저장소를 재설정할 수 있다.
저장소를 재설정하려면 git reset 명령어의 매개변수로 커밋명을 지정하면 된다. 커밋명을 지정하지 않으면 기본값으로 HEAD를 이용한다.
리비전을 지정할 때 ^과 ~N 형식의 커밋명을 이용할 수 있다.
HEAD^은 두개의 커밋을 재설정하고, 540ecb7~3은 540ecb7 이전 3개의 커밋을 재설정한다.
git reset은 저장소를 갱신하고 커밋할 수 있도록 변경 사항을 스테이징한다.
이 기능은 이전 커밋에서 발견한 오류를 수정하려는 경우에 유용하다.
--soft 를 추가하면, 이전 커밋을 모두 스테이징하지만 커밋하지는 않는다.
이렇게 하면, 이전 커밋에 내용을 추가하거나 내용을 빼서 수정할 수 있도록 한다.
git reset의 모든 옵션은 커밋을 지우지만, |
--hard 옵션은 조심해서 사용해야 한다.
--hard를 추가하면 저장소와 작업 트리에서 커밋을 제거해버린다.
저장소에서 이 기능은 ‘되돌리기’버튼이 없는 삭제 버튼과 다름없다.
$ git reset --hard HEAD^ … |
이 명령어는 HEAD 이전 커밋으로 저장소를 재설정한다.
이렇게 하면 이전 2파일을 돌려둔 커밋은 없었던 일로 된다.
1.2. 이력 고쳐 쓰기
정규 코드 리뷰를 통해 자신의 코드를 샅샅이 살펴보는 작업은, 지난 주나 지난 달에 했던 가정이 아직 타당한지 확인하는 좋은 방법이다.
Git를 이용하면 한 걸음 더 나아가 코드의 이력을 검토할 뿐 아니라 수정해서 고쳐 쓸 수 있다.
다음은 코드 고쳐 쓰기가 유용한 몇 가지 사례이다.
l 의미가 더 적합하도록 이력의 순서를 변경하기
사용할 일이 정말 드물기는 하지만, 언제 사용할지 닥치면 알 수 있다.
하나 이상의 커밋이 엉뚱한 위치에 있고 논리적인 순서도 맞지 않을 때 사용하다.
l 여러 개의 커밋을 하나의 커밋으로 합치기
다수의 커밋이 사실은 모두 동일한 사항과 관련되어 있으므로 하나의 큰 커밋이 되어야 한다고 느낀다면 사용한다.
l 하나의 커밋을 여러 개의 커밋으로 나누기
커밋 합치기의 반대 사례이다.
변경사항을 살펴보다, 하나의 커밋에 개별적인 커밋으로 만들고 싶은 여러 개의 변경 사항이 포함되어 있을 때 사용한다.
git rebase –i 를 사용하면 이력을 다듬을 수 있다.
다음은 저장소의 마지막 3개의 커밋이다.
$ git log --pretty=format:“%h %s”HEAD~3.. 45eaf98 add link to blog 540ecb7 copy original to show cross-file blame 222cb82 adding copied lines to showcase git blame |
1.2.1. 커밋 순서 변경하기
git rebase 대화형 모드는 이력을 고쳐 쓸 때 사용하는 도구이다.
대화형 모드로 실행하면 편집기가 실행되어 원하는 대로 변경할 수 있다.
Git에서 편집기를 찾는 방법에 대해서는 4.2 ‘변경 사항 커밋하기’참고.
rebase 를 실행할 때, 작업을 시작할 리비전을 지정해야 한다.
여기서는 HEAD~3이 필요하다.
$ git rebase –i HEAD~3 …설정해놓은 대로 편집기가 실행된다. … pick 222cb82 adding copied lines to showcase git blame pick 540ecb7 copy original to show cross-file blame pick 45eaf98 add link to blog
# Rebase b87524b..45eaf98 onto b87524b # Commands: # … |
편집기에서 #으로 시작하는 줄은 Git에서 무시하는 주석이다.
처음 3줄이 여기서 조작하려는 3개의 커밋이다.
pick 45eaf98 줄을 가장 위로 옮겨보자.
pick 45eaf98 add link to blog pick 222cb82 adding copied lines to showcase git blame pick 540ecb7 copy original to show cross-file blame |
편집기의 내용을 저장하고 종료한다.
그러면 Git은 재정렬을 시작한다.
작업이 끝난 다음 git log 를 다시 실행하면, 새로운 순서를 확인할 수 있다.
$ git log --pretty=format:”%h %s”HEAD~3.. 8c764d3 copy original to show cross-file blame be53bab adding copied lines to showcase git blame 4f7621d add link to blog |
1.2.2. 여러 개의 커밋을 하나로 합치기
블로그와 트위터 커밋을 하나로 합칠 수 있다. 그러니 다시 한번 재정렬한다.
이번에는 트위터 링크 이전 커밋인 0bb3dfb^에 대해서 재정렬한다.
$ git rebase –i 0bb3dfb^ …설정해놓은 대로 편집기가 실행된다. … pick 0bb3dfb add link to twitter pick b87524b commit of original text file pick 4f7621d add link to blog pick be53bab adding copied lines to showcase git blame pick 8c764d3 copy original to show cross-file blame #... |
이번에는 pick 4f7621d가 pick 0bb3dfb 뒤에 오도록 옮겼다.
그리고 옮긴 줄의 pick 을 squash로 바꾼다.
이렇게 변경한 내용은 다음과 같다.
pick 0bb3dfb add link to twitter squash 4f7621d add link to blog pick b87524b commit of original text file pick be53bab adding copied lines to showcase git blame pick 8c764d3 copy original to show cross-file blame |
다시 저장하고 종료한다.
재정렬 작업이 시작되면, 다시 편집기가 뜨면서, 합치려는 2커밋의 커밋메시지를 요청한다.
기본 메시지로 다음과 비슷한 메시지가 채워져 있다.
# This is a combination of two commits. # The first commit’s message is:
add link to twitter
# This is the 2nd commit message:
add link to blog |
메시지를 적절히 조합해서 원하는 대로 수정하고 저장한 후 편집기를 종료하면 Git이 계속해서 재정렬 작업을 진행한다. git log 를 실행해보면 새로운 이력을 볼 수 있다.
$ git log --pretty=format:”%h %s”HEAD~4.. 7509494 copy original to show cross-file blame 8184d47 adding copied lines to showcase git blame 9a750b3 commit of original text file b02376d add link to twitter and blog |
b02376d를 2개의 커밋으로 나눠서 방금한 커밋을 돌려놓자
1.2.3. 하나의 커밋을 여러 개로 나누기
지금까지 커밋 순서를 변경하고 2개의 커밋을 하나로 합쳤다.
하나의 커밋을 2개로 쪼개는 일은 약간 더 작업해야 하지만 시작은 동일하다.
git rebase –i 를 다시 실행한다. 편집기가 실행되면 내용을 다음과 같이 수정한다.
$ git rebase –i HEAD~4 …편집기가 실행된다 … edit b02376d add link to twitter and blog pick 9a750b3 commit of original text file pick 8184d47 adding copied lines to showcase git blame pick 7509494 copy original to show cross-file blame |
첫줄을 edit로 바꿨다.
저장하고 편집기를 종료하면 재정렬 작업이 시작된다.
Git은 편집(edit)하라고 지정해둔 커밋을 만나면 재정렬 작업을 멈추고 다음 내용을 출력한다.
Stopped at b02376d…add link to twitter and blog You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue |
git log –1을 실행하면 마지막 커밋이 이전 절에서 하나로 합친 커밋임을 확인할 수 있다. 이 커밋을 edit로 지정했다.
$ git log -1 add link to twitter and blog |
재정렬 직업은 일시 중지 상태로 사용자가 저장소를 편집할 때까지 기다린다.
마지막 커밋을 취소하고, 2개의 개별적인 커밋이 생성되도록 git reset을 이용해서 커밋을 쪼갤 수 있다.
$ git reset HEAD^ contact.html: locally modified $ git diff diff --git a/contact.html b/contact.html … |
git diff를 실행하면 커밋하려고 대기 중인 모든 변경사항을 보여준다.
2번째 링크를 제거하고 contact.html 파일을 저장한다. 트위터에 대한 링크만 남겨두고 변경한 내용을 커밋한다.
$ git commit –m “add link to twitter”–a … |
이제 블로그에 대한 링크를 다시 추가하고 새로 커밋한다.
$ git commit –m “add link to blog”–a |
이제 커밋을 2개로 나누는 모든 변경 사항이 끝났으니 git rebase –continue를 실행한다.
$ git rebase –continue … |
로그를 살펴보면 합쳐진 커밋 대신에 다시 2개의 커밋이 존재함을 확인할 수 있다.
물론 이런 기능은 git rebase가 제공하는 다른 기능이 없었다면 지원할 수 없었을 것이다.
git rebase는 브랜치를 동기화하거나 이리저리 옮길 때도 사용한다. 이 기능은 9.3 ‘브랜치 재정렬하기’에서 다룬다.
'형상관리 > Git' 카테고리의 다른 글
Git, 분산버전 관리시스템(12) - Gitosis를 이용하여 Git 서버 운용하기 (0) | 2012.04.23 |
---|---|
Git, 분산버전 관리시스템(11) - Git으로 옮겨가기 (0) | 2012.04.23 |
Git, 분산버전 관리시스템(10) – 기본을 넘어서 (0) | 2012.04.23 |
Git, 분산버전 관리시스템(9) - 저장소 조직하기 (0) | 2012.04.23 |
Git, 분산버전 관리시스템(8) - 원격 저장소를 이용하여 작업하기 (0) | 2012.04.23 |
Git, 분산버전 관리시스템(6) - Git 이력 이용하기(1) – 로그/리비전범위/버전간차이점/blame/내용따라가기 (0) | 2012.04.23 |
Git, 분산버전 관리시스템(5) - 브랜치 이해하고 활용하기 (0) | 2012.04.23 |
Git, 분산버전 관리시스템(4) – Git 기초: 추가하고 커밋하기 (0) | 2012.04.23 |
Git, 분산버전 관리시스템(3) – 첫 프로젝트 만들기 (0) | 2012.04.23 |
Git, 분산버전 관리시스템(2) - Git 설정하기 (0) | 2012.04.23 |