Git 상황별 명령어 tips

git의 경우 튜토리얼들과 설명들이 매우 많은편이지만, 처음 접하는 사람들은 명령어들의 숫자나 개념이 익숙하지 않아서 어떤 상황에 어떤 명령어를 넣어야하는지는 정작 힘들 경우가 많다. 그래서 이 포스트에서는 상황을 가정하고 해당 상황일때 어떻게 대처해야하는지를 시나리오별로 정리해보려고 한다. 시나리오들은 생각나는대로 계속 추가 예정이다.

  • 참고1: git에대해 기본적인 내용을 공부하고싶으면 이 사이트를 추천한다. http://learnbranch.urigit.com/ 인터랙티브한 방식으로 개념을 잘 설명하고있어서 처음 배울때 많은 도움이 된다.

  • 참고2: http://git-scm.com/book/ko git에대한 기술적인 상세한 내용을 보려면 이곳을 참고할것.

커밋한 내용을 버리고 이전 커밋 상태로 돌리기

상황 설명:

코드를 작성하다가 실수로 잘못된 수정들을 많이 해서 문제가 생겼을경우, 이것들을 지난 커밋 상태로 돌려놓고 다시 코드를 테스트해보고 싶은 경우들이 많이 있다.

해결하기:

이때 보통 어떤부분을 수정했는지 사람이 완벽하게 다 기억하지는 못하기 때문에 손으로 일일히 복구하기 보단 git의 기능을 이용하여 지난 커밋으로 변경하는 것이 훨씬 빠르고 안정적이다.

  1. 저장소 전체(모든파일) 하드리셋하기

    하드리셋을 할 경우 해당 커밋으로 저장소를 돌려놓으면서 그 이후의 commit 상태들은 다 지워지게됨에 주의하자.(수정된 내용들뿐 아니라 커밋 기록들까지 아예 깨끗히 삭제된다.)

    레포지토리의 마지막 커밋 상태(HEAD)로 돌아가려면 다음명령어를 입력한다.

    git reset --hard

    다음과 같이 실행하면 현재 레포지토리의 HEAD로부터 과거 3번째 커밋으로 돌아간다.

    git reset --hard HEAD~3
  2. 파일별로 수정내용 돌려놓기

    저장소 전체를 돌려놓는 것이 아닌 몇몇 파일만 원래대로 돌려놓으려면 unstage 된 상태에서 체크아웃 명령어를 이용하여 다음과같이 하면 된다.

    git checkout 파일명

    수정된 파일이 이미 커밋을 위해 stage되어있을경우 다음 명령어를 통해 unstage 시킨 후 체크아웃을 해야한다.

    git reset 파일명
  3. 만약 변경하려는 로컬저장소의 커밋이 이미 remote 저장소에 push가 된 상태인경우 reset 명령어를 사용하면 안되고 revert를 사용해야 한다. 로컬에서 reset 해버리면 commit 기록이 삭제되어 해당 저장소의 커밋 히스토리를 공유하고있는 다른 사용자의 저장소에 문제가 생길 수 있기 때문이다. revert 의경우 돌려놓으려는 커밋의 반대되는 내용을 커밋하는 방식으로 상태를 돌려놓는기때문에 다른 사용자들에게도 정상적으로 반영될 수 있다.

현재 작업중인 내용을 저장소 상태를 저장해두고 이전 커밋 상태로 돌리기

상황 설명:

코드를 작성하다보면 새로 작성했거나 수정한 코드에 뭔가 문제가 생겨서 부분적으로 동작이 하지 않는 경우가 생긴다. 이때 어떤부분이 문제가되는지 확인하거나, 기존코드에서도 동일한 문제가 생기는지 확인하려면 저장소 상태를 이전 커밋상태로 checkout 하게된다. 이때 새로 작성한 부분들 또한 잃어버리지 않고 안전하게 보관해 두려면 어떻게 해야할까?

해결하기:

