HelloWorld

[Git] 브랜치, 원격저장소 사용법

웹(WEB)/Git

브랜치 활용하기

  • git commit - 나의 디렉토리에 있는 모든 파일에 대한 스냅샷을 git 저장소에 기록하는 행위. 쉽게 말해 디렉토리 전체를 복사하여 붙여넣는 것과 유사하다.
  • git branch [브랜치명]- 새 브랜치를 만듭니다.
  • git checkout [브랜치명] - 해당 브랜치로 이동합니다.
  • git merge [브랜치명] - 해당 브랜치를 합칩니다.
  • git rebase [브랜치명] - 리베이스는 커밋들을 모아서 복사한 뒤, 다른 곳에 떨궈 놓는 것입니다. 리베이스를 하면 커밋들의 흐름을 보기 좋게 한 줄로 만들 수 있다는 장점이 있습니다. 리베이스를 쓰면 저장소의 커밋 로그와 이력이 한결 깨끗해집니다.

 

HEAD는 현재 체크아웃된 커밋을 가리킵니다. -- 다시 말하자면 현재 작업중인 커밋입니다. HEAD는 항상 작업트리의 가장 최근 커밋을 가리킵니다. 작업트리에 변화를 주는 git 명령어들은 대부분 HEAD를 변경하는것으로 시작합니다.

일반적으로 HEAD는 브랜치의 이름을 가리키고있습니다. 커밋을 하게 되면, 브랜치의 상태가 바뀌고 이 변경은 HEAD를 통해서 확인이 가능합니다.

 

브랜치 : 하나의 커밋과 그 부모 커밋들을 포함하는 작업 내역이다. 브랜치는 많이 만들어도 메모리나 디스크 공간에 부담이 되지 않기 때문에,  작업을 커다란 브랜치로 만들기 보다, 작은 단위로 잘게 나누는 것이 좋다.

 

원격저장소 활용하기

git remote(원격) - 원격 저장소는 백업으로서의 역할을 훌륭하게 수행합니다! 로컬 git 저장소는 파일들을 이전의 상태로 되돌리는 기능을 가지고 있습니다(아시다시피). 하지만 그 모든 정보가 로컬(내PC)에 저장되어 있습니다. 여러분의 git 저장소를 다른 컴퓨터에 복사본을 가지고있으면 로컬 데이터를 다 잃더라도 다른 컴퓨터에 남아있는 복사본으로 다시 출발 할 수 있습니다.

더 중요한 것은, 원격 저장소를 통해 코딩을 다른 사람들과 함께 할 수 있다는것입니다. 여러분의 프로젝트의 복사본이 어느곳에선가 호스트되기때문에 여러분의 친구가 프로젝트에 아주 쉽게 기여할 수 있게됩니다(최근의 변화를 pull하거나).

원격 저장소에서의 활동을 시각화해주는 웹 사이트들을 사용하는것이 추세입니다 (Github 또는 Phabricator등이 있습니다).

하나의 컴퓨터에 있는 여러분의 저장소의 복사본일 뿐입니다. 여러분은 일반적으로 인터넷을 통해서 이 또 하나의 컴퓨터와 커밋을 주고 받는등 대화를 할 수 있습니다.

 

git clone - 원격 저장소의 복사본을 로컬에 생성할때 사용하는 명령어 입니다(github에서 가져올때 라던가).

로컬 저장소에 origin/master라고하는 새 브랜치가 생긴겁니다. 이런 종류의 브랜치는 원격브랜치라고 불립니다

원격브랜치는 다음과 같은 이름짓기 규약이 있습니다. <remote name>/<branch name>

따라서, 브랜치의 이름은 master이고 원격 저장소의 이름은 origin인겁니다.

 

git fetchgit 원격 작업들은 결국 서로다른 저장소에서 데이터를 주고 받는것에 불과하다는것을 알 수 있습니다. 우리가 커밋들을 주고 받을수 있는 한, git을 바탕으로하는 모든 종류의 업데이트를 공유할 수 있습니다(작업, 새로운 파일들, 새로운 아이디어, 러브레터 등...).

원격 저장소에서 데이터를 가져오는 방법으로, 이를 위한 명령어는 git fetch라고 불립니다.

