이 글은 지옥에서 온 문서 관리자 깃&깃허브 입문, 깃 튜토리얼 참고용 사이트, 깃 튜토리얼 공식 문서를 공부하며 정리한 글입니다.

공부자료와 실습 스크린숏을 포함하여 양이 방대하므로 양해 바랍니다.
명령어 별로 나누자니 역할이 다양해 그만큼 분산돼서 개념과 명령어를 여러 글에서 따로 봐야 하고
개념과 명령어 이둘로 나누자니 개념 이해한 뒤 명령어 보는 사람이 불편해질까봐 하나의 글로 합쳤습니다.

이 글에서는 Git의 내부 구조, 타 버전 관리 툴 (SVN, TFS 등)과의 비교 부분 범위까지 다루지는 않습니다.
혹시 Git 내부 구조와 동작 방식이 궁금하시다면 깃 튜토리얼 공식 문서 10.1 Git의 내부 부터 보시는 것을 추천드립니다. 

깃으로 버전 관리하기

버전

Git에서는 문서를 수정할 때마다 간단한 메모와 함께 수정 내용을 스냅숏으로 찍어서 저장한다.

이를 '버전'이라고 한다.

스테이지와 커밋 이해하기

작업 트리(working tree)

파일 수정, 저장 등의 작업을 하는 디렉터리로, '작업 디렉터리(working directory)'라고도 한다. 우리 눈에 보이는 디렉터리가 바로 작업 트리이다.

 

스테이지(stage)

버전으로 만들 파일이 대기하는 곳이다. 스테이징 영역(staging area)이라고 부르기도 한다. 예를 들어 작업 트리에서 10개의 파일을 수정했는데 4개의 파일만 버전으로 만들려면 4개의 파일만 스테이지로 넘겨주면 된다.

즉, 로컬 스테이지에 올려둔 파일만 원격 저장소에 커밋할 자격이 있는 것이다.

 

저장소(repository)

스테이지에서 대기하고 있던 파일들을 버전으로 만들어 저장하는 곳이다.

Git은 원격 저장소와 로컬 저장소 두 종류의 저장소를 제공한다.

  • 원격 저장소(Remote Repository): 파일이 원격 저장소 전용 서버에서 관리되며 여러 사람이 함께 공유하기 위한 저장소다.
  • 로컬 저장소(Local Repository): 내 PC에 파일이 저장되는 개인 전용 저장소다.

 

저장소를 만드는 방법은 두 가지가 있다.
아예 저장소를 새로 만들거나, 이미 만들어져 있는 원격 저장소를 로컬 저장소로 복사해 올 수 있다.

스테이지 내용은 .git/index 파일에 저장되고, 저장소의 내용은 .git/HEAD 파일에 저장된다.

 

리모트 저장소

리모트 저장소를 관리할 줄 알아야 다른 사람과 함께 일할 수 있다.
리모트 저장소는 인터넷이나 네트워크 어딘가에 있는 저장소를 말한다.
저장소는 여러 개가 있을 수 있는데 어떤 저장소는 읽고 쓰기 모두 할 수 있고 어떤 저장소는 읽기만 가능할 수 있다.
간단히 말해서 다른 사람들과 함께 일한다는 것은 리모트 저장소를 관리하면서 데이터를 거기에 Push 하고 Pull 하는 것이다.
리모트 저장소를 관리한다는 것은 저장소를 추가, 삭제하는 것뿐만 아니라 브랜치를 관리하고 추적할지 말지 등을 관리하는 것을 말한다.
이번에는 리모트 저장소를 관리하는 방법에 대해 설명한다.

원격 저장소라 하더라도 로컬 시스템에 위치할 수도 있다. “remote” 저장소라고 이름이 붙어있어도 이 원격 저장소가 사실 같은 로컬 시스템에 존재할 수도 있다. 여기서 “remote” 라는 이름은 반드시 저장소가 네트워크나 인터넷을 통해 어딘가 멀리 떨어져 있어야만 한다는 것을 의미하지 않는다. 물론 일반적인 원격 저장소와 마찬가지로 Push, Pull 등의 기능은 동일하게 사용한다.

리모트 저장소 확인하기

git remote 명령으로 현재 프로젝트에 등록된 리모트 저장소를 확인할 수 있다.
이 명령은 리모트 저장소의 단축 이름을 보여준다. 저장소를 Clone 하면 origin이라는 리모트 저장소가 자동으로 등록되기 때문에 origin이라는 이름을 볼 수 있다.

리모트 저장소 추가하기

기존 워킹 디렉터리에 새 리모트 저장소를 쉽게 추가할 수 있는데

git remote add <단축 이름> <url> 명령을 사용한다.

$ git remote origin
$ git remote add pb <https://github.com/paulboone/ticgit>
$ git remote -v
origin <https://github.com/schacon/ticgit> (fetch)
origin <https://github.com/schacon/ticgit> (push)
pb <https://github.com/paulboone/ticgit> (fetch)
pb <https://github.com/paulboone/ticgit> (push)