이전커밋 상태로 돌리기 전에 현재 저장소의 상태를 저장하려면 두가지 방법이있다.

  1. commit을 해서 실제로 현재 상태를 저장하는 경우
  2. stash 명령을 이용해서 임시로 현재 상태를 저장하는 경우

1번의 경우는 다들 익숙한 개념이니 넘어가고 stash 기능을 설명해보도록 하겠다. ‘stash’ 란 ‘안전한 곳에 넣어두다’ 라는 뜻을 가졌다. 이 말 뜻 그대로 stash 명령은 현재 변경 상태를 잠시 안전하게 보관해 두었다가 나중에 필요할때 꺼내서 사용할 수 있게 해주는 기능을 의미한다.

다음 명령어를 통해 현재 상태를 저장한다.

git stash (혹은 git stash save)

다음 명령어를 통해서 stash에 저장된 가장 최근의 상태를 불러와서 현재 저장소에 적용 후, 해당 stash는 삭제한다.

git stash pop

stash의 내용을 적용한 후에도 해당 stash를 계속 보관하고 싶을 경우에는 다음 명령어를 이용한다.

git stash apply

pop이나 apply 과정에서 충돌(conflict)가 발생할경우에는 일반적인 merge 상황에서 처럼 동일한 방법으로 해결해주면 된다.

마지막으로 stash된 내용들을 모두 삭제하려면 다음 명령어를 이용한다.

git stash clear

저장소에 untracked 상태의 파일들만 삭제하고 싶은 경우

상황 설명:

untracked 파일들은 git에서 트래킹 되고있지 않기 떄문에 stash명령을 사용하더라도 없어지지 않고 그대로 남아있다. 이런 상황에서 pull을 받아올 내용중에 현재 남아있는 untracked 파일들과 충돌이 나는 내용이 있을경우 pull이 불가능해진다. 이런경우 untracked 파일만 찾아서 지워버리고 싶은 경우가 있다.

해결하기:
아래와같이 명령어를 날리면 untracked file (-f), untracked directory(-d) 들이 모두 삭제된다.

git clean -fd

커밋(commit) 메시지 내용을 변경하거나 합치고 싶을 경우

상황 설명:

메이저한 내용들을 수정하고 멋지게 커밋메시지를 작성해서 커밋했는데, 잠시 후 마이너한 버그를 발견해서 코드를 또 수정하게 되었다. 지저분한 커밋 히스토리를 남기고 싶지않아서, 이 마이너한 수정사항을 독립적인 커밋이 아닌 기존 메이저 커밋에 포함시키고 싶을경우 어떻게 해야 할까?

해결하기:

커밋된 내용을 수정 또는 통합하기 위해선 다음과 같은 명령어를 사용하면 된다. 현재 레포지토리의 HEAD로부터 과거 3번째 커밋까지를 인터랙티브한 방법으로 수정하겠다는 명령어이다. 수정하고 싶은 커밋 수많큼 뒤에 숫자로 적어주면 된다.

git rebase -i HEAD~3

인터랙티브 모드에서, 통합할 커밋은 squash 옵션을 주면되고, 커밋 메시지만 수정하려면 reward 를 하면 된다1.

  • 주의: 이미 리모트 서버로 push된 내용이 있을경우에는 커밋 내용을 변경할 경우 리모트 서버와 싱크가 맞지않아 혼란이 발생하기 때문에 항상 push되지 않은 경우만 rebase를 하는것이 좋다.

인터랙티브 모드에대한 더 자세한 설명은 다음 문서를 참조하자.

http://git-scm.com/book/ko/Git-도구-히스토리-단장하기

현재 상태를 완전히 버리고 현재 브랜치의 커서를 원하는 커밋으로 이동하기

상황 설명:

잡다하게 수정하던 내용을 버리고 깨끗한 상태로 원하는 다른 커밋으로 상태를 완전히 이동하고 싶다면?

해결하기:

git reset --hard ${COMMIT_HASH}

현재 수정 내용을 커밋 없이 패치파일로 만들어서 전달하고 싶은 경우

