본문 바로가기
Git

[Git] Git 제대로 알고 사용하기 - 2

by 볼링치는 개발자 2021. 7. 13.
반응형

이번 포스팅에서는 인턴십 교육 때 진행했던 실습에 대해 정리하려고 합니다.

 

이번 교육을 맡으신 분께서 하신 말씀이 있습니다.

Git을 잘 쓴다고 하는 것은, Git Tree를 자유자재로 사용할 수 있는 것이다.

 

이번 실습은 Git, GitHub, SourceTree로 진행되었습니다.

 

먼저 GitHub에서 실습용 repository를 생성하고 SourceTree 프로그램에서 Clone을 해주었습니다.

GitHub에서 repository를 clone한 상태

위의 사진을 보면, main, origin/main, origin/HEAD 3개의 branch를 확인할 수 있습니다.

  • main - main branch
  • origin - 원격 저장소의 디폴트 이름, git clone 하면 자동으로 생성됨
  • origin/main - 원격 저장소의 main branch
  • origin/HEAD - 원격 저장소의 디폴트 branch의 HEAD

Commit

Commit 되기 전의 내용들은 uncommitted changes라고 불립니다.

Uncommitted Changes 파일들

  1. Unstaged - 변경되었지만, commit 되지 않은 파일들
  2. Staged - 변경되었고, commit 할 파일들

즉, unstaged 파일들은 로컬 작업 공간에서 수정이 완료된 직후의 파일들로, 아직 Git에 의해 형상 관리가 되지 않고 있는 파일입니다.

Staged파일들은 로컬 작업 공간에서 수정이 완료되고, 로컬이던, 원격이던 commit 되어서 Git에 의해 형상 관리가 되고 있는 파일입니다.

 

현재 main branch에서 1.txt 파일을 만들어 보겠습니다.

1.txt 생성

$ vi 1.txt 명령어로 vi 에디터를 통해 1.txt 파일을 만들고, $ git status 명령어를 통해 현재 Git 상태를 확인했습니다.

현재 브랜치는 main 브랜치이고, 1.txt 파일이 추적되지 않고 있다고 확인할 수 있습니다.

 

아래 사진을 보면 Git에서 파일이 어떻게 관리되는지 알 수 있습니다.

현재 저희의 1.txt 파일은 Working Directory에서 modified 된 상태입니다.

(Modified + Staged = uncommitted)

 

Git의 파일 상태들

Git의 GUI 프로그램인 SourceTree를 사용해 commit을 해보겠습니다.

git commit 1
git commit 2
commit 완료

위 사진을 보면 1.txt의 commit이 완료된 것을 볼 수 있습니다.

해당 branch는 로컬 작업공간의 main 브랜치이고, 1 ahead라는 뜻은, 원격 저장소의 main branch보다 한 단계 앞에 있다는 뜻입니다.

즉, 현재 작업 중인 main branch는 tracking이 되고 있는 branch임을 알 수 있습니다.

 

해당 branch가 원격 저장소의 main branch보다 한 단계 앞서 있는 이유는, 현재 로컬 작업 공간에 commit을 했지만, 원격 저장소에 반영하는 push 작업은 하지 않았기 때문입니다.


Branch

Branch는 나뭇가지라는 뜻으로, Git tree의 핵심이라고 볼 수 있습니다.

Git의 main branch는 중앙의 기둥이고, 그 기둥을 중심으로 뻗어나가는 나뭇가지들이라고 생각할 수 있습니다.

SourceTree프로그램에서 GUI로 newb라는 새 branch를 하나 만들어 보겠습니다.

 

 

newb branch 생성하기

방금 만든 newb branch에서 2.txt 파일을 만들어 보겠습니다.

 

$ vi 2.txt 명령어로 vi 에디터를 통해 2.txt파일을 만들고, $ git status 명령어를 통해 현재 Git 상태를 확인했습니다.

현재 branch는 newb branch이고, 2.txt 파일이 추적되지 않고 있다고 확인할 수 있습니다.

 

그럼 아까와 동일하게 SourceTree 프로그램 GUI를 통해 commit을 하겠습니다.

그리고 main branch로 다시 돌아와 3.txt 파일을 만들고 commit을 하겠습니다.

(branch 간 이동은 sourceTree프로그램의 왼쪽 BRANCHES부분에서 이동하고자 하는 branch를 더블클릭하면 됩니다)

 

