실습으로 알아보는 기초적인 Git Command

sanghoon Ahn·2022년 10월 27일
0
post-thumbnail

안녕하세요, dvHuni입니다!!

이미 git을 잘 사용하고 계시더라고 GUI로만 사용하시던 분들!!🤓

혹은 이제 협업을 해야하는데 어떤 명령어를 써야하는지 모르시는 분들! 👊

최근에 git 실습을 위한 좋은 사이트를 알게되어 실습해보면서 내용을 조금 정리해보았습니다.

직접 실습을 하셔도 좋고, 간단하게 정리된 내용만 보아도 어느정도 도움이 될 것 같습니다.

이 짧은 글이 조금이나마 도움이 되는 바람으로 포스팅을 시작해 봅니다. 🙏

출발해 보시죠! 🏃

Basic

Commit

Commit이란 github 저장소에 디렉토리에 있는 모든 파일에 대한 스냅샷을 기록하는것.

커밋을 매우 가볍게 유지하기 위해 커밋 할 때마다 디렉토리 전체를 복사하진 않는다.

각 커밋은 저장소의 이전 버전의 내용과 다음 버전의 변경 내역(delta)를 저장한다.

저장소를 clone 하게되면 모든 변경내역(delta)를 가져오게 됩니다.

Branch

branch는 특정 커밋에 대한 reference입니다. 따라서 브랜치 안에 작업들을 많이 가지고 있기 보다는 여러 브랜치에 나누어서 커밋을 가지고 있어도 문제가 발생하지 않습니다.

Merge

merge는 branch를 합치는 방법 중 하나이며, 두개의 parent를 가지는 특별한 커밋을 만드는것을 의미합니다.

하나의 parent는 원본을 가리키고, 하나의 parent는 변경사항을 가르킵니다.

그림과 함께 보시죠

🖼
주석머지 전 두개의 branch머지 이후 생긴 C4 commit은 두개의 parent를 가진다

main 과 bugfix의 브랜치가 있고, 각각은 C3, C2의 작업내용 커밋을 가집니다

이제, bugfix의 내용을 main에 merge 하게되면

git merge bugfix

main에 있는 * 표시는 현재 branch가 main에 있다는 의미입니다.


오른쪽과 같이 C2와 C3를 부모로 가지는 C4 커밋이 생기게 됩니다.

이때 C2의 색이 C0, C1과 같이 변경된것이 보이시나요? 👀

C0, C1의 색은 C2, C3의 색을 합친것 입니다. 즉, C2가 C3의 내용을 포함 했다는 의미입니다.


이제 bugfix로 이동하여 main 에 merge 하게 되면

git chekout bugfix;
git merge main

🖼
주석모든 내용이 merge된 main

모든 내용이 머지된 main을 보실 수 있습니다.

Rebase

두개의 브랜치를 합치는 merge와는 다른 방법입니다.

merge와 다른점은 커밋 로그가 깔끔해지는 장점이 있습니다.

🖼
주석C3의 작업을 C2로 옮기고 싶다main의 작업 내용이 모두 포함된 bugfix가 된다

main 브랜치와 bugfix 브랜치가 존재하며, 현재는 bugfix 브랜치에 있습니다.

오른쪽의 사진처럼 bugfix의 작업(C3)을 main의 위에 옮겨 놓으려 합니다.

이때 rebase를 사용합니다.

git rebase main

이제 오른쪽 사진 처럼 C3이 C2위에 존재하게 됐고, 한줄의 커밋으로 정리됐습니다.

하지만 C3’은 main 위에 올려진 C3의 복사본입니다.

문제를 해결하기 위해서 먼저 main브랜치로 이동하여 bugfix를 rebase 해 줍니다.

git checkout main
git rebase bugfix

🖼
주석main으로 이동하고main을 bugfix로 이동시킨다

그러면 오른쪽 그림과 같이 main과 bugfix가 같은 커밋을 바라보게 되고, main에 C3 내용이 포함되었기 때문에

모든 commit이 같은 색이 되었습니다.

Switching Commit tree

HEAD는 현재 checkout 된 즉 작업중인 커밋을 가르킵니다. 또한 HEAD는 항상 작업중인 tree의 최신 커밋을 가르킵니다.