이제 URL 대신에 pb 라는 이름을 사용할 수 있다.
예를 들어 로컬 저장소에는 없지만 Paul의 저장소에 있는 것을 가져오려면 아래와 같이 실행한다.

$ git fetch pb
remote: Counting objects: 43, done.
remote: Compressing objects: 100% (36/36), done.
remote: Total 43 (delta 10), reused 31 (delta 5)
Unpacking objects: 100% (43/43), done.
From <https://github.com/paulboone/ticgit>
* [new branch] master -> pb/master
* [new branch] ticgit -> pb/ticgit

로컬에서 pb/master 가 Paul의 master 브랜치이다.
이 브랜치를 로컬 브랜치중 하나에 Merge 하거나 Checkout 해서 브랜치 내용을 자세히 확인할 수 있다.
(브랜치를 어떻게 사용하는지는 Git 브랜치에서 자세히 살펴본다)

fetch란? 원격 저장소의 데이터를 로컬에 가져오기만 하기

자세한 내용은 아래 fetch를 정리한 글에서 확인하자.

git status

내 로컬 Git 저장소의 현재 상태를 요약해서 보여준다.

git status 메시지 해석 예시

On branch master

현재 master 브랜치에 있다.

No commits yet

아직 커밋한 파일이 없다.

Changes to be committed

수정된 파일이며 Git 관리 대상에도 스테이지에도 올라간 파일 목록이다.

Untracked files

수정된 파일이며 Git 관리 대상에도 스테이지에도 올라가지 않은 파일 목록이다.

여기서 'Git 관리 대상'이라는 표현은 필자가 Git 공식 레퍼런스의 'Tracked files'에 대한 설명 중 'Git knows about'로 표현한 것을 보고 의역한 것이므로 마음에 들지 않으면 무시해도 된다.

git status의 Short Status

git status 출력문은 매우 포괄적이지만 그만큼 장황하다.
이럴 땐 -s--short 옵션으로 간략화된 출력문을 볼 수 있다.

$ git status -s
M README
MM Rakefile
A lib/git.rb
M lib/simplegit.rb
?? LICENSE.txt

?? : Git 관리 대상에도 스테이지에도 올라가지 않은 상태

A : 스테이지에는 추가된 상태

M : 변경된 파일

 

위 메시지에서 파일명 앞부분에 2글자 칼럼이 있는데, 왼쪽스테이지오른쪽작업 트리를 나타낸다.

예를 들면 README 는 작업 트리에서는 변경됐지만 아직 스테이지에 올라가지 않은 상태,
lib/simplegit.rb 는 작업 트리에서도 변경됐고 스테이지에도 올라간 상태,
Rakefile 은 작업 트리에서 변경됐고 스테이지에도 올라간 상태이며 그 후 다시 작업 트리에서 변경된 것이다.


수정한 파일을 스테이징 하기 ㅡ git add

Git에게 버전 만들 준비를 하라고 알려주는 것을 '스테이징(staging)' 또는 '스테이지에 올린다'라고 표현한다.

스테이징 내용을 .git/index 파일에 저장하기 때문에 스테이지에 올리는 것을 '인덱스(index)에 등록한다' 라고도 한다.

이는 'git add' 명령어로 가능하다.


스테이지에 올라온 파일을 커밋하기 ㅡ git commit

파일이 스테이지에 있다면 이제 버전을 만들 수 있다.
Git에서는 버전을 만드는 것을 간단히 '커밋(commit) 한다'고도 말한다.
커밋할 때는 그 버전에 어떤 변경 사항이 있었는지 확인하기 위해 메시지를 기록해 두어야 한다.

 

'git commit' 명령어에 '-m' 옵션을 붙이면 커밋과 함께 저장할 메시지를 적을 수 있다.

메시지는 명료하고 이해하기 쉽게 남겨야 본인뿐만 아니라 다른 사람이 커밋 이력을 확인하기 쉽다.
Git에서 권장하는 메시지 형식을 따르는 것도 좋다.

1번째 줄 : 커밋 내의 변경 내용을 요약
2번째 줄 : 빈칸
3번째 줄 : 변경한 이유

주로 위 형식으로 메시지를 작성한다.

작업 트리에서 변경된 이력을 원격 저장소에 공유하는 push와 헷갈리지 않도록 해야 한다.

스테이징과 커밋 한꺼번에 처리하기 ㅡ git commit -am

스테이지에 올리고 커밋하는 과정을 한꺼번에 처리할 수 있다.
단, 한 번이라도 커밋한 적 있는 파일을 수정 혹은 삭제한 후 재 커밋할 때에만 사용할 수 있다.

commit 옵션 -a과 -m의 조합이다.
-a(—all) 옵션의 공식 문서 설명은 다음과 같다.
Tell the command to automatically stage files that have been modified and deleted,
but new files you have not told Git about are not affected.
해석 : 명령을 실행하여 수정 및 삭제된 파일을 자동으로 스테이징 한다.
Git에 한 번도 추가된 적 없는 새 파일은 영향을 받지 않는다.