위 작업을 모두 마치면 다음과 같은 Git Tree가 생성됩니다.

빨간색으로 새로 생선 된 newb브랜치를 확인할 수 있고, 해당 branch는 로컬 작업 공간에서 만든 branch기 때문에 아직 원격 저장소에서 tracking 되지 않아 몇 단계 앞인지 표시되지 않습니다.

newb branch로 만든 2.txt 파일을 볼 수 있습니다.

 

main branch는 1.txt, 3.txt를 만들며 원격 저장소의 main branch보다 2단계 더 앞서 나가 있다고 확인할 수 있습니다.

 

위에서 보이는 3개의 commit은 모두 각각 다른 내용을 담고 있습니다.

 

이렇게 branch는 main이라는 나무의 기둥 같은 branch를 기반으로, 나뭇가지처럼 뻗어가는 commit들을 말합니다.

branch는 기존에 개발 중이 프로젝트에 영향을 끼치지 않고, 내가 개발해 보고 싶은 기능을 개발해 테스트해볼 때 유용합니다.


Merge

Merge는 말 그대로 branch를 합치는 것입니다.

현재 상태에서, newb branch에서 4.txt를 하나 더 생성하고 main branch에 merge 해보겠습니다.

main branch를 선택한 상황에서, merge를 클릭해 진행해줍니다.

 

Merge를 완료하면 main branch에서도 newb branch가 생성한 2.txt, 4.txt를 모두 확인할 수 있습니다.

Merge는 실무에서 기존 프로젝트에서 새로운 기능을 개발하고, PM이나 선배 개발자가 먼저 확인한 후에 진행하면 됩니다.


Reset

Reset은 과거로 돌아가는 기능입니다. 주로 commit을 취소하고 싶을 때 사용합니다.

방금 main branch에서 newb branch를 commit 했던 이력을 reset으로 merge전의 상태로 돌려보겠습니다.

main branch로 newb branch를 merge 하기 전에, main branch에서 1.txt, 3.txt만 만들었던 commit을 누르고 reset을 해줍니다.

 

Reset을 진행하려면 reset의 3가지 옵션 중 하나를 선택해야 합니다

  • Soft - 변경 이력으로 돌아가면서, 변경 이력 이후의 파일들 유지, add 된 상태로 바로 commit가능
  • Mixed - 변경 이력으로 돌아가면서, 변경 이력 이후의 파일들 유지, 인덱스 삭제하면서 commit 하려면 add 해야 됨 (default)
  • Hard - 변경 이력으로 돌아가면서, 변경 이력 이후의 파일들 모두 삭제

Hard Option - 변경 이력 이후의 파일들 없음

Soft Option - 변경 이력 이후의 파일들 유지, add 된 상태로 바로 commit가능

 

Mixed Option - 변경 이력 이후의 파일들 유지, 인덱스 삭제하면서 commit 하려면 add 해야 됨


Conflict

Conflict는 git으로 협업 중에, merge, rebase 등 branch를 다루는 작업들은 파일들의 충돌입니다.

Conflict를 실습하기 위해 newc branch를 생성하고, newb branch에서 생성한 2.txt와 동일한 이름이지만, 내용이 다른 파일을 만들고, main branch에 newb branch를 merge 하고, newc branch를 merge 해보겠습니다.

newb branch에서 생성한 2.txt가 main branch로 병합되고, main branch에 병합된 2.txt와 newc branch의 2.txt의 내용이 달라서 충돌이 생깁니다.

Git은 충돌이 생기면 이렇게 개발자들에게 충돌이 일어났다고 알려만 주고, 어느 코드를 선택할지는 개발자가 선택해야 합니다.

2.txt를 newc에서 작성한 내용으로 수정하고 merge를 하면, conflict를 해결할 수 있습니다.


Revert

Revert는 과거의 특정 commit에 대한 반대 commit을 하는 것입니다.

과거의 특정 변경사항들을 반대로 고치고 다시 commit 합니다.

 

Reset과 비슷하지만, reset은 시간을 되돌리는 것이고, revert는 코드를 되돌리면서 되돌린 이력을 남깁니다.

 

예를 들어 개발을 진행하다가 로그인 버튼에서 버그를 발견했다고 가정합니다.

해당 로그인 버튼을 display를 hidden으로 수정하고, 버그를 수정합니다.