일반적으로 HEAD는 작업중인 branch의 이름을 가르키며 커밋을 하게되면 branch의 상태가 바뀌며, HEAD를 통해서도 변화를 확인 할 수 있습니다.

HEAD 분리하기

HEAD를 분리하는것은 HEAD를 branch 대신 commit에 붙이는것을 의미합니다.

현재는 main에 있지만 아래 명령어를 실행하면 오른쪽 처럼 HEAD가 생기게 됩니다.

git checkout C1

🖼
주석현재는 main에 있는 상태HEAD를 main에 위치시킵니다

Relative Reference(상대참조)

위에서는 편리하게 C1, C2로 checkout을 했지만, CLI에서는 commit의 hash를 입력해야합니다.

각 커밋은 고유한 hash를 갖습니다

커밋의 hash를 확인하기 위해 git log 명령어를 사용할 수 도 있지만,

fed2da64c0efc5293610bdd892f82a58e8cbc5d8

위처럼 긴 hash를 일일이 치기는 어렵죠.

그래도 다행히 git은 hash를 커밋의 고유한 값임을 알 수 있는 정도만 hash를 입력해도 무방합니다.

fed2da6 처럼 말이죠.

그러나 hash를 입력하는것 조차 귀찮습니다.

여기에서 Relative Reference(상대참조)가 등장합니다.

상대경로와 비슷한 개념이 아닐까 싶습니다 🧐

branch라던가, HEAD로 부터 특정 지점에 이동하여 작업을 할 수 있습니다.

간단한 두가지 방법이 있습니다.

  • 한번에 한 커밋 위로 이동하는 ^
  • 한번에 여러 커밋 위로 이동하는 ~<commit num>

일반적인 GUI(source tree, fork, git kraken)등은 부모가 아래에 있습니다. 즉 여기서 말하는 위로는 부모라는 의미이며, GUI에서는 아래로 내려가는것을 의미합니다.

^(캐럿) 연산자는 하나 추가할 때마다 명시한 커밋의 부모를 찾습니다.

main^는 C1이 되고, main^^는 C2가 됩니다.

git checkout main^

🖼
주석현재는 main에 있는 상태캐럿 연산자를 사용하여 HEAD 분리

명령어를 실행하면 오른쪽과 같이 HEAD가 C1을 가리키게 됩니다.


또한 HEAD 역시 사용할 수 있습니다.

git checkout C3
git checkout HEAD^
git checkout HEAD^
git checkout HEAD^

위 명령어를 실행하면 어떻게 될까요?

HEAD는 C0를 가르키게 됩니다.

🖼
주석현재는 main에 있는 상태HEAD와 캐럿 연산자를 사용하여 HEAD 분리

위 명령어는 조금 없어보이니 다음과 같이 사용할 수 있습니다.

git checkout C3
git checkout HEAD~3

~(틸드) 연산자는 한번에 여러 커밋의 부모를 찾아갈 수 있습니다.

C0까지 이동하기에는 ^ 연산자로 명령어를 실행하면 꽤나 귀찮을것 같습니다.

~ 연산자에 원하는 커밋만큼의 수를 입력하여 한번에 이동할 수 있습니다.

git checkout HEAD~4

🖼
주석현재는 main에 있는 상태틸드 연산자를 사용하여 HEAD 분리

브랜치 강제로 옮기기

그렇다면 이렇게 상대참조를 해서 무엇을 할 수 있을까요?

바로 브랜치를 강제로 옮길 수 있습니다.

git branch 명령어에 -f 옵션을 지정하여 브랜치를 특정 커밋에 재지정 할 수 있습니다.

git branch -f main HEAD~3

main의 브랜치 이동 없이, main을 C1 커밋으로 이동 시켰습니다 👏

🖼
주석현재는 bugfix에 있고, main과 같은 커밋을 바라보는 상태브랜치를 변경하지 않고 main을 C1 커밋으로 변경

Git에서 작업 되돌리기

Git에서 작업을 되돌리는 방법에는 여러가지가 있습니다.

작업내역을 되돌리는것도 커밋과 마찬가지로 개별 파일이나 묶음을 스테이징 하는 낮은 수준의 일과 실제 변경이 복구되는 높은 수준의 일이 있습니다.