Git Basics - Recording Changes to the Repository
Skipping the Staging Area 설명 부분을 보면 자세히 나온다.

 

Changes not stage for commit

수정된 파일이며 Git 관리 대상에는 올라가 있지만 스테이지에 올라가지 않은 상태이다.
Untracked files과는 다른데, track의 뜻을 명확히 알고 싶어서 Git 공식 레퍼런스의 '2.2 Git Basics - Recording Changes to the Repository'를 찾아봤다.

track은 한 번이라도 커밋된 적 있는 파일을 추적하는 상태를 말하는 것 같다. untrack은 커밋이 한 번도 된 적이 없는

Remember that each file in your working directory can be in one of two states: tracked or untracked.
해석 : 작업 디렉터리의 각 파일은 tracked 또는 untracked의 두 가지 상태 중 하나에 있을 수 있다.

Tracked files are files that were in the last snapshot; they can be unmodified, modified, or staged. In short, tracked files are files that Git knows about.
해석 : Tracked files 마지막 스냅숏에 있었던 파일이며 수정된 것, 수정 안된 것 또는 스테이징 된 것일 수 있다.
간단히 말해서, 이는 Git 관리 대상 파일들이다.


Untracked files are everything else — any files in your working directory that were not in your last snapshot and are not in your staging area. When you first clone a repository, all of your files will be tracked and unmodified because Git just checked them out and you haven’t edited anything.
해석 : Untracked files 마지막 스냅숏에도 없고 스테이징 영역에도 없는 작업 디렉터리의 모든 파일이다. 처음 원격 저장소를 clone 할 때 Git은 파일을 체크아웃했지만 수정한 것이 없기 때문에 모든 파일이 추적되고 수정되지 않습니다.

파일 상태에 따른 Git에서의 관리 영역

 

파일이 하나라도 일부 수정 코드만 커밋하는 명령어를 찾아보니 아래 링크에 정리된 글이 있어서 남겨둔다.

수정된 소스의 일부분만 추가 및 커밋하기

git log

지금까지 커밋했던 기록을 살펴보기 위한 명령어이다.

각 커밋에는 영문/숫자로 이루어진 40자리 고유 이름이 붙는다.
저장소에선 이 40자리 이름을 보고 각 커밋을 구분하고 선택한다.
이를 '커밋 해시' 또는 '깃 해시' 라고 한다. 위 사진의 '10620b7cf...' 가 해당된다.

(HEAD → master)는 이 버전이 가장 최신이라는 표시이다.

 

https://git-scm.com/book/ko/v2/Git의-기초-커밋-히스토리-조회하기

여러 옵션 중 -p, --patch는 굉장히 유용한 옵션이다.
-p는 각 커밋의 diff 결과를 보여준다. 다른 유용한 옵션으로 '-2'가 있는데 최근 두 개의 결과만 보여주는 옵션이다.

또 git log 명령에는 히스토리의 통계를 보여주는 옵션도 있다. --stat 옵션으로 각 커밋의 통계 정보를 조회할 수 있다.

 

git diff

작업 트리에 있는 파일과 스테이지에 있는 파일을 비교하거나,
스테이지에 있는 파일과 저장소에 있는 최신 커밋을 비교해서 수정한 파일을 커밋하기 전에 최종적으로 검토할 수 있다.


git checkout 커밋 해시

지정한 커밋 해시로 이동하는 명령어다.

파일을 수정한 뒤 소스가 정상적으로 동작하지 않는 등의 이유로 수정한 내용을 취소하고 가장 최신 버전 상태로 되돌려야 할 때가 있다. 수천 줄이 넘는 소스를 수정하는 상황이면 직접 되돌리는 건 거의 불가능할 것이다.
이때 checkout 명령을 사용하면 작업 트리에서 수정한 내용을 쉽게 취소할 수 있다.

checkout으로 되돌린 내용은 다시 복구할 수 없다. restore 가 포함돼있기 때문이다.

git reset HEAD 파일 이름

수정된 파일을 스테이징 했을 때, 스테이징을 취소하는 방법이다.

git reset HEAD^

수정된 파일을 스테이 징하고 커밋까지 했을 때, 가장 마지막에 한 커밋을 취소하는 방법이다.

이렇게 되돌리면 커밋도 취소되고 스테이지에서도 내려진다.

취소한 파일이 작업 트리에만 남는 것이다.

 

git reset 명령의 옵션

—soft HEAD^ : 최근 커밋을 하기 전 상태로 작업 트리를 되돌린다.

—mixed HEAD^ : 최근 커밋과 스테이징을 하기 전 상태로 작업 트리를 되돌린다.
옵션 없이 git reset 명령을 사용할 경우 이 옵션을 기본으로 작동한다.
—hard HEAD^ : 최근 커밋과 스테이징, 파일 수정을 하기 전 상태로 작업 트리를 되돌린다.
이 옵션으로 되돌린 내용은 복구할 수 없다.

