회원가입을 하면 원하는 문장을
저장할 수 있어요!
다음
AWS 이용 중이라면 최대 700만 원 지원받으세요
개발 프로젝트를 시작할 때 가장 먼저 하는 일이 있다. 바로 Git init
명령어로 로컬 레파지토리를 만드는 일이다. 로컬 레파지토리를 GitHub(깃허브)나 GitLab(깃랩) 사이트의 원격 레파지토리와 연결해준 후에 팀원들을 초대하고, 프로젝트를 시작한다. 동료들과 협업하며 알게 된 사실은, 생각보다 Git 커맨드를 제한적으로 사용하는 사람들이 많다는 사실이었다. 동료들의 의견은 이러했다.
회원가입을 하면 원하는 문장을
저장할 수 있어요!
다음
회원가입을 하면
성장에 도움이 되는 콘텐츠를
스크랩할 수 있어요!
확인
개발 프로젝트를 시작할 때 가장 먼저 하는 일이 있다. 바로 Git init
명령어로 로컬 레파지토리를 만드는 일이다. 로컬 레파지토리를 GitHub(깃허브)나 GitLab(깃랩) 사이트의 원격 레파지토리와 연결해준 후에 팀원들을 초대하고, 프로젝트를 시작한다. 동료들과 협업하며 알게 된 사실은, 생각보다 Git 커맨드를 제한적으로 사용하는 사람들이 많다는 사실이었다. 동료들의 의견은 이러했다.
두 가지 의견에 충분히 공감이 가지만, 그럼에도 Git 커맨드들을 다양하게 활용하지 못하는 것이 아쉬웠다.
우선 GUI 툴을 사용할 때 특정 버튼은 잘 보이는 위치에, 특정 버튼은 비교적 찾기 어려운 위치에 배치되어 있기 때문에 만약 어떤 기능을 사용하고 싶더라도 UI 상에서 찾지 못한다면 사용할 수 없는 기능이 되고 만다. 그리고 GUI 툴은 그래픽 시각화를 위한 시간이 소요된다. 커밋(Commit) 이력을 시각적으로 확인하고 싶을 경우가 아닌데도 매번 그래픽 렌더링에 시간이 걸린다면 작업 속도가 불필요하게 저하될 수 있다.
또 Add, Commit, Push, Pull만 사용하는 경우, 메인 브랜치에서 하위 브랜치로 갈라지는 것에 유연하게 대처하기 어렵다. Git에서는 하나의 메인 브랜치에서 하위 브랜치를 나누어 작업한 후 다시 메인 브랜치에 머지하는 작업방식을 지원한다.
무엇보다 브랜치가 여러 개로 나누어지면 각각의 브랜치에 서로 다른 커밋 히스토리가 추가되는데, 이 브랜치들을 하나의 브랜치로 합칠 때 충돌이 발생할 가능성이 있다. 이럴 때는 기본 명령어만으로는 해결하기가 어렵다. 무엇보다 충돌은 브랜치를 나누지 않고 하나의 브랜치에서 공동 작업을 할 때도 커밋의 선후관계에 따라 얼마든지 발생할 수 있는 문제다.
이번 글에서는 직접 사용해본 유용한 Git 커맨드들을 브랜치의 생명주기를 따라 살펴보고, 각 커맨드들이 존재하는 맥락에 관해서 이야기해보겠다.
분산형 버전관리 시스템 Git에서는 커밋 하나하나가 버전이 된다. 버전관리의 장점을 최대한 살리려면 작업 단위를 작게 쪼개 하위 브랜치를 만들어서 작업하는 것이 좋다. 하위 브랜치에서 쌓인 커밋은 메인 브랜치에 바로 반영되지 않기 때문에, 만들고 있는 기능을 하위 브랜치에서 자유롭게 테스트할 수 있다. 만약 하위 브랜치의 작업을 메인 브랜치로 머지했을 때 문제가 생기더라도, 해당 커밋들만 삭제하면 다시 안정화된 버전으로 돌아갈 수 있다.
Git을 만든 사람들에게는 엄격한 버전관리 철학이 있으며, 수많은 명령어는 그들이 의도했던 엄격한 버전 관리를 위해 만들어졌다. 따라서 Git의 버전관리 철학을 이해하면 각 커맨드의 존재를 자연스럽게 받아들일 수 있다. 그럼에도 모든 커맨드를 다 알기는 쉽지 않고, 그 중 열 개 내외의 커맨드만 잘 활용하더라도 협업의 효율을 크게 높일 수 있다.
>Git clone
>Git init
>Git remote add origin
>Git push
Git 시스템으로 버전관리를 하기 위해서는 ‘.Git’이라는 숨김 폴더를 가진 레파지토리를 생성해야 한다. GitHub 원격 레파지토리를 받으면 원격 레파지토리와 연결된 로컬 레파지토리가 생긴다. 또는 로컬 레파지토리를 만든 후 직접 원격 레파지토리와 연결할 수도 있다.
# 원격 레파지토리 내려받기
>Git clone [레파지토리 url]
# 로컬 레파지토리 생성 후 원격 레파지토리와 연결
>Git init
>Git remote add origin [레파지토리 url]
>Git push origin [브랜치 이름]
>Git checkout -b
>Git branch
하나의 레파지토리는 여러 개의 브랜치를 가질 수 있다. 레파지토리 최초 생성시에는 main이라는 이름의 기본 브랜치가 하나 생긴다.
# 브랜치 생성
>Git checkout -b [브랜치 이름]
# 생성된 브랜치 목록 확인
>Git branch
브랜치는 서브 태스크를 위한 작업 테이블이라고 생각하면 좋다. 하나의 작업 테이블에서 모든 일꾼이 동시에 작업하면 사용하는 자원들이 섞여서 혼란스러울 수 있다. Git에서는 동시에 같은 파일의 같은 라인이 변경되면 충돌이 발생한다.
이러한 혼란을 방지하기 위해서는 브랜치를 나누는 것이 좋다. 각자의 브랜치는 메인 브랜치에서 갈라져 나온 시점을 기억한다. 기능이 완성되어 다시 메인 브랜치로 머지하기 전에 자신이 작업하는 동안 메인 브랜치에 새로 쌓인 커밋 히스토리를 받아온다. 그 위에 자신의 커밋이 추가되도록 메인 브랜치로 보내면 파일의 변경이력이 시간 순서대로 정렬되어 쌓인다. 파일의 변경 이력은 Git log 명령어를 통해 확인할 수 있다.
이러한 맥락에서 보자면 메인 브랜치에서 가져온 브랜치를 너무 오래 간직하는 건 좋지 않다. 그사이에 새로 시작되는 작업은 이전 메인 브랜치를 기준으로 시작되기 때문이다.
>Git fetch origin
>Git rebase
협업을 할 때는 여러 개의 하위 브랜치가 생성되고 메인 브랜치에 합쳐지기를 반복하므로, 하위 브랜치의 작업이 메인 브랜치와 차이가 나는 것은 당연하다. 만약 하위 브랜치에서 작업 중이라면 주기적으로 메인 브랜치에 추가된 내용(커밋들)을 확인하고, 이 커밋들을 하위 브랜치에도 반영하는 것이 좋다. 메인 브랜치에 새로 추가된 내용이 많을수록 하위 브랜치의 내용과 충돌이 발생할 확률이 높아지기 때문이다.
다른 브랜치에 추가된 커밋을 받아올 때 일반적으로 Git pull을 사용하면 되지만, 동일한 파일에 대한 변경사항이 있다면 Git pull이 실패한다. 가령 원격에 Push 되어있는 main 브랜치에서 A라는 파일을 변경했는데 로컬의 서브 브랜치에서도 A라는 파일을 변경한 채 Git pull을 하면, 브랜치가 갈라졌다는 경고메시지가 나타나며 Git pull에 실패한다.
이런 경우 Rebase를 사용해서 해결하면 된다. Git 시스템을 하나의 트리로 볼 때, 상위 브랜치는 하위 브랜치들의 뿌리가 된다. Rebase는 하위 브랜치의 뿌리를 변경하는 과정으로 볼 수 있다. 하위 브랜치가 뿌리로 삼고 있는 낡은 예전의 상위 브랜치를 버리고, 새로 업데이트된 최신 버전의 상위 브랜치를 뿌리로 삼겠다는 것이다.
먼저 Git fetch를 통해 원격 레파지토리의 최신 내용을 로컬에 받아오고, Git rebase 커맨드를 치면 양쪽 브랜치에서 변경된 내용을 모두 볼 수 있다. 변경된 내용을 확인하며 직접 충돌을 해결한 후, 충돌 해결에 대한 커밋을 만든 후 Git rebase –continue
를 해주면 리베이스가 완료된다.
>Git fetch origin
>Git rebase origin/[브랜치 이름]
( 충돌 해결 후 add, commit, push )
>Git rebase --continue
>Git stash
>Git stash pop
>Git cherry-pick
Git에서 파일의 버전을 추적하도록 하기 위해서는 Add -> Commit -> Push라는 3단계 작업 버전 확정 과정을 거친다. 작업 공간에 있는 내용들은 Add를 통해 스테이징 영역에 올라가고, Commit으로 하나의 작업단위로 묶여서 고유한 해시를 부여받는다. 끝으로 Push를 통해 원격 브랜치에 반영된다.
그런데 만약 스테이징 영역에 올리거나 커밋 로그로 남기기에는 아직 불충분하거나 불확실한 작업내역을 일단 하나로 묶어서 보류해두고 싶다면? Git stash를 사용하면 된다. Stash한 파일들은 치워두기 영역에 보관되며 현재 작업 공간에서는 사라진다. 이후 Git stash pop이나 Git stash apply를 통해 다시 불러올 수 있다. Git은 이렇게 여러 단계의 작업 버전 확정 과정을 제공함으로써 작업의 단위들을 세분화해서 관리할 수 있도록 도와준다.
Cherry-pick커맨드는 다른 브랜치에서 작업한 특정 커밋만 받아오고 싶을 때 사용한다. 당연하게도 해당 브랜치와 현재 브랜치의 커밋 히스토리의 차이가 큰 상황이라면 Cherry-pick은 충돌을 야기할 가능성이 크다. 그렇지만 버전 히스토리가 동일하거나 거의 차이가 나지 않는다면 Cherry-pick을 통해 다른 브랜치에서 수정한 내용을 일일이 복사 붙여넣기 하지 않고도 현재 브랜치에 빠르게 반영할 수 있어서 편리하다.
>Git branch -d
브랜치를 삭제하지 않고 두면 나중에 같거나 비슷한 이름으로 브랜치를 생성할 때 로컬 브랜치와 원격 브랜치의 동기화가 잘못될 수 있다. 하위 브랜치에서 완성한 기능은 상위 브랜치로 머지하고, 하위 브랜치는 지워주는 커맨드다.
>Git branch -d [브랜치 이름]
Git의 창시자인 리누스 토르발스는 리눅스 커널을 개발할 당시 분산형 버전관리의 필요성을 느껴서 시스템을 개발했다. 분산형 버전관리는 소프트웨어 개발방식을 중앙집중식에서 분산식으로 바꾸어 놓았다.
Git의 커밋들은 하나하나가 고유한 해시값을 가지고 이전의 히스토리를 기억하고 있는 버전이다. 그래서 언제든지 이전 버전으로 되돌가거나 각 커밋에서 추가된 내용을 확인할 수 있다. 따라서 현재까지 작업한 커밋 히스토리를 원한다면 수정할 수 있음에도 손대지 않는 것이 안전하다.
Git에서 제공하는 커맨드는 정말 많다. 하지만 그 모든 것을 한 번에 외워야 할 숙제라기보다 필요할 때마다 하나씩 사용해보며 작업의 효율과 편의성을 높이는 여정이라고 생각하면 앞으로의 개발 작업에 도움이 될 것이다.
요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.