그럼 로그인 버튼의 display를 다시 show 해야 하는데, 이때 display를 hidden 했던 commit을 revert 하면 됩니다.


Rebase

교육을 해주시는 분께서 엄청 강요하신 rebase입니다.

교육을 받기 전에 rebase와 merge에 대해 개념이 잘 안 잡혀있었고, 두 개는 서로 헷갈리는 개념이었습니다.

Rebase를 간단하고 쉽게 말하면, '나뭇가지를 꺾어서 다른 곳에 붙인다'라고 말할 수 있습니다.

여기서 나뭇가지란 Git Tree 중 branch를 얘기합니다.

Rebase를 Git Tree를 자유자재로, 강력하게 수정할 수 있는 기능입니다.

앞서 말씀드렸다시피 "Git을 잘 쓴다 = Git Tree를 자유자재로 다룬다"라고 했었죠?

Rebase를 언제 쓰나?

첫 번째 이유

다음과 같은 Git Tree를 봅시다.

Main branch와, A, B branch 총 3개가 있습니다.

이렇게 개발을 진행하던 도중에,  B branch 개발자가 A branch 개발자가 commit 한 내용을 사용하고 싶으면 어떻게 할까요?

이때 rebase를 통해서 다음 사진처럼 Git Tree를 수정하면 됩니다.

이렇게 B branch를 main branch로 rebase 하면 A branch에서 commit 한 내용을 보고, 사용할 수 있게 됩니다.

이렇게 rebase를 하는 과정에서 충돌(conflict)이 일어날 수 있습니다.

또한 위 사진의 B branch를 rebase 한만큼 빨간 화살표로 나타냈는데, 이 길이가 길어지면 충돌이 일어날 확률이 높아집니다.

하지만 협업을 하면서 충돌은 흔히 일어나는 일이고, rebase를 통해 충돌을 미리 해결하면 훨씬 효율적입니다.

Rebase를 하지 않고 끝까지 개발하게 되면, main branch와 차이가 점점 커질 것이므로, rebase로 미리 해결할 수 있습니다.

 

두 번째 이유

뒤에서 치고 올라오는 merge를 방지할 때 사용합니다.

 

예를 들어 본인이 branch를 하나 따서 장시간 개발을 했다고 가정합니다.

개발을 모두 완료하고, pull request를 남기려면, 그동안 main branch에서 개발되던 내용들과 충돌이 생길 수 있습니다.

일반적으로 이런 충돌은 모두 관리자가 해결합니다.

관리자는 해당 코드를 작성한 사람도 아니고, 남의 코드에서 일어난 충돌을 관리자가 해결하기엔 정말 어려운 일입니다.

그럼 개발할 때 틈틈이 rebase를 하면서 충돌을 미리 해결하고, 마지막에 pull request를 하면 충돌이 없거나 덜 생긴 상태로 merge 할 수 있겠죠?

 

세 번째 이유

불필요한 commit을 한 번에 정리할 수 있습니다.

 

로컬에서 작업하면서 자리 이동을 하거나, 자리를 비울 때 혹시 모를 상황에 의해 작업 내용의 손실을 방지하기 위해 commit을 해놓는 경우가 생길 수 있습니다.

commit message : "화장실 갔다 오기 전 commit"
commit message:  "점심시간 전 commit"

이렇게 commit을 여러 번 하다 보면, 쓸데없이 개발 branch가 길고 복잡해질 수 있습니다.

Rebase을 함과 동시에 squash옵션으로 여러 개의 불필요한 commit을 하나로 줄이고, commit message가 하나로 단일화되며 간결해지기 때문에 cherry pick이나 코드 리뷰를 할 협업자에게 편리함을 제공할 수 있습니다.

 

이 부분이 merge 명령어와 가장 큰 차이점이었습니다.

Merge와 rebase모두 branch를 병합하고 합치는 기능을 갖고 있지만, merge는 기존의 commit내용들을 그대로 병합하지만, rebase는 commit내용들을 한번 정리하고 병합할 수 있다는 큰 장점을 갖고 있습니다.

 

Rebase는 다음 명령어를 통해 진행할 수 있습니다.

(여태까지 실습은 모두 SourceTree GUI를 통해 했지만, 교육 진행자 분께서 rebase만큼은 CLI로 진행한다고 하시네요.)

git rebase -i [target branch]