git reset 커밋 해시

특정 커밋으로 되돌리는 명령이다.

이 명령을 사용하면 HEAD 포인터를 제어해 브랜치를 이동할 수 있다.

위 상태에서 '7026d...' 가 최신 커밋인데 '10620...' 커밋으로 되돌리면

중간에 있는 '61e00...' 커밋과 '7026d...' 커밋이 삭제된다.

git revert 커밋 해시

커밋을 삭제하지 않고 되돌리는 명령이다.

reset과는 다르게 취소하려고 하는 커밋 해시를 지정하면 된다.

revert 명령을 실행할 때는 Git을 설치할 때 지정했던 기본 편집기가 자동으로 나타나면서 커밋 메시지를 입력할 수 있다. 커밋 메시지 맨 위에는 어떤 버전을 revert 했는지 나타나 있다. 문서 맨 위에 revert 하면서 추가로 남겨둘 내용이 있다면 입력하고 저장한다.


깃과 브랜치

모든 버전 관리 시스템에는 '브랜치(Branch)'라는 개념이 있다. 버전 관리 시스템에서는 나무가 가지에서 새 줄기를 뻗듯 여러 갈래로 퍼지는 데이터 흐름을 말한다.

브랜치 기능 살펴보기

깃으로 버전 관리를 시작하면 기본적으로 master라는 브랜치가 만들어진다.

사용자가 커밋할 때마다 master 브랜치는 최신 커밋을 가리킨다.

즉, 브랜치는 커밋을 가리키는 포인터와 비슷하다고 생각하면 된다.

새 브랜치를 만들면 기존에 저장한 파일을 master 브랜치에 그대로 유지하면서

기존 파일 내용을 수정하거나 새로운 기능을 구현할 파일을 만들 수 있다.

아래 그림처럼 master 브랜치에서 뻗어 나오는 새 브랜치를 만드는 것을 '분기(branch)한다'고 한다.

 

새 브랜치에서 원하는 작업을 다 끝냈다면 새 브랜치에 있던 파일을 원래 master 브랜치에 합칠 수 있다. 이렇게 분기했던 브랜치를 master 브랜치에 합치는 것을 '병합(merge)한다'고 한다.


브랜치 만들기

git branch

깃에서 브랜치를 만들거나 확인하는 명령어

 

git branch apple로 고객사인 apple을 위한 브랜치를 만들었다고 가정하자.

현재 브랜치는 master와 apple이 있으며 master 앞에 * 표시는 아직 master 브랜치에서 작업하고 있다는 뜻이다.

git log를 하면 위의 (HEAD → master, apple) 이 보일 텐데

HEAD → master 이므로 현재 작업 중인 브랜치는 master 브랜치라는 의미이다.

브랜치 사이 이동하기 ㅡ git switch

브랜치를 여러 개 만들었으면 각 브랜치를 오가면서 작업할 수 있어야 한다.
브랜치를 (없는 경우 생성하면서) 변경할 수 있다.

단순 변경은 switch '브랜치명', 생성 후 변경은 switch -c '브랜치명' 으로 가능하다.
이 외에도 다양한 옵션이 있으니 git switch —help로 확인 가능하다.

브랜치 사이 이동하기 ㅡ git checkout

브랜치를 여러 개 만들었으면 각 브랜치를 오가면서 작업할 수 있어야 한다.

master 브랜치뿐만 아니라 ms, google, apple 브랜치에도 최신 커밋이 'work 3' 이라는 뜻이다.

여기서 'work 3' 커밋에 해당하는 파일(예 : work.txt)에 어떠한 내용을 추가하고 'master work 4'라는 메시지와 함께 커밋한 후 git log —oneline으로 확인해보자.

참고로 —oneline 옵션은 한 줄에 한 커밋씩 나타내 주는 것이다.

최신 커밋인 'master work 4'는 master 브랜치에만 적용돼있다.
ms, google, apple 브랜치는 아직 'work 3' 커밋 상태이다.

 

이제 git checkout apple으로 apple 브랜치로 이동(체크아웃) 하고 log를 확인해보자.

$ 위에 나타난 파일 경로 끝에 (apple)이라고 표시될 것이다. 현재 브랜치가 apple이라는 뜻이다.

 

그리고 master 브랜치에서 apple 브랜치를 분기하기 전까지 master 브랜치에 있던 커밋들은 그대로 apple 브랜치에 복사된 것을 확인할 수 있다. 그리고 최신 커밋 해시에서 HEAD가 apple을 가리키고 있다. (HEAD → apple)

위는 커밋 'work 3'과 'master content 4'에 해당하는 work.txt의 파일 내용을 보여주고 있다. apple 브랜치는 최신 커밋이 'work 3' 이기 때문에 master 브랜치에서 입력했던 데이터가 없다.
이를 통해 master 브랜치에서 분기된 이후에 master 브랜치에 추가된 커밋은 apple 브랜치에 영향을 미치지 않았단 것을 알 수 있다.


