GIT 에 대한 그림 이해

Posted on February 19th, 2022

Git 4가지 영역

말보다는 직접 보는 것이 이해하기 쉽습니다. 직접그렸습니다.

Git이 어려운 이유는 머리에 이 4가지 영역의 그림이 그려지지 않기 때문에 어려운 것입니다. 항상 기본적으로 GitHub에 저장소를 만들고 Intellij 또는 이클립스를 통해 코드를 짠 폴더에 들어가서 git init(안하면 그냥 일반 폴더, 하면 깃 명령어를 사용할 수 있는 폴더)을 해서 초기화를 시켜주고 Git Hub Repository에 연결하여 add . -> commit -> push 를 기계마냥 합니다.

그림을 보고 이해하면 그 과정이 훨씬 더 쉽게 다가올 수 있습니다. 작업공간 은 우리가 그 IDE 프로젝트 폴더가 있는 그 공간을 뜻합니다. IDE에서 작업을 하고 저장을 하면 그 폴더에 저장이 되고 유지가 됩니다. 그리고 Git Add 를 하는 순간 곧 커밋 될 작업 저장 공간(Staging Area)에 저장이 됩니다.

여러 파일들을 Staging Area에 올렸고 이번 Version (Commit)에 더 이상 파일을 올릴 필요가 없을 때, Commit을 하고 내 컴퓨터에 커밋된 저장 공간(Local Repository) 에 올립니다.

마지막으로 여러 Version(Commit)을 나의 컴퓨터가 아닌 Git Hub Respository(Remote)에 Push를 하고 저장시켜줍니다.

이 4가지 영역만 잘 이해하면 전부 다 안다고 해도 무방하다고 말은 못하지만 앞으로의 명령어들이 이해하기가 쉬울겁니다.

곧 커밋 될 작업 저장 공간 ? (Staging Area)

잠깐 ! 곧 커밋 될 작업 저장 공간 ?

처음에 접하면 의아할 것이다. 곧바로 작업 공간에서 작업을 하고 Commit을 하면 될텐데 왜 이렇게 중간에 임시 저장 공간이 필요할까?

저도 궁금해서 찾아보았습니다. 여러 글을 읽어보면 간단하게 결론은 하나입니다. 작업 공간에서 작업한 모든 파일을 Commit하고 싶지 않다. 간단 명료하다. 좀 더 얘기를 해보자면 Commit을 하면서 Version 관리를 하는 데 어떠한 파일은 이 Version에 넣기 싫은 것. 그럼 그 작업 내용만 냅두고 당장의 Version에 올릴 파일들만 곧 커밋 될 작업 저장 공간에 넣는 것 입니다.

origin, upstream에 대한 이해

'git remote'명령어를 통해 원격 저장소를 관리할 수 있습니다. 캡처된 사진을 보면 **upstream**과 **origin**이 있습니다. 이 둘에 대한 개념이 애매모호하며 이해가 잘 되지않습니다. 🤔 다른 예입니다. 저의 하나의 프로젝트입니다. 여기에는 git remote 를 이용하여 저장소를 확인해보니 upstream은 없고 origin 밖에 없습니다. 🧐

자, upstream을 알기 전에 origin 부터 설명하겠습니다. origin 은 즉, remote 입니다. 여기서 remote는 우리 컴퓨터의 local이 아닌 실제 git hub에 프로젝트의 저장 공간을 뜻합니다. origin은 remote입니다. ( 그림으로 직관하기 위해서는 맨 위에 구름의 빨간 박스 라고 생각하시면 쉽습니다. )

이제 origin은 무엇인지 알았는데.. upstream은 무엇일까요? upstream에 대한 것을 느끼기 위해 그림을 봅시다.

우선 **upstream**을 직관적으로 생각해봅시다. 말 그대로 '**상류**'로 해석이 됩니다. 상대적인 개념입니다. 예를 들어보겠습니다. 우리는 git을 사용할 때 **fork**라는 것을 합니다. 말 그대로 복제입니다. 다시 한번 그림을 볼까요? 저는 every-moment라는 소유의 Tech_Interview_Preparation 저장소를 fork를 하였고 fork를 한 결과 minyul 소유의 Tech_Interview_Preparation 저장소가 생겼습니다.

자, 느낌이 오시나요? 🤭 local - origin(local의 remote) - upstream(origin의 remote) 느낌입니다. 혼란스러운 이유는 딱 한가지입니다. origin 처럼 다른 이름으로 명명하면 되는데 upstream이라는 상대적인 이름을 아예 박아버렸기에 헷갈리는 겁니다. 차라리 A - B - C 하고 각 관계에서 upstream downstream으로 했으면 얼마나 편했을까요?

branch에 대한 이해 (origin, remote update)

