요즘IT
위시켓
최근 검색어
전체 삭제
최근 검색어가 없습니다.

여러 사람과 함께 작업하다 보면 프로젝트에는 다양한 커밋이 등록됩니다. 곧 PR(Pull Request)을 통해 이러한 커밋들이 하나의 브랜치로 합쳐지죠. 다만 많은 사람이 저장소 하나를 공유하면 어떤 브랜치 전략을 사용하든 종종 충돌이 발생하고는 합니다.

회원가입을 하면 원하는 문장을
저장할 수 있어요!

다음

회원가입을 하면
성장에 도움이 되는 콘텐츠를
스크랩할 수 있어요!

확인

개발

팀장님에게 칭찬받기 좋은 Git 꿀팁 2가지

년차,
어떤 스킬
,
어떤 직무
독자들이 봤을까요?
어떤 독자들이 봤는지 궁금하다면?
로그인

여러 사람과 함께 작업하다 보면 프로젝트에는 다양한 커밋이 등록됩니다. 곧 PR(Pull Request)을 통해 이러한 커밋들이 하나의 브랜치로 합쳐지죠. 다만 많은 사람이 저장소 하나를 공유하면 어떤 브랜치 전략을 사용하든 종종 충돌이 발생하고는 합니다.

 

깃에서 말하는 충돌이란, 여러 사용자가 동일한 내용을 수정할 때, 깃이 어떤 변경점을 적용해야 하는지 알지 못해 병합 작업을 중단하는 것을 말합니다. 깃 작업을 하다 일어난 충돌을 쉽사리 해결하지 못해 시간을 많이 쓴 경험, 누구나 한 번쯤은 있을 겁니다. 오늘은 그런 여러분을 위해 알아두면 유용한 팁 두 가지를 공유하고자 합니다.

 

 

Bisect - 버그 커밋 쉽게 찾기

첫 번째로 알아볼 커맨드는 Bisect입니다. Bisect는 “두 갈래로 나누다”라는 뜻을 가진 영어 단어인데요. 깃에서도 이와 유사한 원리로 동작하는 커맨드입니다. 특히 문제가 발생한 최초의 커밋을 찾아낼 때 아주 유용하게 사용할 수 있죠. 여러분의 이해를 돕기 위해 한 가지 예시 상황을 같이 살펴보겠습니다.

 

프로젝트의 커밋은 이렇게 되어있다. <출처: 작가>

 

쇼핑몰을 만드는 “my-mall” 프로젝트는 위와 같은 커밋 이력을 갖고 있습니다. 마스터 브랜치에 커밋이 쌓이다가 브랜치가 두 개로 분리되어 각자의 작업을 하고 다시 마스터 브랜치로 합쳐진 모양입니다.

 

문제는 가장 최근 커밋(4f28eec, “unused imports 삭제”)에서 발견되었습니다. 개발자는 프로젝트를 빌드하려고 명령어를 입력했지만, 곧 실패하고 오류 화면을 보게 됩니다.

 

프로젝트 빌드에 실패해 버렸다. <출처: 작가>

 

아무래도 useState를 사용하는 과정에서 오타가 발생한 모양입니다. 그런데 이 오류가 어느 커밋에서 발생했는지 어떻게 알 수 있을까요? 예시 프로젝트는 CRA(Create React App)를 사용했지만, 프로젝트마다 오류 결과를 보여주는 모습이 조금씩 다를 수도 있습니다. 게다가 리액트 프로젝트 환경이 아닐 수도 있고요. Bisect는 이런 상황에서 문제가 발생한 최초의 커밋을 찾아낼 수 있는 아주 강력한 기능입니다.

 

Bisect 동작 원리

Bisect가 동작하는 원리는 이진 탐색과 동일합니다. 처음과 끝을 기준 삼아 중간 지점을 확인하고, 다시 그 중간 지점부터 새로 중간 지점을 설정해 확인하는 방식이죠. 시간 복잡도 역시 이진 탐색과 같이 밑이 2인 O(logN)입니다.

 

Bisect는 예시 이미지에서 숫자 77을 찾는 과정과 동일하게 동작한다. <출처:나무위키 - 이진탐색>

 

Bisect 사용법

Bisect는 다음 명령어로 매우 쉽게 사용할 수 있습니다.

  • git bisect start: Bisect를 시작하는 명령어입니다. 동작을 위해 항상 먼저 입력해야 합니다.
  • git bisect good <commit>: 특정 커밋에 문제가 없을 경우 입력하는 명령어입니다. <commit>에는 커밋 해시를 입력할 수 있는데, 입력하지 않으면 현재 HEAD가 위치한 커밋의 해시를 사용합니다.
  • git bisect bad <commit>: 문제가 발생하는 커밋을 Bisect에 알려줄 때 사용하는 명령어입니다. <commit> 부분은 위와 같습니다.
  • git bisect reset: Bisect를 종료할 때 사용하는 명령어입니다.

 

오류 커밋 찾아가기