브랜치 정보 확인하기

여기서는 여러 브랜치에서 각각 커밋이 이루어질 때 커밋끼리 어떤 관계를 하고 있는지 확인하는 방법과 브랜치 사이의 차이점을 확인하는 방법을 알아본다.

새 브랜치에서 커밋하기

apple 브랜치에 새로운 커밋을 만들기 위해 기존에 이어서 작업하던 work.txt라는 파일에 'apple content 4'를 추가 입력 후 저장해보자.

실제 업무에서는 apple 고객사만을 위한 파일도 필요할 테니 apple.txt라는 파일도 만들고 'apple content 4'를 추가 입력 후 저장해보자.

이제 이를 "apple content 4"라는 메시지로 커밋하고 로그를 확인하면 아래처럼 나온다.

 

(HEAD → apple)이니 현재 apple 브랜치에 체크아웃한 상태이고, apple의 최신 커밋은 'apple content 4'이다.

 

git log에서 —branches 옵션을 사용하면 각 브랜치의 커밋을 함께 볼 수 있다.

 

현재 apple 브랜치지만 master 브랜치의 커밋도 조회되는 것을 알 수 있다.

master 브랜치의 최신 커밋은 'master content 4'이고 ms 브랜치와 google 브랜치의 최신 커밋은 'work 3'이다.

 

브랜치와 커밋 관계를 좀 더 보기 쉽게 그래프 형태로 표시하려면 git log 명령에 —graph 옵션을 함께 사용한다.

 

커밋 내역 왼쪽에 직선 | 은 커밋과 커밋의 관계를 보여주는 것이다.

 

apple 브랜치의 최신 커밋은 'apple content 4'인데, 점선을 따라 'apple content 4' 커밋의 부모를 찾아가면 (위 로그는 찾아가는 방향이 아래가 되겠다.) 'work 3' 커밋을 만나게 된다.
즉, apple 브랜치에서는 'work 3' 커밋 다음에 'apple content 4' 커밋이 만들어졌다는 뜻이다.

 

master 브랜치의 최신 커밋은 'master content 4'인데, 직선을 따라가 보면 역시 부모 커밋 'work 3'을 만난다. apple 브랜치와 master 브랜치가 같은 부모 커밋을 갖고 있다.
즉, master 브랜치나 apple 브랜치는 'work 3' 커밋까지는 같고 그 이후부터 브랜치마다 다른 커밋을 만들었다는 사실을 알 수 있다.

브랜치 사이의 차이점 알아보기

브랜치마다 커밋이 점점 쌓여갈수록 브랜치 사이에 어떤 차이가 있는지 확인하기 어려워진다. 이때 브랜치 이름 사이에 마침표 두 개(..)를 넣는 명령으로 차이점을 쉽게 확인할 수 있다.

위처럼 하면 master 브랜치에는 없고 apple 브랜치에만 있는 커밋, 즉 'apple content 4' 커밋을 보여준다.

브랜치 순서를 반대로 하면 apple 브랜치에는 없고 master 브랜치에만 있는 커밋을 보여준다.


브랜치 병합하기

만들어진 각 브랜치에서 작업하다가 어느 시점에서 브랜치 작업을 마무리하고 기존 브랜치와 합해야 한다. 이것을 '브랜치 병합(merge)'이라고 한다.

각 상황마다 병합하는 방법을 알아보고 병합 시 발생하는 충돌과 해결방법도 알아보자.

서로 다른 파일 병합하기

우선 master 브랜치에 work2.txt 파일을 만들고 '1'을 입력하여 저장한 후 'work 1' 메시지와 함께 커밋한다.

master가 work 1 커밋을 가리키고, HEAD가 master 브랜치를 가리키고 있는 것이다.

 

이제 'o2'라는 브랜치를 만들고 master 브랜치에 master.txt라는 파일을 하나 더 만들고 'master 2'라고 입력하고 저장한 후 'master work 2' 메시지와 함께 커밋한다.

o2 브랜치를 먼저 만들었기 때문에 o2는 work 1 커밋을 가리키고 있는 상태이고, 이후 master 브랜치에서 새로운 작업을 했기 때문에 master가 master work 2 커밋을 가리키고, HEAD가 master 브랜치를 가리키고 있는 것이다.

 

이제 o2 브랜치로 체크아웃하고, o2 브랜치에서 o2.txt라는 파일을 만들고 'o2 2'라는 내용을 저장하고, 'o2 work 2'라는 메시지와 함께 커밋한다.

o2 브랜치가 o2 work 2 커밋을 가리키고, HEAD가 o2 브랜치를 가리킨다.

 

이제 o2 브랜치에서 작업이 다 끝났다고 가정하고, o2 브랜치의 내용을 master 브랜치로 병합하자. 우선 master 브랜치로 체크아웃 하자. 이후 브랜치를 병합하려면 git merge 명령 뒤에 가져올 브랜치 이름을 적는다. master 브랜치에 o2 브랜치를 가져와 병합하려면 다음과 같이 입력한다.