이제 branch에 대해 공부하도록 하겠습니다. 음.. 정말 간단히 얘기하면 복사를 당한 또 다른 코드라고 생각하면 됩니다. 즉슨, 코드인데 branch라는 것은 그 코드에 대한 복제라고 생각하면 됩니다. 그럼 원래의 원본과 복제가 여러개 있겠죠? 복제를 만드는 이유는 원본 코드에 영향을 가지않기 위해 독립적으로 개발하기 위해 branch 라는 개념이 생겼습니다. 현재의 branch에서 복제를 하기 위해서는 'git branch 복제이름'으로 복제를 하면 됩니다. ( 복제본에 대한 복제를 또 만들 수 있겠죠? 즉, 상대적인 개념입니다. 🤭) 상대적으로 원본과 복제가 있지만 뿌리 그 자체는 main, master 로 생각하시면 될 것 같습니다. git init 을 통해 초기화를 할 때 제일 먼저 생기는 branch 니깐요.

+) -a option을 통해서 모든 branch를 볼 수 있습니다.

여기서 조금 궁금한 부분이 생겼습니다. remotes/origin/HEAD -> origin/main 라는 부분입니다. 다른 두개의 빨간 branch는 origin, upstream에 대한 branch 라서 이해는 합니다만, HEAD라는 부분이 이해가 어려울 수 있습니다. HEAD라는 것은 현재 상태의 포인터를 나타내는 것이며 사진에 있는 것은 현재 origin 저장소에서의 포인터는 origin/main branch 라는 것입니다. 즉슨, 이 포인터를 이용해서 clone할 때 다른 branch를 지정해줄 수 있겠지요?

한번 실습을 해보겠습니다. git branch -a를 통해서 현재 branch 를 보았고 직접 해당 프로젝트에 대해서 git hub에 들어가서 **new_branch**라는 브랜치를 만들었습니다. 그리고 **git remote update**를 통해 orgin에 있는 branch들을 update 시켜줬습니다. 🤭 (upstream 같은 경우에는 git fetch upstream을 해줘야합니다. ) 하나의 실습을 더 해보겠습니다. origin에서 새로운 branch 만들었고 그와 같이 local에서 똑같이 new_branch라는 복제를 만들었을 때 ! git pull을 이용하면 변경된 데이터를 갖고 올 수 잇을까요? If you wish to set tracking information for this branch you can do so with !! **왼쪽 그림을 보면 추적할 수 없다고 나옵니다. 셋팅을 해줘야겠군요.**

git branch --set-upstream-to=origin/new_branch new_branch 의 명령어를 이용하여 추적이 가능하게끔 했습니다. 다시 pull을 해보면 오른쪽 그림처럼 잘 갖고와서 데이터를 병합해줍니다. 🤭

이렇게 각 local, origin 에 branch(복제)가 여러 개가 있고 local에서 origin의 branch를 사용할 때 추적이 가능하게 셋팅을 해줘야합니다!

clone에 대한 이해 (non default branch, single branch)

협업을 할 때 제일 먼저 하는 것은 clone입니다. clone에 대해서 공부해보겠습니다. git repository를 clone을 해봤습니다.

해당 폴더에 clone한 저장소가 들어왔습니다. 🤭 clone을 한 후, branch에 대한 부분을 확인하고 싶습니다. origin에 있는 모든 branch를 갖고 왔을까요? 현재 origin 에는 main branch, test branch가 있습니다. 명령어 git clone 을 하였을 때는 그 저장소의 주축이 되는 branch(default branch)만 갖고 옵니다. 즉슨,

그럼 !? main branch가 아닌 test branch만 갖고오려면 어떻게 해야할까요? git clone -b {origin_branch_name} --single-branch {origin_url} 🧐🧐 실습을 해보니 현재 tracking 되고 있는 branch가 아닌 test branch 라는 단 하나의 branch를 갖고 오게 됩니다.

fetch에 대한 이해

git fetch에 대해서 공부를 해보겠습니다. 우리는 Add 명령어를 통해 스테이징 영역에 보관을 해놓고 Push를 통해 Remote(Origin)에 저장합니다. 근데 만약 ! ! 그 Remote(Origin)에 있는 저장된 데이터들이 우리(Local)에서는 모르고 있으면 어떻게 될까요? 즉슨, 협업을 통해서 하나의 저장소에 동기화를 시켜주고 서로 데이터를 저장해야하는 데 동기화가 되어있지않다면 밑에 사진과 같이 Push가 먹히지 않습니다.

경고창에 보면 보통은 Pull을 사용합니다. ( + Pull = Fetch + Merge ) 😁😁 맞습니다. Pull을 쓰면 됩니다. 하지만 Pull을 알고 쓰기 위해서는 Fetch를 알아야겠죠? Fetch를 잘쓰지않지만 Fetch를 공부해봅시다.

Fetch는 단순하게 '변경된 내용을 알고 싶을 때, 로컬에 임시로 갖고 온다.' 그러면 Pull이라는 것은 Fetch + Merge 기에 해석을 하자면 변경된 내용을 알고 합친다 ! 라고 해석이 되겠죠?