현재 HEAD는 프로젝트의 가장 최근 커밋인 “unused imports 삭제”에 위치합니다. 이 상태에서 git bisect start 명령어를 입력하겠습니다. 이제 깃이 사용자가 Bisect를 시작하겠다는 신호를 받아들입니다.

 

<출처: 작가>

 

git bisect start 명령어를 입력하고 나니, good/bad 커밋 입력을 기다린다는 메시지를 확인할 수 있었습니다. 이 명령어는 시작점과 끝점을 입력해야 그때부터 본격적으로 동작합니다. Bisect는 문제가 없는 커밋을 Good, 문제가 있는 버그 커밋을 Bad로 인식합니다.

 

<출처: 작가>

 

현재 HEAD는 가장 최신 커밋에 있는데요, 이 상황에서 프로젝트 빌드가 정상적으로 동작하지 않았으므로 이 커밋은 문제가 있는 커밋이라 볼 수 있습니다. git bisect bad 명령어를 입력해 현재 HEAD는 버그가 존재하는 커밋이라고 알려주면, Bisect에서는 나머지 끝점에 해당하는 정상적인 커밋(Good) 입력을 기다립니다.

 

Bisect는 시작점과 끝점을 모두 입력해야 동작한다. <출처: 작가>

 

위 스크린샷은 최초의 커밋인 “my-mall 프로젝트 설치”의 커밋 해시를 사용해 git bisect good 명령어를 실행한 모습입니다. 시작점과 끝점을 모두 입력받은 Bisect는 동작을 시작합니다. 두 커밋의 절반쯤에 위치한 커밋으로 HEAD를 옮겨 탐색하는 거죠. 

 

Bisect가 동작하면 HEAD의 위치는 양 끝점의 절반쯤 위치로 옮겨진다. <출처: 작가>

 

이제 앞으로 남은 일은 HEAD가 옮겨진 커밋에서 문제가 있는지 검사하는 일입니다. 여전히 문제가 있다면 git bisect bad를, 문제가 없는 정상적인 커밋이라면 git bisect good 명령어를 입력합니다. 곧 Bisect는 다음 목적지로 HEAD를 다시 옮겨가며 최초로 문제가 발생한 커밋을 찾게 됩니다.

 

Bisect 진행 도중에는 커밋의 상태에 따라 HEAD의 위치가 달라진다. <출처: 작가>

 

진행을 따라가며 계속해서 good/bad 커밋을 입력하다 보면 Bisect는 결국 문제가 되는 커밋을 찾게 됩니다. 

 

Bisect가 버그가 있는 커밋을 찾아낸 경우 <출처: 작가>

 

버그를 만든 최초의 커밋을 발견하면 Bisect는 해당 커밋에 대한 상세 정보를 화면에 보여줍니다. 어떤 파일이 바뀌었는지, 커밋의 내용은 무엇인지, 누가 커밋을 했는지 등 정보를 확인할 수 있습니다. 모든 정보를 얻은 다음, Bisect를 더 사용하고 싶지 않으면 git bisect reset 명령어로 종료할 수 있습니다.

 

이렇게 Bisect를 이용하면 아주 빠른 속도로 버그의 출처를 확인할 수 있습니다. 커밋이 10만 개나 쌓여 있다 하더라도 O(logN)의 속도로 버그를 찾아내기까지 필요한 확인은 단 17번입니다. 모든 커밋을 전부 확인하는 것보다 훨씬 강력한 기능이죠. 한 번 사용해 보시는 것을 추천해 드립니다.

 

 

Reflog - 사라진 커밋 찾기

두 번째 커맨드는 Reflog입니다. Bisect보다 더 생소할 수 있는데요, 이 명령어는 우리가 실수로 로컬 환경에 쌓아놓은 커밋을 삭제했을 때 아주 유용하게 사용할 수 있습니다. 여러 브랜치를 옮겨 다니며 작업을 하다 보면 실수하게 마련이죠. 특히 이전에 작업했던 커밋을 잃어버려 기억에 의존하며 처음부터 다시 코드를 짠 경험이 한 번쯤은 있을 겁니다. 이제부터 그럴 때는 Reflog 명령어를 사용해 봅시다.

 

가장 최근 커밋을 실수로 삭제해 버렸다! <출처: 작가>

 

Reflog 명령어는 git reflog를 터미널에 입력하는 것만으로 동작합니다.

 

무시무시한 무언가가 화면에 보인다. <출처: 작가>

 

일반적으로 우리가 보던 로그의 형태와 사뭇 다르게 생겼죠. Reflog 명령어가 깃의 HEAD 변화만을 추적하기 때문입니다. Reflog 명령어를 사용하면 화면에서 HEAD 변화에 따른 결과를 볼 수 있습니다. 위의 스크린샷을 볼까요? HEAD@{0}과 같은 표시가 로그 맨 앞에 나타납니다. 이는 HEAD의 상태 변화가 어떤 순서로 기록되었는지 보여주는 일종의 번호입니다. 중괄호 안의 숫자가 작을수록 가장 최근에 변경된 이력입니다.

 