$ git checkout master
$ git merge o2

그러면 자동으로 편집기가 실행되면서 'Merge branch o2'라는 커밋 메시지가 나타난다. 이는 브랜치를 병합하면서 만들어지는 커밋 메시지다. 커밋 메시지를 수정할 수도 있고, 자동 메시지를 그대로 사용해도 된다.

Merge branch o2 커밋이 master 브랜치의 최신이며, HEAD는 master 브랜치를 가리킨다. o2 브랜치는 이제 master 브랜치에 병합된 것이다.

빨리 감기 병합(fast-forward merge)

master 브랜치에서 브랜치를 분기한 후에 master 브랜치에 아무 변화가 없다면(새로운 커밋을 만들지 않았다면) 분기한 브랜치를 병합하는 것은 간단하다. 분기한 브랜치에서 만든 최신 커밋을 master 브랜치가 가리키게만 하면 되기 때문이다.
이 경우에는 화면에 커밋 해시가 업데이트됐다는 내용과 함께 fast-forward라는 메시지가 나타난다. 이런 병합을 빨리 감기 병합(fast-forward merge)이라고 부른다.
git merge 명령의 결과가 단순히 포인터를 움직이기만 한 것이기 때문에 커밋 메시지 창은 열리지 않는다.

같은 문서의 다른 위치를 수정했을 때 병합하기

master 브랜치와 o3 브랜치에는 똑같이 work3.txt 파일이 있다고 가정하자. 양쪽 브랜치에 work3.txt 문서를 수정하되 서로 다른 위치를 수정한 후 브랜치를 병합했을 때 어떤 결과가 나오는지 확인해보자.

 

master 브랜치에서는 work3.txt의 3번째 줄에 'master content 2'라는 문장을 입력하고 커밋

o3 브랜치에서는 work3.txt의 6번째 줄에 'o3 content 2'라는 문장을 입력하고 커밋

master 브랜치에 o3 브랜치를 머지한 결과, 위처럼 모든 작업 문장이 반영된 것을 알 수 있다.

 

같은 문서의 같은 위치를 수정했을 때 병합하기

깃에서는 줄 단위로 변경 여부를 확인한다. 그래서 같은 줄의 내용을 서로 다른 브랜치에서 모두 수정하면 브랜치 충돌(conflict)이 발생한다.

이는 개발자가 직접 코드를 보며 해결해야 한다.

 

현재 master 브랜치는 'MERGING' 상태인 것을 알 수 있다.

'<<<<<<< HEAD'와 가운데 가로줄(=======) 사이의 내용은 현재 브랜치인 master 브랜치에서 수정한 내용이다.

가로줄(=======)과 '>>>>>>> o4' 사이의 내용은 o4 브랜치에서 수정한 내용이다.

양쪽 브랜치의 내용을 참고하며 직접 내용을 수정해야 한다.

그런데, 코드의 양이 많으면 비효율적이므로 이를 도와주는 머지 툴(예 : Beyond Compare, Diffmerge)이 있으니 잘 활용하자.

병합이 끝난 브랜치 삭제하기

브랜치를 병합한 후 더 이상 사용하지 않는 브랜치는 깃에서 삭제할 수 있다.

단, 브랜치를 삭제해도 완전히 지워지는 게 아니라서 다시 같은 이름의 브랜치를 만들면 예전 내용을 볼 수 있다.

즉, 브랜치를 삭제한다는 것은 완전히 저장소에서 없애는 것이 아니라 깃의 흐름 속에서 감추는 것이라고 생각하면 된다.

master 브랜치에 병합하지 않은 브랜치를 삭제하려면 오류 메시지가 나타난다.
이럴 경우 -d 대신 -D를 사용해서 강제로 브랜치를 삭제할 수 있다.

깃에는 여태까지 참조했던 모든 것을 기록으로 남겨두는데 이것을 조회하려면 git reflog 명령어를 사용해야 한다. 실수한 git 명령 작업을 복구할 때 사용될 것이므로 개념을 알아두자.

브랜치 관리하기

HEAD는 현재 작업 트리(워킹 디렉터리)가 어떤 버전을 기반으로 작업 중인지를 가리키는 포인터다. 기본적으로는 master 브랜치를 가리킨다.

그리고 브랜치는 기본적으로 브랜치에 담긴 커밋 중에서 가장 최근의 커밋을 가리킨다.

예를 들어 저장소에 c1 커밋을 만들면 HEAD는 master 브랜치를 가리키고 master 브랜치는 c1 커밋을 가리킨다.

이 상태에서 sub 브랜치를 만들면 sub 브랜치 또한 c1 커밋을 가리킨다.

 

이 상태에서 c2.txt를 만들고 c2라는 메시지와 함께 커밋하면 아래처럼 master 브랜치가 c2 커밋을 가리키고 HEAD는 master를 가리킨다.