먼저 실제 변경이 복구할 수 있는 방법은 크게 두가지가 있습니다.

git reset
git revert

git reset

git reset은 브랜치에서 예전 커밋을 바라보도록 이동시키는 방식으로 변경 내용을 되돌릴 때 사용합니다.

즉, 커밋하지 않은 것 처럼 되돌아가는 방법입니다.

이 방법을 이용하고 다시 커밋을 하게된다면, 이전의 커밋은 없어지는 것이기 때문에 “히스토리를 다시 작성한다”고 도 말할 수 있습니다.

git reset HEAD~1

🖼
주석현재는 main에 있는 상태현재 HEAD에서 한 커밋 뒤로 리셋

main branch는 C1으로 이동했고, C2의 내용은 투명해졌습니다.

🚨 여기에서 커밋을 다시 하게되면 이전 커밋은 없어지는것이기 때문에 C2가 다시 생기는것이 아닌, C3이 생기게됩니다. 🚨

git revert

각자 작업하는 로컬브랜치(local)의 경우 revert를 사용해도 무방하지만 함께 작업하는 브랜치에서 원격브랜치(remote)에서 reset을 사용하게 되면 커밋 히스토리가 없어질 수 있기 때문에 원격브랜치에서 ⭐️ 사용하지 않는 것을 권장합니다.⭐️

대신 변경내용을 공유할 수 있는 git revert를 사용하는것을 권장드립니다. 🤘

git revert HEAD

🖼
주석현재는 main에 있는 상태C2'의 커밋 생성

C2’는 C2의 작업내용을 되돌리는 커밋입니다.

이렇게되면 C2의 작업내용을 되돌리면서 커밋 히스토리를 남겨 공동 작업자도 해당 커밋의 변경내역이 되돌려진 것을 알 수 있습니다.

Apply Commit

지금까지는 Git의 기초를 알아보았습니다 ! 😄

commit을 하여 작업내용을 남기고, branch를 만들어 commit tree를 생성하고, 만들어진 브랜치를 여기저기 옮겨다녔습니다.

솔직히 이정도만 알아도 개발자들이 사용하는 일반적인 작업에는 무리없이 함께 할 수 있습니다. 🤩

(참고자료에서는 90% 라고 했는데, 적절한 것 같습니다 ㅋㅋㅋ)

그렇다면 나머지 10%와 추가적인 기능은 어떤것일까요?

복잡한 작업을 하거나 작업을 하다가 흔히 말하는 git이 꼬였다 라는 상황에서 유용하게 쓰일 수 있는 기능들 입니다.🤯

CherryPick

먼저 git cherry-pick입니다.

이번에는 조금 다르게 명령어를 먼저 보고 설명드리겠습니다.

git cherry-pick {commit1} {commit2} {...}

commit이 인자로 여러 개 붙을 수 있는데요, 현재 위치에서 아래에 있는 인자로 전달된 커밋들에 대한 복사본을 만들겠다는 의미입니다.

잘 모르겠죠? 그림과 함께 봅시다.

🖼
주석초기상태

main에 side의 작업을 가져오는 방법에는

이전에 배운 rebase를 통해 가능할 것 같습니다.

그렇지만 만약 side의 모든 내용이 아닌, C2, C4 작업만 가져오고 싶을때

cherry-pick 을 사용할 수 있습니다.

git cherry-pick C2 C4

🖼
주석현재는 main에 있는 상태C2, C4와 동일한 C2',C4' 커밋 main에 생성

다만 이때 작업한 파일중에 동일한 파일이 있다면 conflict(충돌)를 해결해야합니다 😅

Interactive Rebase

Cherry Pick은 원하는 커밋이 무엇인지 알 때 유용합니다.

원하는 커밋이 무엇인지 모를때에는 Interative Rebase를 사용하면 됩니다!

이전에 Rebase를 보았었는데요,

Interactive Rebase는 rebase-i 옵션을 사용하는 것 입니다.

일반적인 Rebase와 다른점은 Rebase의 목적지가 되는곳 아래의 커밋들을 검토할 수 있는 가장 좋은 방법입니다.

CLI에서는 vim같은 텍스트 편집기에서는 커밋과 해시들을 보여주지만