** 상황 설명:**

로컬에서 수정한 내용을 다른곳에 보내서 이어서 수정하고 싶지만 커밋으로 기록을 남기는 것은 부담스러운 경우

해결하기:

수정 내용을 패치로 만들기

git diff > test.patch

패치 파일을 받은 후 해당 내용을 코드에 반영하기

patch -p1 < test.patch

버그 수정을 위한 브랜칭(브랜치에서 특정 커밋만 반영하기)2

상황 설명:

개발중에 현재 상태를 보존해 둔 상태로 현재 계속 발생하고있는 버그를 수정해야 하는 상황에 처했다. 하지만 간단한 버그가 아니여서 먼저 bug_fix 라는 branch를 생성했고, 며칠에 걸려 찾느라 버그를 찾기위한 logging 명령어들과 기타 테스트코드 또한 히스토리를 남기기 위해 bug_fix branch에 지속적으로 커밋을 했다.

결국 버그는 코드의 한 부분만 수정하면 되는것으로 밝혀졌고, 이 최종 수정을 bug_fix branch에 마지막으로 커밋했다. 이때 디버깅을 위한 logging 코드를 제외하고 실제 버그를 수정하는 최종 수정된 커밋내용만 master 브랜치에 적용하고 싶을 경우 어떻게 하면 될까?

해결하기:

불필요한 디버그 코드는 bug_fix 브랜치에 남겨둔채 마지막 커밋만 master 브랜치로 가져오려면 cherry-pick 명령어를 이용하면 된다.

master 브랜치를 먼저 선택한 후

git checkout master

다음 명령어를 입력하면 bug_fix 브랜치의 HEAD 포인터가 가리키고있는 커밋의 내용(가장 최근 커밋 내용)이 동일하게 master 브랜치에 커밋되게된다.

git cherry-pick bug_fix

이미 리모트로 push된 마지막 커밋을 삭제하고싶을경우

마지막으로 수정해서 푸시한 커밋에 문제가 있을경우 이를 돌려놓고 싶을 경우가 있다. 단, 푸시된 커밋을 다른 유저가 이미 받아갔을경우에는 소스가 꼬이게되므로 리모트 저장소에있는 마지막 커밋을 아무도 받아가지 않은 경우에만 가능하다.

로컬에서 먼저 마지막 커밋을 리셋한 후에

git reset --hard HEAD^

force push 옵션을 줘서 강제로 리모트저장소로 푸시해버리면 된다.

git push -f

실수로 브랜치를 삭제한 경우 복구하기

remote에 push하지 않은 브랜치를 실수로 삭제했거나, remote에 있는 내용까지 force delete를 했다면 눈앞이 캄캄해진다. 하지만 이런 상황에서도 git은 우리에게 살아날 구멍을 남겨두었다.

git reflog

reflog 명령어를 이용하면 브랜치들의 마지막 커서가 옮겨다녔던 로그들이 쭉 나타난다 (심지어 삭제된 commit들 일지라도). 이중에서 삭제되었던 브랜치에 있었던 원하는 커밋의 sha1 값을 찾아서 체크아웃 해주면 해당 commit으로 커서가 옮겨가게 된다.

git checkout [sha1]

이 상태에서 바로 새롭게 브랜치를 생성하면 다시 이 브랜치로부터 작업을 재개 할 수 있다.

부록: Git GUI Client 소스트리(SourceTree) 소개

비트버켓(bitbucket)이던 깃허브(github)던 개인 git서버를 쓰던 가장 추천하는 GUI 클라이언트는 소스트리이다. github에서 제공하는 클라이언트의 경우 매우 기능이 제한적인데 반해, 소스트리의 경우는 위에서 설명했던 커맨드라인에서 가능한 거의 대부분의 명령어들을 마우스 클릭 몇번으로 실행할 수 있다. 게다가 무료이며 Windows, Mac 모두 지원하니 강력 추천한다.

SourceTree