이 상태에서 sub 브랜치에 커밋을 만들면 아래처럼 sub브랜치가 해당 커밋을 가리키고 HEAD가 sub브랜치를 가리키게 된다.

 

브랜치가 여러 개일 때 reset 명령은 어떻게 사용할 수 있을까?

이때는 현재 브랜치가 아닌 다른 브랜치에 있는 커밋을 골라서 최신 커밋으로 지정할 수 있다.

위의 be0c34e에 해당하는 c2 커밋을 reset 해보고 log를 조회해보자.

 

그 결과 sub 브랜치의 최신 커밋이 master 브랜치의 최신 커밋인 c2로 바뀌었다. 그리고 HEAD는 그대로 sub 브랜치를 가리키고 있다.

이제 sub 브랜치는 c2 커밋을 가리키고 있기 때문에 원래 가리키고 있던 s1 커밋은 연결이 끊기면서 삭제된다.

git reset 명령어를 사용하면 HEAD가 가리키고 있는 브랜치의 최신 커밋을 원하는 커밋으로 지정할 수 있다.

이때 어떤 브랜치에 있는 커밋이든 지정할 수 있으며, 명령을 수행한 뒤 브랜치와 연결이 끊긴 커밋은 삭제된다.

수정 중인 파일 감추기 및 되돌리기 ㅡ git stash

https://backlog.com/git-tutorial/kr/reference/stash.html

 

누구나 쉽게 이해할 수 있는 Git 입문~버전 관리를 완벽하게 이용해보자~ | Backlog

누구나 쉽게 알 수 있는 Git에 입문하신 것을 환영합니다. Git을 사용해 버전 관리를 할 수 있도록 함께 공부해봅시다!

backlog.com

master 브랜치에서 작업하는 도중 apple 브랜치로 checkout 해서 다른 작업을 해야 할 일이 생겨서 git checkout apple을 했더니 아래의 에러 메시지와 함께 거절됐다.

error: Your local changes to the following files would be overwritten by checkout: work.txt Please commit your changes or stash them before you switch branches. Aborting
오류: 다음 파일에 대한 로컬 변경사항을 체크아웃으로 덮어쓴다. work.txt
branch를 전환하기 전에 변경 사항을 커밋하거나 스테이징 해라. 중단

따라서 master 브랜치의 작업을 임시로 저장해 두고 싶었고 그때 필요한 명령어가 바로 git stash이다.

git stash를 사용하려면 파일이 tracked 상태여야 한다. 즉, 한 번이라도 커밋한 상태여야 한다.

 

$ git stash save

save는 생략할 수 있습니다. 또한, save 뒤에 내용을 나타내는 메시지를 지정할 수 있습니다.
$ git stash list

여기서 stash 목록들은 stack처럼 저장된다.
이 때문에, 최신으로 추가된 stash@{인덱스}에서 인덱스가 0이고 나중에 추가된 파일일수록 인덱스가 증가된다.
$ git stash pop

인수를 지정하지 않으면 저장된 작업 목록 중 최신으로 저장된 작업을 복원한다.
stash@{1}과 같이 인수를 지정하면 특정 작업을 선택하여 복원할 수 있다.
$ git stash drop

인수를 지정하지 않으면 저장된 작업 목록 중 최신으로 저장된 작업을 삭제한다.
stash@{1}과 같이 인수를 지정하면 특정 작업을 선택하여 삭제할 수 있다.
$ git stash clear
  • stash 목록에 저장된 수정 내용을 나중에 또 사용할지도 모른다면
$ git stash apply

stash 목록에서 최신 항목을 되돌리지만 저장했던 내용은 그대로 남겨둔다.

깃허브로 백업하기

원격 저장소에 파일 올리기 ㅡ git push

$ git push -u origin master

위 코드는 지역 저장소의 브랜치를 origin, 즉 원격 저장소의 master 브랜치로 push 하라는 명령이다.

여기서 -u 옵션은 지역 저장소의 브랜치를 원격 저장소의 master 브랜치에 연결하기 위한 것으로

처음에 한 번만 사용하면 된다.

$ git push <저장소명> <브랜치명>

원격 저장소명을 먼저 입력하고 내가 push 하고 싶은 브랜치명을 이어서 입력하면 된다.

원격 저장소에서 파일 내려받기 ㅡ git pull

원격 저장소와 지역 저장소의 상태를 같게 만들기 위해 원격 저장소의 소스를 가져오는 것이다.

이해가 잘 안 된다면 아래에서 설명할 fetch 관련 설명의 fetch + merge를 다시 봐도 좋다.

SSH 원격 접속이란

SSH는 Secure Shell의 줄임말로 보안이 강화된 안전한 방법으로 정보를 교환하는 방식이다. SSH에서는 기본적으로 Private Key와 Public Key를 한 쌍으로 묶어서 컴퓨터를 인증한다. Private Key는 사용자 컴퓨터에 저장되고, Public Key는 외부에 공개된다. 사용자 컴퓨터에서 SSH 키 생성기를 실행하면 두 Key가 만들어진다.