git fetch는 두가지의 중요한 단계를 수행합니다.

  • 원격 저장소에는 있지만 로컬에는 없는 커밋들을 다운로드 받습니다. 그리고... 
  • 우리의 원격 브랜치가 가리키는곳을 업데이트합니다 (예를들어, origin/master)

git fetch는 본질적으로 로컬에서 나타내는 원격 저장소의 상태를 실제 원격 저장소의 (지금)상태와 동기화합니다.

git fetch는 일반적으로 원격 저장소와 인터넷을 통해 접근합니다(http:// 또는 git://와같은 프로토콜로).

git fetch는 그러나, 여러분의 로컬 상태는 전혀 바꾸지 않는습니다. 여러분의 master 브랜치도 업데이트하지 않고 파일 시스템의 모습이던 그 어떤것도 바꾸지 않습니다.

이것을 이해하는게 아주 중요한데, 왜냐하면 수 많은 개발자들이 git fetch를 하면 자신의 로컬 작업이 변경되어 원격 저장소의 모습을 반영해 업데이트 될것이라고 생각하기 때문입니다. 앞의 과정에 필요한 데이터를 다운로드는 하지만, 실제로 로컬 파일들이나 브랜치를 변경하지는 않습니다.

 

git pull - 자 우리는 원격 저장소에서 git fetch로 어떻게 데이터를 내려 받는지 보았습니다. 이제 우리의 작업을 업데이트해서 변경들을 반영해 봅시다!

사실 이걸 하는 방법은 여러가지 있습니다 -- 새 커밋들을 로컬에 내려받은 이후에는 그냥 다른 브랜치에있는 일반 커밋처럼 활용할 수 있습니다. 이런 명령들을 실행할 수 있다는 뜻 입니다.

  • git cherry-pick origin/master
  • git rebase origin/master
  • git merge origin/master
  • 기타 등등

 

사실 원격 저장소의 변경을 fetch하고 그이후에 merge하는 작업의 과정이 워낙 자주있는 일이라서 git은 이 두가지를 한번에 하는 명령을 제공합니다! 이 명령어는 git pull 입니다. (fetch + merge = pull)

 

git fakeTeamwork - 앞으로 배울 레슨들에서 원격 저장소에서 일어난 변경들을 어떻게 로컬로 가져올것인지에 대해 배워 볼것입입니다.

그런데 여기서 우리는 불가피하게 그 변경들을 만들어야 되는데, 원격 저장소가 동료 / 친구 / 협력자등에 의해 특정 브랜치나 여러개의 커밋이 갱신되는 경우를 표현할 필요가 있습니다. 즉 우리는 팀워크를 "가장"할 필요가 있는것 입니다.

이런 문제를 해결하기 위해서 git fakeTeamwork 명령을 만들었습니다!

명령어에 추가할 커밋의 갯수나 어떤 브랜치에 추가할지 지정하는것도 가능합니다. git fakeTeamwork [브랜치명] [갯수]

 

git push - 원격 저장소에서의 변화들을 가져오는 방법도 알고 로컬의 내 작업과 합칠줄도 알게되었습니다. 이제 나의 훌륭한 작업을 다른 사람들과 공유하려면 어떻게 해야되는거죠? 공유된 작업을 내려받는것의 반대는 작업을 업로드해 공유하는것입니다. git push 여러분의변경을 정한 원격저장소에 업로드하고 그 원격 저장소가 여러분의 새 커밋들을 합치고 갱신하게 합니다. git push가 끝나고 나면, 여러분의 친구들은 원격저장소에서 여러분의 작업을 내려받을수 있게됩니다.

노트 -- git push를 매개변수 없이 사용하는 디폴트 행동은 push.default라 불리는 git의 설정에 따라 결정 됩니다. 이 설정의 기본값은 여러분이 사용하는 git 버전에 따라 다릅니다만, 우리 강의에서는 upstream을 값으로 사용합니다. 대단한것은 아니지만, 여러분이 프로젝트를 push하기전에 한번쯤 확인해볼 가치가 있습니다.

 

지금까지 우리는 다른곳에서 커밋을 pull해서 내려받고 우리가 만든 변경들을 push하는 방법을 배웠습니다. 간단해보이는데, 왜 사람들이 이것 때문에 곤란해 할까요?

어려움은 저장소의 히스토리가 엇갈릴 때 찾아옵니다. 자세히 살펴보기 전에 예제를 확인해봅시다...

상상을 해봅시다. 여러분은 월요일에 저장소를 clone해서 부가기능을 만들기 시작했습니다. 금요일쯤 기능을 공개할 준비가 되었습니다 -- 그런데 오 이런! 동료들이 주중에 코딩을 잔뜩해서 여러분이 만든 기능은 프로젝트에 뒤떨어져서 무용지물이 되었습니다. 이 사람들이 그 커밋들을 공유하고있는 원격 저장소에도 공개했습니다, 이제 여러분의 작업은 이제 의미가 없는 구버전의 프로젝트를 기반으로한 작업이 되어버렸습니다.

이런 경우, 명령어 git push가 할 일이 애매해집니다. git push를 수행했을때, git은 원격 저장소를 여러분이 작업했던 월요일의 상태로 되돌려야 할까요? 아니면 새 코드를 건들지 않고 여러분의 코드만 추가해야 되나요? 아니면 여러분의 작업은 뒤 떨어졌기 때문에 완전히 무시해야되나요?

이렇게 상황이 애매모호하기 때문에(히스토리가 엇갈렸기 때문이죠), git은 여러분이 push하지 못하게 합니다. 사실 여러분이 작업을 공유하기전에 원격 저장소의 최신 상태를 합치도록 강제합니다.

 

git pull --rebase    = (fetch+merge+rebase)

 

git branch -u [원격브랜치명] [브랜치명] 브랜치에 원격추적설정을 하는 방법입니다.

만약 [브랜치명](이)가 현재 작업하고 있는 브랜치라면 생략해도 됩니다.

 

git branch -u o/master - 원격-추적 브랜치

지난 몇개의 레슨에서 "마법"처럼 보일 수 있는게 하나 있었는데, git이 master브랜치가 origin/master와 연관 되어있는걸 안다는 것입니다. 물론 이 두 브랜치가 이름이 비슷하기 때문에 로컬 master브랜치가 원격의 master브랜치와 연결 되어있다고 하자면 어찌 논리적으로 말이 되긴 합니다만..., 이 연결은 두가지 시나리오를 통해 뚜렷하게 확인이 됩니다:

  • pull 작업을 하는 도중, 커밋들은 origin/master에 내려받아 지고 그다음 master 브랜치로 merge됩니다. merge에서 내재된 타겟은 이 연결에서 결정합니다.
  • push 작업을 하는 도중, master 브랜치의 작업은 원격의 master브랜치(로컬에서 origin/master로 표현되는)로 push 됩니다. push의 목적지는 master와 origin/master의 연결에서 결정됩니다.

 

간단히 말해서, 이 master와 origin/master사이의 연결은 브랜치의 "원격 추적" 속성을 통해 간단하게 설명됩니다. master브랜치는 origin/master브랜치를 추적하도록 설정되어 있습니다 -- 이것은 master가 merge와 push할 내재된 목적지가 생겼다는 뜻 입니다.

여러분은 어떻게 이 속성을 지정해주는 그 어떤 명령어 없이 master 브랜치에 설정되있는지 궁금할것 입니다. 사실, 여러분이 git으로 저장소를 clone할때 이 속성이 여러분을 위해 자동으로 설정 됩니다.

clone을 진행하면서 git은 원격 저장소에있는 모든 브랜치에 대해 로컬에 원격 브랜치를 생성합니다(origin/master같은것들 말이죠). 그 후 원격 저장소에서 현재 active한 브랜치를 추적하는 로컬 브랜치를 생성합니다, 대부분의 경우 master가 됩니다.

git clone이 완료되면, 여러분은 오로지 하나의 로컬 브랜치를 가지게 됩니다. 물론 원격 저장소에있는 여러 다른 브랜치도 여전히 확인할 수 있습니다(호기심이 많으시다면). 로컬, 원격 저장소 양쪽에 최적화 되있는거죠!

 

여러분은 아무 임의의 브랜치를 origin/master를 추적하게 만들 수 있습니다. 이렇게 하면 이 브랜치 또한 내재된 push,merge 목적지를 master로 할 것입니다. 여러분은 이제 totallyNotMaster라는 브랜치에서 git push를 수행해서 원격 저장소의 브랜치 master로 작업을 push할 수 있습니다!

이 속성을 설정하는데에는 두가지 방법이 있습니다.

▶방법 #1

첫 번째는 지정한 원격 브랜치를 참조해서 새로운 브랜치를 생성하여 checkout 하는 방법 입니다. 다음을 실행하면

git checkout -b [브랜치명] [원격브랜치명]

[브랜치명] 이라는 이름의 새 브랜치를 생성하고 [원격브랜치명] 을 추적하게 설정합니다.

방법 #2

브랜치에 원격 추적 설정을 하는 또 다른 방법으로는 간단하게 git branch -u 옵션을 사용하는 방법이 있습니다. 다음을 실행하면

git branch -u [원격브랜치명] [브랜치명]

 [브랜치명] 브랜치가 [원격브랜치명] 을 추적하도록 설정합니다. 만약 [브랜치명] 이 현재 작업하고 있는 브랜치라면 다음과 같이 생략해도 됩니다.

git branch -u [원격브랜치명]

 

Git push의 인자들

git push <remote> <place> - 여러분은 push를 하면 git이 push를 할 대상으로 원격저장소, 브랜치를 현재 작업중인 브랜치에 설정된 속성("추적" 대상)을 통해 알아낸다는것을  배웠습니다. 이것은 인자를 넣지않고 실행할 때 일어나는것 입니다, 그런데 git push에 다음과 같은 형식으로 선택적으로 인자를 사용할수도 있습니다. git push <remote> <place>

<place>인자가 무엇을 의미할것 같나요? 세부사항은 알아보기 전에 예시부터 봅시다. 다음 명령어를 보세요:

git push origin master

해석해 보면:

내 저장소에 있는 "master"라는 이름의 브랜치로 가서 모든 커밋들을 수집합니다, 그다음 "origin"의 "master"브랜치로 가서 이 브랜치에 부족한 커밋들을 채워 넣고 완료 되면 알려줍니다.

master를 "place"인자로 지정해서 우리가 git에게 어디서부터 커밋이 오는지, 그리고 어디로 커밋이 가야하는지 알려줍니다. 두 저장소간에 동기화 작업을 할 "장소"를 지정해 주는것이라고 볼 수 있습니다.

git이 알아야 할 것은 다 알려줬기 때문에(두 인자를 모두 지정했죠), git은 현재 우리가 체크아웃한 브랜치는 무시하고 명령을 수행합니다.

 

git push origin <source>:<destination>이전에 우리는 master를 커밋의 근원이되는 source와 목적지가 되는 destination으로 명령어의 인자로 넣어줌으로써 지정해줬습니다. 여러분은 이런 생각이 들 수 있어요 -- 내가 source와 destination이 다르길 원하면 어떻게 해야되지? 로컬의 foo 브랜치에서 원격의 bar 브랜치로 커밋을 push하고 싶으면 어떻게 해야 되지?

source와 destination을 모두 지정하기 위해서는, 이렇게 간단히 두개를 콜론을 사이에 두고 표현하면 됩니다.

git push origin <source>:<destination>

이것을 일반적으로 colon refspec(콜론 참조스펙)이라고 부릅니다. 참조스펙은 그냥 "git이 알아낼 수 있는 위치"를 이름 붙여서 말하는거에요 (브랜치 'foo'라던가 HEAD~1 라던가)

source와 destination을 따로 지정할 수 있게 되면서, 이제 원격관련 명령어를 좀 멋지고 정확히 사용할수 있게 되었어요.

source는 git이 이해하는 아무 위치를 말합니다.

 

Git fetch의 인자들

git fetch origin [브랜치명]git fetch에 다음 명령어와 같이 place를 지정해주면:

git fetch origin foo

Git은 원격 저장소의 foo 브랜치로 가서 현재 로컬에 없는 커밋들을 가져와 로컬의 origin/foo 브랜치 아래에 추가 할 것입니다. 커밋들을 foo브랜치에서만 내려받은 후 로컬의 origin/foo브랜치에만 적용합니다.

여러분은 분명 궁금할거에요 -- 왜 로컬의 foo에 그냥 커밋을 추가하지 않고 로컬의 원격 브랜치 origin/foo에 커밋들을 추가한거지? <place> 인자는 로컬하고 원격 저장소 모두에 똑같이 있는 곳을 의미한게 아니였나?

음, git이 이번 상황은 특별히 예외적으로 처리하기 때문입니다. 여러분이 foo브랜치에 작업을 했을지도 모르는데 이 명령으로 망쳐서 건드릴지도 모르니까요!  -- git fetch는 로컬의 원격 브랜치가 아닌 브랜치는 갱신하지 않습니다, 커밋들을 내려받기만 합니다(여러분이 확인해보고 나중에 병합할 수 있도록 말이죠).

"그렇다면, 이전 git push origin <source>:<destination>처럼 source와 destination를 모두 직접 지정해주면 어떻게될까요? 이 명령어로 말이죠 <source>:<destination>"

여러분이 커밋을 직접 로컬 브랜치로 fetch할 열의가 있다면, 네 콜론 참조스펙으로 지정해서 할 수 있습니다. 하지만 체크아웃된 브랜치에 fetch할 수 는 없고 체크아웃되지 않은 브랜치만 가능합니다.

주의 할점이 하나 있는데 -- <source>는 이제 받아올 커밋이 있는 원격에 있는 place를 넣어줘야하고 <destination>은 그 커밋들을 받아올 local의 place를 인자로 넣어줘야 합니다. git push와 정반대로 하는거죠, 데이터를 반대의 방향으로 옮기는 작업이니 이게 더 납득이 갑니다.

언급한 것 처럼, 실제로 이것을 하는 개발자들은 많지 않습니다. 이것을 소개하는것은 fetch와 push가 방향이 반대일뿐 컨셉이 비슷하다는것을 표현하기 위해서입니다. 만약 git fetch를 인자없이 수행하면 원격저장소에서 모든 원격 브랜치들로 커밋들을 내려받습니다...

 

Git은 <source> 인자를 두가지 방법으로 이상하게 사용합니다. 이 두가지 오용은 여러분이 git push와 git fetch에 source에 "없음"을 지정할 수 있기 때문에 나타납니다. "없음"을 지정하는 방법은 인자로 아무것도 안쓰면 됩니다:

  • git push origin :[브랜치명]
  • git fetch origin :[브랜치명]

위에 처럼 말이죠, 뭘 할 수 있는지 확인해봅시다...

"없음"을 원격 브랜치로 push하면 무엇을 할까요? 원격저장소의 그 브랜치를 삭제합니다!

마지막으로, "없음"을 fetch하면 로컬에 새 브랜치를 만듭니다.

 

Git pull의 인자들

git fetch git push의 인자들을 다 알았기 때문에, git pull에서 더 설명할게 사실 없습니다.

git pull은 결국 merge가 따라오는 fetch 그 자체이기 때문이죠. git fetch와 같은 인자를 사용하며 커밋들을 어디로 merge되는지 알면 됩니다.

정신나간것마냥-복잡한 인자들도 기본적으로는 똑같다고 보면 됩니다. 예시를 살펴봅시다:

git에서 다음 명령어들은 같습니다:

git pull origin foo 는 다음과 같습니다:

git fetch origin foo; git merge origin/foo

그리고...

git pull origin bar~1:bugFix 는 다음과 같습니다:

git fetch origin bar~1:bugFix; git merge bugFix

보이죠? git pull은 그저 fetch + merge의 축양형일 뿐이에요, 그리고 git pull은 커밋들이 도착하는곳을 신경씁니다(fetch를 하며 지정된 destination인자의 위치로 merge가 수행됩니다).

 

 

위 내용은 'Learn Git Branching' 사이트를 참고하였습니다. 더 자세한 내용이나 실습을 원하시는분은 다음의 사이트에서 배우실 수 있습니다.

https://learngitbranching.js.org

 

Learn Git Branching

An interactive Git visualization tool to educate and challenge!

learngitbranching.js.org

 

 

'웹(WEB) > Git' 카테고리의 다른 글

[Git] Fork 레파지토리 원격저장소와 동기화  (0) 2019.09.30