# 이 챕터에서 내가 기억해야 할 만한 것들만 정리
- 실습 상황 설정하기
- 먼저 브랜치를 만들어야 하는 상황을 가상으로 설정해 본다. work 1, 2, 3라는 내용으로 3번 커밋했고, 현재 master 브랜치에는 txt 파일이 하나 있다.
- 새 브랜치 만들기
$ git branch
- .깃에서 브랜치를 만들거나 확인하는 명령이다.
$ git branch apple
- 새로운 브랜치를 만들려면 git branch 다음에 만들고 싶은 브랜치 이름을 적는다.
$ git branch
apple
google
* master
ms
이렇게 여러 개의 브랜치를 만들어 보았다. master 앞의 *은 우리가 master 브랜치에서 작업하고 있다는 것을 의미한다.
- 브랜치 사이 이동하기
- 현재는 master, apple, google, ms 브랜치의 최신 커밋이 모두 work 3이다. 한번 work 4라는 내용으로 커밋을 시도해 보겠다.
$ git log --oneline
--oneline 옵션을 사용하면 한 줄에 한 커밋씩 나타내주기 때문에 커밋을 간략히 확인할 때 편리하다.
dndkg@kei MINGW64 ~/manual (master)
$ git log --oneline
65cd8c3 (HEAD -> master) master commit 4
7ae2d59 (ms, google, apple) work 3
370706a work 2
5f79679 work 1
다음과 같이 master 브랜치만 최신 커밋인 master commit 4가 적용되어 있고 ms, google, apple 브랜치는 work 3 커밋 상태임을 확인할 수 있다.
이 상태에서 다른 브랜치로 이동해보겠다.
그런데... 저번 게시물에서 먼저 다루었듯이 책에서는 checkout 명령을 통해 다른 브랜치로 이동하라고 알려주고 있지만, 우리는 이미 switch 명령을 통해 브랜치 사이를 이동해야 함을 알고 있다. 정말 다행히도 checkout 명령과 형태는 같고 단어를 대체해주기만 하면 된다.
$ git switch apple
// checkout을 switch로 작성
dndkg@kei MINGW64 ~/manual (master)
$ git switch apple
Switched to branch 'apple'
dndkg@kei MINGW64 ~/manual (apple)
정상적으로 apple 브랜치로 이동한 모습이다.
dndkg@kei MINGW64 ~/manual (apple)
$ git log --oneline
7ae2d59 (HEAD -> apple, ms, google) work 3
370706a work 2
5f79679 work 1
git log를 통해 확인해도 master 브랜치에서 이뤄진 커밋은 영향을 주지 않았다는 것을 알 수 있다.
- 새 브랜치에서 커밋하기
- 새로 만든 apple 브랜치에서 또 다른 수정사항을 만든 뒤 커밋해보겠다.
$ git add .
$ git commit -m "apple content 4"
git add 뒤에 마침표(.)를 추가하면 현재 저장소에서 수정된 파일을 한꺼번에 스테이지에 올릴 수 있다.
dndkg@kei MINGW64 ~/manual (apple)
$ git log --oneline
1e1ff72 (HEAD -> apple) apple content 4
7ae2d59 (ms, google) work 3
370706a work 2
5f79679 work 1
현재 apple 브랜치에서 작업하고 있으며, apple 브랜치에만 수정사항이 적용된 것을 알 수 있다.
$ git log --oneline --branches
--branches 옵션을 추가하는 것으로 각 브랜치의 커밋을 함께 볼 수 있다.
$ git log --oneline --branches --graph
--graph 옵션까지 추가하게 되면 브랜치와 커밋의 관계를 더욱 쉽게 알아볼 수 있다.
$ git log --oneline --branches --graph
* 1e1ff72 (HEAD -> apple) apple content 4
| * 65cd8c3 (master) master commit 4
|/
* 7ae2d59 (ms, google) work 3
* 370706a work 2
* 5f79679 work 1
기호를 통해 work 3 커밋까지는 같지만 master 브랜치와 apple 브랜치에 각자 새로운 커밋이 만들어졌다는 것을 알 수 있다.
- 브랜치 간 차이점 알아보기
$ git log master..apple
- 위 코드와 같이 두 브랜치 사이에 마침표 2개(..)를 넣어주면 차이점을 쉽게 확인할 수 있다. 이 명령은 마침표 왼쪽에 있는 브랜치를 기준으로 오른쪽 브랜치와 비교하게 된다.
$ git log master..apple
commit 1e1ff7287166a8883220a1c119197aaff723aed9 (HEAD -> apple)
Author: Aqua <dndkgds@gmail.com>
Date: Tue Jul 12 21:16:20 2022 +0900
apple content 4
이런 식으로 master 브랜치에는 없고 apple 브랜치에만 존재하는 커밋을 보여준다.
- 서로 다른 파일 병합하기
- 새로운 저장소를 만들어 필요한 브랜치와 커밋만 사용해 연습해 보도록 하자.
$ cd ~
$ git init manual_2
$ cd manual_2
$ ls -la
git init 다음에 디렉토리 이름을 입력하면 새로운 디렉토리를 생성하고 저장소를 초기화하는 과정을 한번에 처리할 수 있다.
dndkg@kei MINGW64 ~/manual_2 (o2)
$ git log --oneline --branches --graph
* 7b27783 (HEAD -> o2) o2 work 2
| * 73a51da (master) master work 2
|/
* ad5122d work 1
현재 상황을 정리해보면, 커밋 'work 1'은 master 브랜치와 o2 브랜치가 모두 가지고 있고, master 브랜치에는 'master work 2' 커밋이, o2 브랜치에는 'o2 work 2' 커밋이 각각 존재하는 상황이다. 이 상태에서 o2 브랜치의 내용을 master 브랜치로 병합해보겠다.
$ git switch master
일단 마스터 브랜치로 스위치한다.
$ git merge o2
git merge 다음에 병합시킬 브랜치 이름을 적는다. 지금은 master 브랜치에 o2 브랜치를 가져와 병합할 예정이므로 위 내용과 같이 적는다.
명령을 실행하면 자동으로 vim이 실행되면서 'Merge branch 'o2''라는 커밋 메시지가 나타난다. 수정해도 되고, 그대로 냅둬도 된다. 적당히 수정했다면 저장 후 편집기를 종료한다.
$ ls -la
total 23
drwxr-xr-x 1 dndkg 197609 0 Jul 12 22:05 ./
drwxr-xr-x 1 dndkg 197609 0 Jul 12 22:07 ../
drwxr-xr-x 1 dndkg 197609 0 Jul 12 22:07 .git/
-rw-r--r-- 1 dndkg 197609 10 Jul 12 22:03 master.txt
-rw-r--r-- 1 dndkg 197609 6 Jul 12 22:05 o2.txt
-rw-r--r-- 1 dndkg 197609 2 Jul 12 21:38 work.txt
두 브랜치가 병합되어 o2 브랜치에만 존재했던 o2.txt 파일이 master 브랜치에 존재하는 모습이다.
$ git log --oneline --branches --graph
* 31c0952 (HEAD -> master) Merge branch 'o2'
|\
| * 7b27783 (o2) o2 work 2
* | 73a51da master work 2
|/
* ad5122d work 1
위에서 이미 다루었던 --graph 옵션을 통해 브랜치가 다시 합쳐진 과정을 살펴볼 수 있다.
※ 빨리 감기 병합이란?
- master 브랜치에서 브랜치를 분기한 후에 master 브랜치에 아무 변화가 없다면 브랜치를 병합하는 것이 더욱 간단해진다. 분기한 브랜치에서 만든 최신 커밋을 그냥 master 브랜치가 가리키기만 하면 되기 때문. 이 경우에는 화면에 커밋 해시가 업데이트되었다는 내용과 함께 fast-forward라는 메시지가 나타난다. 이런 병합을 빨리 감기 병합이라고 하며, 따로 커밋 메시지 창은 열리지 않는다.
※ 브랜치를 병합할 때 편집기 창이 열리지 않게 하려면
- 굳이 커밋 메시지를 새로 작성하지 않겠다면 브랜치를 병합할 때 커밋 메시지를 추가 작성하라고 뜨는 편집기 창이 마음에 안들 수 있다. 이 때에는 --no-edit 옵션을 추가하면 된다.
$ git merge o2 --no-edit
브랜치를 병합할 때 편집기 창이 나타나지 않도록 설정한 경우, 커밋 메시지를 추가하거나 수정하고 싶다면 --edit 옵션을 다시 추가해주면 된다.
$ git merge o2 --edit
- 같은 문서의 다른 위치를 수정했을 때 병합하기
- 위에서 만든 예제에서 master 브랜치와 o2 브랜치에는 공통적으로 work.txt가 존재하는데, 양쪽 브랜치에서 이 문서를 수정하되 서로 다른 위치를 수정하고 브랜치를 병합하면 어떻게 될까? 를 확인해보겠다.
지금 master 브랜치의 work.txt 파일과 o2 브랜치의 work.txt 파일을 모두 수정했지만 문서 안의 수정 위치는 다르다.
$ git switch master
o2 브랜치를 master 브랜치에 합치기 위해 master 브랜치로 스위치한다.
git merge o2
이어서 바로 git merge 명령을 사용한다.
Merge branch 'o2'
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
~
~
~
자동으로 vim이 실행되며 커밋 메시지가 나타난다.
$ git merge o2
Auto-merging work.txt
Merge made by the 'ort' strategy.
work.txt | 1 +
1 file changed, 1 insertion(+)
병합 이후 터미널에 "Auto-merging work.txt"라는 문구가 나타나며 수정 내용을 자연스럽게 합쳐준다.
- 같은 문서의 같은 위치를 수정했을 때 병합하기
- Git에서는 줄 단위로 변경 여부를 확인하는데, 이 때문에 각 브랜치에 같은 파일 이름을 가지고 있으면서 같은 줄을 수정했을 때 브랜치를 병합하면 브랜치 충돌이 발생한다. 이를 해결하는 방법에 대해 알아보자.
- 현재 상황: master 브랜치와 o2 브랜치에 각각 work.txt가 존재하며, 같은 위치를 다른 문구로 수정한 상황이다.
$ git merge o2
Auto-merging work.txt
CONFLICT (content): Merge conflict in work.txt
Automatic merge failed; fix conflicts and then commit the result.
git merge 명령을 사용해 o2 브랜치를 master 브랜치에 병합시키려고 시도했더니 오류 메시지를 띄운다.
# title
content
<<<<<<< HEAD
master content 2
=======
o2 content 2
>>>>>>> o2
#title
content
~
현재 work.txt의 내용인데, <<<<<<< HEAD와 ======= 사이의 내용은 master 브랜치에서 수정한 내용, =======와 >>>>>>> o2 사이의 내용은 o2 브랜치에서 수정한 내용이다. 이와 같이 충돌이 발생하면 직접 내용을 수정해줘야 한다.
$ git log --oneline --branches --graph
* caee4b8 (HEAD -> master) merge o2 branch
|\
| * d49ea99 (o2) o2 work 2
* | 9fe0b61 master commit 2
|/
* 2dbe6c1 work 1
지금까지 수행한 작업의 과정을 git log를 이용해 확인해 보면 다음과 같다.
※ 병합 및 충돌 해결 프로그램
깃의 브랜치 병합을 자동으로 처리해 주고 충돌을 해결해 주는 서드파티 프로그램들이 많이 나와있다. 병합 알고리즘에는 2 way merge와 3 way merge가 있는데 3 way merge를 지원하는 프로그램을 추천하고 있다.
- 병합이 끝난 브랜치 삭제하기
- 브랜치를 병합하고 더 이상 사용하지 않는다면 Git에서 삭제할 수 있다. 그러나 다시 같은 이름의 브랜치를 생성하면 예전 내용을 다시 볼 수 있다.
$ git branch
* master
o2
현재 브랜치는 master와 o2 2개가 존재하고 있다. 저장소의 기본 브랜치는 master이므로 브랜치를 삭제하기 위해서는 master 브랜치에서 작업해야 한다.
$ git branch -d o2
브랜치를 삭제할 때는 git branch 명령에 -d 옵션을 사용하면 된다.
* master 브랜치에 병합하지 않은 브랜치를 삭제하려면 오류 메시지가 나타난다. 이 때는 -D 옵션을 사용하면 강제로 브랜치를 삭제할 수 있다.
$ git branch -d o2
Deleted branch o2 (was d49ea99).
정상적으로 브랜치가 삭제된 것을 확인할 수 있다. 그러나 같은 이름의 브랜치를 다시 생성하면 예전에 작업했던 내용이 그대로 나타난다.
- 브랜치에서 checkout과 reset의 작동 원리
※ 책에서는 브랜치를 이동할 때 checkout 명령을 사용하라고 명시하고 있고, 이 문단 역시 브랜치를 이동할 때의 checkout 명령의 작동 원리를 설명하는 것이다. 그래서 이 문단에서는 책과는 적당히 조율하면서 진행하겠다.
- 여기서는 먼저 HEAD와 브랜치의 개념부터 알아보도록 하자.
$ cd ~
$ git init test
$ cd test
일단 홈 디렉토리에 test라는 깃 저장소를 만들고, test 디렉토리로 이동한다.
$ vim c1.txt
$ git add c1.txt
$ git commit -m "c1"
간단하게 c1.txt를 만들고 내용에는 숫자 1만 적은 채로 커밋한다.
$ git log
commit 49ec08e50fe4e20c0aab250dc80208b2923bf220 (HEAD -> master)
Author: fuyuu <dndkgds@gmail.com>
Date: Mon Jul 18 17:18:22 2022 +0900
c1
commit log를 확인해 보면 첫번째 줄에 (HEAD -> master) 라는 문구를 확인할 수 있다.
여기서 HEAD는 현재 작업 트리가 어떤 버전을 기반으로 작업 중인지를 가리키는 포인터이다. HEAD는 기본적으로 master 브랜치를 가리킨다. 그리고 브랜치는 브랜치에 담긴 커밋 중에서 가장 최근의 커밋을 가리킨다.
지금까지 따라했다면 HEAD는 master 브랜치를 가리키고 master 브랜치는 49ec08e 커밋을 가리키게 되는 것이다.
$ git branch sub
이제 sub 브랜치도 생성했기 때문에 sub 브랜치도 49ec08e 커밋을 가리키게 된다.
$ git commit -m "c2"
[master 04f5fe9] c2
1 file changed, 1 insertion(+)
create mode 100644 c2.txt
master 브랜치에 c2.txt 파일을 생성하여 커밋했다. 이제 master 브랜치는 04f5fe9 커밋을 가리키고 있으며 HEAD는 그대로 master 브랜치를 가리키고 있다. sub 브랜치에는 아무 짓도 하지 않았으므로 여전히 49ec08e 커밋을 가리키고 있다.
$ git switch sub
switch 명령을 이용하여 sub 브랜치로 이동한다. 이 명령을 사용하면 HEAD가 sub 브랜치를 가리키게 된다. 앞에서도 사용했던 switch 명령(책에서는 checkout 명령)을 사용하면 HEAD가 가리키는 것을 제어할 수 있다.
$ git commit -m "s1"
[sub d9027a5] s1
1 file changed, 1 insertion(+)
create mode 100644 s1.txt
sub 브랜치에 s1.txt 파일을 생성해서 커밋했다. 이제 HEAD는 sub 브랜치를 가리키고, sub 브랜치는 d9027a5 커밋을 가리키고 있다.
- 브랜치가 여러 개일 때 reset 명령은 어떻게 사용하면 되는 지 알아보도록 하자.
Ch.02에서 배웠던 reset 명령으로는 master 브랜치에 있던 여러 커밋 중 하나를 골라서 되돌아갔다. 하지만 브랜치가 여러 개라면 현재 브랜치가 아닌 다른 브랜치에 있는 커밋을 골라서 최신 커밋으로 지정할 수 있다. 예컨대, sub 브랜치에 있는 상태에서 master 브랜치에 있는 04f5fe9 커밋을 sub 브랜치의 최신 커밋으로 지정할 수 있는 것이다.
$ git reset 04f5fe9
sub 브랜치에 위치한 상태에서 위 명령어를 입력하면 master 브랜치의 04f5fe9 커밋이 덮어씌워진다.
$git log --oneline --branches --graph
* 04f5fe9 (HEAD -> sub, master) c2
* 49ec08e c1
다음과 같이 c2라는 커밋 메시지가 적힌 커밋이 sub와 master 브랜치의 최신 커밋이 된 모습이다. 이 경우 기존에 sub 브랜치가 가리키고 있던 d9027a5 커밋은 연결이 끊기면서 삭제된다.
- 수정중인 파일 감추기 및 되돌리기
- 한가지 상황을 가정해보자. 브랜치에서 파일을 수정하고 커밋하지 않은 상태에서 급하게 다른 파일을 커밋해야 할 경우가 생길 수도 있다. 이 경우 그냥 무시하고 진행해도 상관은 없겠지만 계속 커밋하라는 메시지가 출력되기 때문에 번거롭다. 게다가 실수로 다른 파일과 함꼐 커밋이 될 수도 있다. 이러한 경우에 아직 커밋하고 작업 중인 파일들을 잠시 감춰둘 수 있는 기능이 존재한다. 그리고 당장 필요한 작업들을 끝낸 후에는 다시 감춰둔 파일들을 꺼내오면 된다. 이 방법에 대해서 알아보도록 하자.
$ cd ~
$ git init st
$ cd st
연습을 위해 홈 디렉토리에 새로운 디렉토리를 만들어준다.
우리가 연습하려는 git stash 명령은 파일이 tracked 상태여야 한다. 그렇기 때문에 일단 아무 내용이나 적어서 f1.txt와 f2.txt의 2개의 파일을 만들어서 커밋해보겠다.
커밋한 이후에 f1.txt와 f2.txt를 vim으로 열어 아무렇게나 내용을 수정한 뒤 저장해놓는다.
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: f1.txt
modified: f2.txt
no changes added to commit (use "git add" and/or "git commit -a")
git status 명령을 이용해 확인해보면 현재 브랜치에서 f1.txt와 f2.txt가 수정되었다고 나타난다.
이제 이 두 개의 파일을 커밋하기 전에 다른 파일을 수정해야 한다고 가정해 보자. 커밋하지 않은 수정 내용을 어딘가에 보관하려면 git stash 명령을 사용한다.
$ git stash
# git stash save 라고 해도 된다.
dndkg@kei MINGW64 ~/st (master)
$ git stash
warning: in the working copy of 'f1.txt', LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of 'f2.txt', LF will be replaced by CRLF the next time Git touches it
Saved working directory and index state WIP on master: b7211c7 f2
dndkg@kei MINGW64 ~/st (master)
$ git status
On branch master
nothing to commit, working tree clean
git stash 명령을 사용하고 다시 git status를 통해 확인해 보면 modified 메시지가 사라진 것을 확인할 수 있다. (실제로 txt 파일이 사라진 것은 아니다.)
같은 방법으로 여러 파일들을 수정한 후 따로 보관할 수 있으며, 감춰진 파일의 목록은 git stash list 명령을 통해서 확인할 수 있다.
$ git stash list
stash@{0}: WIP on master: b7211c7 f2
지금은 stash를 통해 감춘 파일이 stash@{0}에 들어있지만, 새로운 파일을 또 다시 감추게 되면 새로 감춘 파일이 stash{0}에 들어가게 되고 먼저 감춘 파일은 stash@{1}로 옮겨져 담기게 된다. 이러한 특성 때문에 stash stack이라고도 표현한다.
*stack은 first in, last out 방식의 저장 공간을 가리킨다.
$ git stash pop
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: f1.txt
modified: f2.txt
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (52758d1cd78b16d2126bd8d979a7e3c57b87af10)
pop을 추가하면 stash stack에서 가장 최근 항목을 되돌려준다.
※ stash apply와 stash drop
- stash 목록에 저장된 수정 내용을 나중에 또 사용할지도 모른다면 git stash apply 명령을 사용한다. 이 명령을 사용하면 stash 목록에서 가장 최근 항목을 되돌려주고, stash에 저장했던 내용을 삭제하지 않고 그대로 남겨둔다.
-git stash drop 명령은 stash 목록에서 가장 최근 항목을 삭제한다.
책에서 Git의 기본을 다루며 로컬 저장소에서 노는 내용은 이쯤에서 마무리되고, 다음 챕터부터는 GitHub를 사용하여 원격 저장소에 대한 이야기들을 하게 된다.
'Git' 카테고리의 다른 글
[Do it! Git&GitHub] Ch.04 깃허브로 백업하기 (0) | 2022.07.19 |
---|---|
[Do it! Git&GitHub] Ch.02 깃으로 버전 관리하기 (0) | 2022.07.12 |
[Do it! Git&GitHub] Ch.01 깃 시작하기 (0) | 2022.07.10 |