이 두 Key를 사용해 현재 사용 중인 기기를 깃허브에 인증하는 것이다.

이를 이용하면 로그인을 요구할 때 아이디와 비밀번호를 입력하지 않아도 돼 번거로움이 줄어든다.


깃허브로 협업하기

원격 master 브랜치

말 그대로 원격 저장소의 master 브랜치다.

아래의 스크린숏을 보면 내 로컬 저장소에서 알고 있는 원격 저장소의 master 브랜치의 위치가 Merge branch 'o4' for master with latest task. 커밋 임을 알 수 있다.

물론 원격 저장소에 다른 사람이 함께 작업 중인데, master 브랜치에 push를 했다면, 내 로컬 저장소에서 최신화할 때 origin/master 가 가리키는 커밋 위치가 변경된다.

원격 브랜치 정보 가져오기 ㅡ git fetch

fetch란? 원격 저장소의 데이터를 로컬에 가져오기만 하기

pull을 실행하면, 원격 저장소의 내용을 가져와 자동으로 병합(merge) 작업을 하게 된다.
그러나 단순히 원격 저장소의 내용을 확인만 하고 로컬 데이터와 병합은 하고 싶지 않은 경우에는 fetch 명령어를 사용할 수 있다.

fetch를 실행하면, 원격 저장소의 최신 이력을 확인할 수 있다. 이때 가져온 최신 커밋 이력은 이름 없는 브랜치로 로컬에 가져오게 된다. 이 브랜치는 'FETCH_HEAD'의 이름으로 체크아웃할 수도 있다.

fetch + merge = pull

$ git checkout master
$ git merge FETCH_HEAD

원격 브랜치 정보를 가져올 때, FETCH_HEAD라는 포인터로 원격 브랜치의 커밋을 가리킨다.

패치로 가져온 브랜치 한 번에 병합하기

패치한 뒤 병합할 때 원격 master 브랜치에 있는 커밋이라면 다음과 같이 병합한다.
$ git merge origin/master

다른 브랜치에 있는 커밋이라면 다음과 같이 병합한다.
$ git merge origin/브랜치 이름

하지만 매번 브랜치 이름을 써야 한다면 번거로우니 다음과 같이 명령하면 패치한 뒤 지역 저장소에 반영하지 않은 최신 커밋을 병합 가능하다.
$ git merge FETCH_HEAD
협업 중에는 내가 원격 저장소에 push 하기 전 pull을 하고 나서 올려야 한다는 점을 잊지 말자.

 

새로 만든 브랜치 푸시하기

$ git checkout -b 브랜치 이름
$ git push origin 브랜치 이름

Pull Request로 push 한 브랜치 병합하기

원격 저장소에 push 하고 나면 프로젝트 관리자에게 이 내용을 알려야 하는데 이것을 Pull Request라고 한다.
Git 호스팅 사이트에서 관리자에게 보낼 메시지를 생성하거나 git request-pull 명령으로 이메일을 수동으로 만들 수 있다. GitHub의 “Pull Request” 버튼은 자동으로 메시지를 만들어 주는데 관련 내용은 여기 에서 살펴볼 수 있다.

 

그리고 이 링크를 참고하면 git request-pull 명령으로 커맨드 창에서 Pull Request를 요청해볼 수 있겠지만, 요즘은 Github 사이트에서 간편한 UI를 활용하거나 Github Desktop, SourceTree 같은 Git GUI를 활용하는 게 보편화돼있고 기능별 버튼이 마련돼있어 훨씬 직관적이다.

 

Github의 경우 저장소 상단의 Pull Request 메뉴에서 "New pull request" 버튼을 누르면,

어떤 브랜치를 기준(base)으로 어떤 작업 브랜치를 비교(compare)해 요청할지 선택한 후

 

해당 작업 파일들의 변경(추가와 수정은 연두색에 '+', 삭제는 붉은색에 '-') 파일들의 변경 라인을 미리 요약하여 조회해보고 요청할 수 있다.

요청 이후 코드에 대한 리뷰 피드백을 이어서 반영할 수도 있다.

병합해도 된다고 판단했다면 원하는 병합 방식을 선택한 후 "Merge pull request" 버튼을 누르면 된다.

아래 그림에 나온 3가지 병합 방식에 대해서는 이 글을 참고하기 바란다.

 


출처

 

지옥에서 온 문서 관리자 깃&깃허브 입문

깃 튜토리얼 참고용 사이트

깃 튜토리얼 공식 문서

 

[Tech] Git 2.23.0 출시: checkout 기능 분리(switch, restore)
http://honeymon.io/tech/2019/08/30/git-2.23-release.html

'Git' 카테고리의 다른 글

Git Pull Request 컨벤션  (0) 2021.03.26
Git 커밋 메시지 컨벤션  (2) 2021.03.26