곧바로 실습해보겠습니다! git fetch origin을 통해 우선 변경된 내용을 로컬로 갖고옵니다. 그리고 git checkout FETCH_HEAD로 이동한다음 ! ! 이력을 확인해봅시다.

변경된 내용을 갖고와서 확인을 할 수 있습니다. 자! 이제 마무리를 하겠습니다. Push가 안되던 Branch로 다시 돌아가서 Merge를 시켜줬습니다. 물론 Pull만 해도 됩니다. 👏👏

Merge 대한 이해

Merge 같은 경우에는 영어의 뜻 처럼 하나의 브랜치에서 다른 브랜치를 합치는 겁니다. 자 ! 우리는 상대적인 개념으로 원본이라는 브랜치가 존재한다고 가정해봅시다. 그리고 이 원본 브랜치를 복제한 브랜치가 있구요! 그 복제브랜치에서 하나 하나 코드를 고치거나 추가하면서 Commit 을 합니다 ! 이 수정, 추가된 코드를 원본에 Merge ! 합쳐버리면 어떤 상황이 일어날까요 ?

당연히 아무 이상없이 잘 합쳐질 겁니다. 충돌이 나는 경우를 만들어봅시다. 🤔🤔

충돌이 납니다. 두 개의 브랜치가 같은 부분을 수정하고 merge를 했기 때문에 충돌이 난 것입니다. 하지만 먼저 merge를 실행한 issue1_branch는 Fast-forward가 나오면서 원본 브랜치에 자동으로 merge가 되었습니다. 이 Fast-forward는 무엇일까요? 원본은 그대로 있습니다. 딱히 무엇을 하지 않았어요. 그냥 바라만 볼뿐. 원본 브랜치에서 복제브랜치가 그대~로 앞으로 간 것 뿐입니다. 딱히 어떠한 Merge Commit을 할 필요없이 빠르게 Merge가 된것을 뜻합니다.

자. 그럼 우선 충돌된 부분을 고쳐보도록 하겠습니다.

현재 충돌난 파일을 확인하고 어느 부분에서 충돌이 나는 지 확인해봤습니다. <<<<, >>>>> 이 부분이 충돌난 부분 입니다. 이 부분을 수동으로 GUI로 수정했고 다시 스테이징에 넣어 Commit을 해봤습니다. main|MERGING 상태에서 main으로 변했습니다. 🙏

Log를 보면 issue 브랜치 2 수정에 대한 이력과 충돌 해결한 부분에 대한 이력이 같이 올라가있습니다.

origin을 fork하고 upstream에 업데이트를 했을 때

기존 저장소에서 fork를 하고 나서 그 fork 한 저장소로 clone을 합니다. 이제 local에서 코드를 수정하고 orgin에 push를 한다음 upstream에 PR을 날리는데요 ! 🤔🤔

만약 !! Upstream에 누군가 PR을 날려서 우리의 Orgin 버전과 Upstream이 다르면 어떻게 할까요? 동기화를 시켜줘야합니다. 하지만 우리 Local에서는 Orgin에 대한 Clone만 했을 뿐. Upstream 에 대한 정보가 없는데요.

이럴 때 우리는 Upstream을 셋팅해줘야합니다. ** git remote add upstream [ 브랜치 명 ]** 을 해주면 됩니다. 반대로 !? 만약 orgin이 었다면 ** git remote add orgin [ 브랜치명 ]** 을 해주면 됩니다.

밑에 실습입니다. 추가를 시켜줬고 ! git remote -v 을 통해서 확인해본 결과 upstream 또한 추가 된 것을 볼 수 있습니다.

이제 우리는 **git pull upstream main** 을 통해서 upstream에 있는 최신 버전의 코드를 갖고와서 pull( Fetch + Merge ) 를 시켜줍니다. 이상입니다. 👏👏

orgin 에서 single branch clone 했을 때, 다른 branch를 못갖고온다! ( remote update not working ! )

회사에서 겪었던 일이다 ! 보통은 git clone 하고 갖고 온다. 그리고 협업을 할 때 팀원 아무나 origin에 branch 을 새로 팠을 때 나는 이 branch 를 갖고 와야한다. git remote update 를 해보자. 어? 안되네.. 뚜둥 !! 되지않는다. 🧐🧐 ( 그냥 git clone 으로 모든 브랜치를 갖고 왔다면 git remote update로 가능하다 ! )

자 ! 이제 해결해보자. 나는 origin의 main branch를 갖고 오고 싶다! git remote set-branches --add origin main 으로 먼저 ! 지정해주자. 아무일도 일어나지 않는다. 하지만 이미 셋팅이 되어있는 것. 짜잔 ! 그리고 **git fetch orgin main : main ** 해주면 ? 새로운 브랜치를 갖고 올 수 있다.

Rebase 에 대한 이해

git pull 안될 때 ( init 당시 히스토리를 서로 모를 때 )

git pull origin 브런치명 --allow-unrelated-histories