기타 git GUI에서는 UI를 노출합니다.

rebase -i 명령어를 실행하면 아래의 기능들을 수행할 수 있습니다.

  • 적용할 커밋들의 순서를 바꿀수 있습니다.
  • pick을 이용해 원하지 않는 커밋들을 뺄 수 있습니다.
  • squash를 통해 커밋들을 합칠 수 있습니다.

rebase 내용들의 부모 커밋 or 해시를 지정합니다.

git rebase -i c1

🖼
주석현재는 main에 있는 상태rebase -i를 사용하여 나타난 UI, rebase 시 포함될 commit 선택 가능모든 커밋을 선택한 상황

실습 사이트에서는 위와 같은 UI가 노출되는군요.

C1 커밋으로 부터 시작된 C2, C3, C4, C5 커밋을 Interative Rebase를 시도합니다

이때 원하는 커밋을 선택할 수 도 있고, 커밋들의 순서를 변경할 수 도 있습니다.

참고로 제가 사용하는 fork라는 GUI 툴에서는 위와 같이 나옵니다.

여러분이 사용하는 GUI에서는 어떻게 나오나요? 👀

Tag

특정 commit에 milestone을 남기고 싶을땐 tag명령어를 사용합니다.

tag는 커밋들이 추가로 생성되어도 변경되지 않습니다.

git tag v1 C1

🖼
주석현재는 main에 있는 상태C1 커밋에 남겨진 태그

C1 커밋을 지정하여 v1 태그를 남깁니다.

만약 커밋을 지정하지 않으면 현재 HEAD에 태그를 남깁니다.

이때 태그에서 commit을 하게된다면 commmit의 부모는 C1을 가르키게 됩니다.

tag로 남겨진 v1 은 그저 위치를 나타내는 역할을 수행합니다.

태그는 주로 release 버전에 사용했던것 같아요. 👀

마무리하며..

오늘은 git command를 몇가지 정리해보았는데요.

아직 실습 사이트에는 내용이 더 남아있지만 제목에서 볼 수 있듯이 기초적인 내용만 다루었습니다.

또 실제로 가장 많이 쓰이는 명령어들 이기도 합니다.

잘 모르겠다구요?

제가 사용하는 git fork, 유료이지만 확실히 다른 툴보다는 월등히 좋았던 Git Kraken, 사용할 때 크래시가 엄청 발생했던 sourcetree 까지

많은 git GUI 툴들은 위에서 설명드렸던 command를 GUI로 실행하고 있습니다.

오늘도 fork 나 kraken, sourcetree 를 가지고 커밋을 하셨다면
git commit 명령어를 사용하신게 맞습니다. 😁

조금 재미있지 않나요? 😅

저는 이 실습을 진행하고나서 interactive rebase에 매력을 느꼈습니다.

A-B-C 커밋이 있는데 B를 수정해야 한다면 어떻게 하실건가요?

여러 방법이 잇겠지만 저는 A 커밋까지 rebase -i 를 실행하여 B, C 커밋의 순서를 변경한 후 (A-C-B)

B커밋을 Amend로 수정하고, 다시 rebase -i 를 실행할것 같습니다.

이렇게 하면 C의 커밋메세지도 그대로 남아서 편했던것 같아요. 😄


각설하고 실제로 업무를 하다보면 작업은 다 끝냈는데 git을 정리해야 하는 경우가 생깁니다.

동료들이 많은 커밋을 올려두거나 같은곳을 수정한 상황, 작업에 필요한 내용을 다른 동료가 커밋해주는 상황 등등

수 많은 상황이 생깁니다. 😭

그 때마다 어떤 명령어를 쓰면 무엇을 할 수 있는지 알고 있다면 조금 쉽게 문제를 해결할 수 있을거에요.
(그 때마다 이 글을 찾아와주시면... 🥹)

git을 잘 활용하는것도 협업을 조금 더 잘 할 수 있는 방법중에 하나라고 생각합니다.

오늘도 조금 성장했네요, 뿌듯합니다. 😁

그럼 오늘의 포스트는 여기서 마치겠습니다. 👋

질문이나 지적은 언제나 환영합니다. 🙇🏻‍♂️

profile
hello, iOS

0개의 댓글