-i 옵션은 interactive의 약자로 대화형으로 rebase를 진행하는 옵션입니다.

이때 rebase의 3가지 옵션인 pick, squash, fixup 중 선택해서 사용할 수 있습니다.

  • pick - pick 한 commit은 유지하고 사용하겠다 (1개는 필수적으로 pick 해야 합니다)
  • squash - 다른 commit과 합치겠다, commit message도 수정할 수 있습니다
  • fixup - squash와 동일하지만, 해당 commit message는 삭제합니다

 

다음 사진을 보면, 사전에 말한 것처럼 쓸데없는 여러 개의 commit으로 인해 newd branch가 굉장히 길어지고 있습니다.

$git checkout newd

위 명령어로 newc branch로 이동합니다.

$git rebase -i @~3

위 명령어로 newc branch에 대한 rebase를 시작해 봅니다.

@~3는 최근의 3개 커밋을 rebase 하겠다는 의미입니다.

그럼 아래와 같은 화면이 보이며 vi에디터로 interactive 하게 rebase를 진행할 수 있습니다.

squash 하기 위해 2개의 commit의 pick을 s로 변경해줍니다.

그 후에 :wq를 통해 저장하면, 바로 다음과 같은 화면이 뜨게 됩니다.

sqaush옵션을 사용했기 때문에 commit message들이 잔존하게 되고, 커밋을 1개로 합쳤기 때문에 commit message한개를 작성해줍니다.

앞의 모든 commit message를 삭제하고, 다음과 같이 한 줄의 commit message만 남기고, :wq를 통해 저장하고 나옵니다.

그러고 나서 SourceTree의 GUI를 확인하면, 아까 복잡했던 newd branch가 하나의 commit으로 rebase 되며 Git tree가 간결해진 것을 확인할 수 있습니다.


Cherry-pick

교육을 진행해주신 분께서 rebase다음으로 강조하신 cherry-pick 기능입니다.

학부 생활, 작은 프로젝트를 진행하면서 git을 여러 번 써봤지만, cherry-pick이라는 기능은 완전 처음 들어봤습니다.

Cherry-pick은 다른 branch의 commit을 현재 작업 중인 branch에 반영할 때 사용하는 기능입니다.

Merge나 rebase로 할 수 있지 않냐고 생각하실 수 있는데, 하나의 메서드 정도의 기능을 가져오고 싶을 때 merge와 rebase를 사용하면 branch가 엄청 복잡해지기도 하고 과한 느낌이 들 수도 있습니다.

 

아래 사진처럼 Git Tree가 형성되었을 때, B branch 개발자가 A branch 개발자의 commit을 B branch에 반영하고 싶을 때 사용합니다.



다음 사진과 같이 newe branch는 7.txt를 개발했고, newf branch는 8.txt, 9.txt를 개발했습니다.

아직 두 branch모두 main branch에 반영되지 않았습니다.

이 시점에서 newe branch가 newf branch의 8.txt가 필요하다고 가정합시다.

다음 사진처럼 SourceTree GUI로 newe branch에서 newf의 8.txt commit을 cherry-pick 해주면 됩니다.

 

 

Cherry-pick 전의 newe 작업 공간

Cherry-pick 후의 newe 작업 공간

newf가 개발한 8.txt가 있는 것을 확인할 수 있습니다.

SourceTree GUI에서도 newe branch를 보면 newf branch에서 9.txt를 가져온 것을 확인할 수 있습니다.


Stash

Stash는 하던 작업을 임시 저장하는 것입니다.

작업하다가 화장실을 가거나 자리를 비울 때 작업 내용을 저장하고 싶지만 commit 하고 싶지는 않을 때 사용합니다.

Stash를 사용하면 임시 저장소에 저장되며 다른 branch에서 끌어다가 사용할 수 있습니다.

로컬 저장소에 저장되기 때문에 손실될 수 있다는 단점이 있습니다.


마무리

긴 글을 작성하며 교육 때 배웠던 내용과 실습을 직접 해보며 익힐 수 있었습니다.

이를 시점으로 Git Tree를 자유자재로 다루는 사람이 되려고 합니다.

혹여나 중간에 잘못된 점이나 개선사항을 찾으시면 댓글로 남겨주시면 감사하겠습니다.

반응형

'Git' 카테고리의 다른 글

[Git] Git 제대로 알고 사용하기 - 1  (0) 2021.07.07

댓글