HEAD@{0} 옆에는 checkout 등 깃 명령어에 해당하는 키워드가 적혀있습니다. 즉, HEAD의 상태가 바뀌었을 때 어떤 일이 발생했는지를 보여주는 내용입니다. 빨간색 박스 안 checkout의 경우, ‘7e894e…’ 커밋에서 ‘e97eca…’ 커밋으로 HEAD를 옮기며 업데이트가 있었다는 내용입니다.

 

실수로 지운 커밋 복구하기

가장 최근 HEAD의 변경점을 다시 볼까요? reset 명령어로 HEAD가 e97eca7 커밋으로 이동했다는 것을 알 수 있습니다. 이 커밋은 Merge branch ‘task-2’에 해당하는 커밋입니다. 우리가 실수로 커밋을 지우고 난 다음, 가장 최근으로 올라온 커밋이기도 하지요.

 

이때 reset 명령어를 사용하면 HEAD를 강제로 옮길 수 있습니다. 즉, git reset --hard <commit> 명령어만으로 쉽게 삭제한 커밋을 복구할 수 있다는 뜻입니다. HEAD@{0}의 기록은 이미 커밋이 지워진 다음이니 우리는 HEAD@{1}의 커밋으로 되돌려 보겠습니다.

 

4f28eec 커밋이 우리가 되돌아갈 커밋이다. <출처: 작가>

 

git reset --hard 4f28eec 명령어를 실행하겠습니다. HEAD가 다시 사라졌던 커밋의 위치로 되돌아 갑니다.

 

커밋이 복구되었다! <출처: 작가>

 

이렇게 간단하게 커밋이 되돌아왔습니다! 깃에서는 reset 명령어에 hard 옵션을 사용해도 커밋이 완전히 사라지지 않습니다. 일종의 고아(orphan) 상태로 전환될 뿐이죠. 이는 커밋 기록은 여전히 존재하지만, 그 어떤 커밋과도 연결 지점이 없는 상태를 의미합니다. Reflog를 잘 활용하면 git log 명령어로는 확인할 수 없는 커밋의 고아 상태를 볼 수 있습니다. 이제 여러분의 사라진 커밋도 복구할 수 있겠죠.

 

 

마치며

지금까지 사용 빈도가 그리 높지 않지만 아주 강력한 기능을 가진 깃 명령어, Bisect와 Reflog를 알아보았습니다.

 

Bisect는 버그가 발생한 최초 커밋을 검증 몇 번만으로 찾을 수 있는 명령어였습니다. start로 시작을 선언하고 good, bad 커밋을 입력하면 그때부터 깃이 이진 탐색을 시작하는 방식입니다. O(logN)의 속도로 버그가 처음 발생한 커밋을 찾을 수 있습니다.

 

Reflog는 실수로 지운 커밋을 아주 손쉽게 찾을 수 있는 명령어입니다. 깃은 HEAD의 상태 변화를 따로 추적하고 있습니다. 따라서 HEAD의 위치가 달라지는 모든 명령어를 조회할 수 있죠. 실수로 지운 커밋은 깃에서 완전히 사라지는 것이 아닙니다. 어떠한 커밋과도 연결점이 존재하지 않는 고아 상태로 전환될 뿐입니다. Reflog로 그 위치만 찾는다면, reset 명령어로 HEAD를 재조정해 커밋을 살릴 수 있습니다.

 

이외에도 깃에는 유용한 명령어가 아주 많습니다. 여러분도 종종 새로운 명령어를 익혀 보다 폭넓게 깃을 활용해 보세요!


요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.

좋아요

댓글

공유

공유

괴짜 프론트엔드 개발자
96
명 알림 받는 중

작가 홈

괴짜 프론트엔드 개발자
96
명 알림 받는 중
프론트엔드 개발자로 지내면서 배우고 성장했던 모든 내용을 공유하고자 합니다.
정규직이나 계약직, 프리랜서 등 다양한 경험을 갖고 있으며 여러 기업의 문화를 배웠습니다.
7년차 프론트엔드 개발자로 살고 있으며 리액트, 타입스크립트, 테스트 코드에 관심이 많습니다.
https://medium.com/@moonformeli 블로그에도 간간히 글을 기재하고 있습니다.

좋아요

댓글

스크랩

공유

공유

지금 회원가입하고,
요즘IT가 PICK한 뉴스레터를 받아보세요!

회원가입하기
요즘IT의 멤버가 되어주세요! 요즘IT의 멤버가 되어주세요!
요즘IT의 멤버가 되어주세요!
모든 콘텐츠를 편하게 보고 스크랩해요.
모든 콘텐츠를 편하게 보고 스크랩 하기
매주 PICK한 콘텐츠를 뉴스레터로 받아요.
매주 PICK한 콘텐츠를 뉴스레터로 받기
로그인하고 무료로 사용하기