
Git은 데이터를 Change Set이나 변경사항으로 기록하지 않고 일련의 스냅샷으로 기록한다.
커밋하면 Git은 현재 Staging Area에 있는 데이터의 스냅샷에 대한 포인터, 저자나 커밋 메시지 같은 메타데이터, 이전 커밋에 대한 포인터 등을 포함하는 커밋 객체를 저장
이전 커밋 포인터가 존재하기 때문에 현재 커밋이 무엇을 기준으로 바뀌었는지 알 수 있다.
🎲 파일이 3개 있는 디렉토리를 커밋하려고 한다.
커밋하면 먼저 루트 디렉토리와 각 하위 디렉토리의 트리 객체를 체크섬과 함께 저장소에 저장
그 다음에 커밋 객체를 만들고 메타데이터와 루트 디렉토리 트리 객체를 가리키는 포인터 정보를 커밋 객체에 넣어 저장
[커밋과 트리 데이터]

[커밋과 이전 커밋]

[브랜치와 커밋 히스토리]

$ git branch <브랜치명>
[한 커밋 히스토리를 가리키는 두 브랜치]

Git은 HEAD라는 지금 작업하는 로컬 브랜치를 가리키는 특수한 포인터가 있다.[현재 작업 중인 브랜치를 가리키는 HEAD]

git log --decorate 를 사용하면 브랜치가 어떤 커밋을 가리키는지 확인 가능$ git log --oneline --decorate
git checkout 으로 다른 브랜치로 이동할 수 있다.$ git checkout <브랜치명>
▶️ HEAD는 지정한 브랜치를 가리킨다.
커밋을 새로 하면 HEAD가 가리키고 있는 브랜치는 새로운 커밋을 가리킨다.
이전 커밋을 가리키고 있는 HEAD가 가리키는 브랜치에서 파일을 수정한 후 커밋하면 기존의 커밋 객체와 합쳐지는 것이 아니라 별도의 커밋 객체가 생긴다.
Merge🎲 지금 작업하는 프로젝트에서 이전에 master 브랜치에 커밋을 몇 번 했다고 가정
[현재 커밋 히스토리]

이슈 관리 시스템에 등록된 53번 이슈를 처리하려고 한다.
▶️ 이 이슈에 집중할 수 있는 브랜치를 새로 하나 만든다.
git checkout -b 명령은 브랜치를 만들면서 Checkout까지 한번에 처리
$ git checkout -b <브랜치이름>
# 예시
$ git checkout -b iss53
[브랜치 포인터를 새로 만듦]

▶️ iss53 브랜치를 Checkout 했기 때문에 작업을 하고 커밋하면 iss53 브랜치가 앞으로 나아간다.
[진행 중인 iss53 브랜치]

🎲 만들고 있는 사이트가 문제가 생겨서 즉시 고쳐야 한다.
버그를 해결한 Hotfix에서 iss53이 섞이는 것을 방지하기 위해 iss53과 관련된 코드를 어딘가에 저장해두고 원래 운영 소스로 복구해야 한다.
▶️ Git을 사용하면 그냥 master 브랜치로 돌아가면 된다.
하지만, 아직 커밋하지 않은 파일이 Checkout할 브랜치와 충돌 나면 브랜치를 변경할 수 없다.
작업하던 것을 모두 커밋하고 master 브랜치로 옮긴다.
▶️ 이때 워킹 디렉토리는 53번 이슈를 시작하기 이전 모습으로 되돌려지기 때문에 새로운 문제에 집중할 수 있는 환경이 만들어진다.
해결해야 할 Hotfix가 생겼을 때 Hotfix 브랜치를 만들고 새로운 이슈를 해결할 때까지 사용한다.
[master 브랜치에서 갈라져 나온 hotfix 브랜치]

$ git merge hotfix
Merge 할 때 B 브랜치가 A 브랜치 이후의 커밋을 가리키고 있으면 A 브랜치가 B 브랜치와 동일한 커밋을 가리키도록 이동Fast forward🎲 hotfix는 master 브랜치에 포함됐고 운영 환경에 적용할 수 있는 상태가 되었다고 가정
[Merge 후 hotfix와 같은 것을 가리키는 master 브랜치]

급한 문제를 해결하고 master 브랜치에 적용하고 나면 다시 일하던 브랜치로 돌아가야 한다.
더 이상 필요없는 hotfix 브랜치 삭제
$ git branch -d hotfix
[master와 별개로 진행하는 iss53 브랜치]

🎲 53번 이슈를 다 구현하고 master 브랜치에 Merge하려고 한다.
git merge 명령으로 합칠 브랜치에서 합쳐질 브랜치를 Merge$ git checkout master
$ git merge iss53
Merge할 브랜치의 조상이 아니므로 Fast forward로 Merge 하지 않는다.3-way Merge를 한다.[커밋 3개를 Merge]

Merge의 결과를 별도의 커밋으로 만들고 해당 브랜치가 그 커밋을 가리키도록 이동[Merge 커밋]

master에 Merge 하고 나면 더는 iss53 브랜치가 필요 없기 때문에 브랜치를 삭제하고 이슈 상태를 처리 완료로 표시Merge하는 두 브랜치에서 같은 파일의 한 부분을 동시에 수정하고 Merge하면 Git은 해당 부분을 Merge 하지 못한다.
Merge 충돌이 일어났을 때 Git이 어떤 파일을 Merge할 수 없었는지 살펴보려면 git status 명령 이용
▶️ 충돌이 일어난 파일은 unmerged 상태로 표시
충돌을 해결하려면 둘의 내용 중에서 고르거나 새로 작성하여 Merge 한다.
git mergetool이라는 Merge 도구로 충돌 해결 가능
Merge 도구를 종료하면 Git은 잘 Merge 했는지 물어본다.
▶️ 잘 마쳤다고 입력하면 자동으로 git add가 수행되고 해당 파일이 Staging Area에 저장
▶️ 잘 저장됐는지 확인했으면 git commit 으로 Merge 한 것을 커밋
git branch를 실행하면 브랜치의 목록을 보여준다.$ git branch
iss53
* master
testing
▶️ * 기호가 붙어 있는 master 브랜치는 현재 Checkout 해서 작업하는 브랜치를 나타낸다.
git branch -v 를 실행하면 브랜치마다 마지막 커밋 메시지도 함께 보여준다.$ git branch -v
iss53 93b412c fix javascript issue
* master 7a98805 Merge branch 'iss53'
testing 782fd34 add scott to the author list in the readmes
git branch --merged로 이미 Merge한 브랜치 목록을 확인한다.$ git branch --merged
iss53
* master
▶️ * 기호가 붙어 있지 않은 브랜치는 git branch -d 로 삭제해도 되는 브랜치다.
▶️ 이미 다른 브랜치와 Merge 했기 때문에 삭제해도 정보를 잃지 않는다.
Merge 하지 않은 브랜치를 살펴보려면 git branch --no-merged 사용$ git branch --no-merged
testing
▶️ 아직 Merge 하지 않은 커밋을 담고 있기 때문에 git branch -d로 삭제되지 않는다.
Merge 하지 않은 브랜치를 강제로 삭제하려면 -D 옵션으로 삭제master 브랜치에 Merge 해서 안정 버전의 코드만 master 브랜치에 둔다.develop나 next라는 이름으로 추가로 만들어 사용한다.Merge 한다.[안정적인 브랜치일수록 커밋 히스토리가 뒤쳐짐]

[각 브랜치를 하나의 "실험실"로 생각]

proposed 혹은 pu(proposed updates)라는 이름의 브랜치를 만들고 next나 master 브랜치에 아직 Merge 할 준비가 되지 않은 것을 일단 Merge 시킨다.🎲 master 브랜치를 checkout 한 상태에서 어떤 작업을 한다고 가정
master 브랜치로 되돌아가서 dumbidea 브랜치를 하나 더 만든다.[토픽 브랜치가 많음]

🎲 iss91v2 브랜치가 괜찮아서 적용하기로 결정했다.
dumbidea 브랜치도 괜찮아 보인다.
Merge한다.[dumbidea 와 iss91v2 브랜치를 Merge 하고 난 후의 모습]

리모트 트래킹 브랜치 : 리모트 브랜치를 추적하는 레퍼런스이며 브랜치
▶️ 로컬에 있지만 임의로 움직일 수 없다.
▶️ 리모트 서버에 연결할 때마다 리모트의 브랜치 업데이트 내용에 따라서 자동으로 갱신된다.
▶️ 리모트 저장소에 마지막으로 연결했던 순간에 브랜치가 무슨 커밋을 가리키고 있었는지를 나타낸다.
리모트 트래킹 브랜치의 이름은 <리모트저장소명>/<브랜치이름> 형식
🎲 git.ourcompany.com 이라는 Git 서버가 있고 이 서버의 저장소를 하나 clone 하면 Git은 자동으로 origin 이라는 이름을 붙인다.
origin 으로부터 저장소 데이터를 모두 내려받고 master 브랜치를 가리키는 포인터를 만든다.master 브랜치가 origin/master 를 가리키게 한다.master 브랜치에서 작업을 시작할 수 있다.[Clone 이후 서버와 로컬의 master 브랜치]

git.ourcompany.com 서버에 push 하고 master 브랜치를 업데이트한다.origin/master 포인터는 그대로다.[로컬과 서버의 커밋 히스토리는 독립적임]

git fetch origin 명령을 사용한다.origin 서버의 주소 정보(이 예에서는 git.ourcompany.com)를 찾아서, 현재 로컬의 저장소가 갖고 있지 않은 새로운 정보가 있으면 모두 내려받고, 받은 데이터를 로컬 저장소에 업데이트하고 나서, origin/master 포인터의 위치를 최신 커밋으로 이동시킨다.[git fetch 명령은 리모트 브랜치 정보를 업데이트]

🎲 리모트 저장소를 여러 개 운영하는 상황을 이해할 수 있도록 개발용으로 사용할 Git 저장소를 팀 내부에 하나 추가해보자.
git.team1.ourcompany.com 이며 git remote add으로 현재 작업 중인 프로젝트에 팀의 저장소를 추가한다.[서버를 리모트 저장소로 추가]

git fetch teamone으로 teamone 서버의 데이터를 내려받는다.origin 서버에도 있는 것들이라서 아무것도 내려받지 않는다.teamone/master가 teamone 서버의 master 브랜치가 가리키는 커밋을 가리키게 한다.[teamone/master의 리모트 트래킹 브랜치]

push 해야 한다.🎲 serverfix 라는 브랜치를 다른 사람과 공유할 때도 브랜치를 처음 push하는 것과 같은 방법으로 push
$ git push origin serverfix
Counting objects: 24, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (24/24), 1.91 KiB | 0 bytes/s, done.
Total 24 (delta 2), reused 0 (delta 0)
To https://github.com/schacon/simplegit
* [new branch] serverfix -> serverfix
fetch하고 나서 서버에 있는 serverfix 브랜치에 접근할 때 origin/serverfix라는 이름으로 접근할 수 있다.$ git fetch origin
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://github.com/schacon/simplegit
* [new branch] serverfix -> origin/serverfix
fetch 명령으로 리모트 트래킹 브랜치를 내려받는다고 해서 로컬 저장소에 수정할 수 있는 브랜치가 새로 생기는 것이 아니다.origin/serverfix 브랜치 포인터가 생기는 것Merge하려면 git merge origin/serverfix 사용Merge 하지 않고 리모트 트래킹 브랜치에서 시작하는 새 브랜치를 만들려면 아래와 같은 명령 사용$ git checkout -b serverfix origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'
▶️ 그러면 origin/serverfix에서 시작하고 수정할 수 있는 serverfix 로컬 브랜치가 만들어진다.
리모트 트래킹 브랜치를 로컬 브랜치로 Checkout 하면 자동으로 트래킹 브랜치가 만들어진다.
▶️ 트래킹 하는 대상 브랜치를 Upstream 브랜치라고 부른다.
트래킹 브랜치에서 git pull를 하면 리모트 저장소로부터 데이터를 내려받아 연결된 리모트 브랜치와 자동으로 Merge 한다.
서버로부터 저장소를 clone하면 Git은 자동으로 master 브랜치를 origin/master 브랜치의 트래킹 브랜치로 만든다.
트래킹 브랜치를 직접 만들 수 있는데 리모트를 origin이 아닌 다른 리모트로 할 수 도 있고, 브랜치도 master가 아닌 다른 브랜치로 추적하게 할 수 있다.
git checkout -b <branch> <remote>/<branch>로 간단히 트래킹 브랜치를 만들 수 있다.
▶️ --track 옵션을 사용하여 로컬 브랜치 이름을 자동으로 생성할 수 있다.
$ git checkout --track origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'
$ git checkout serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'
$ git checkout -b sf origin/serverfix
Branch sf set up to track remote branch serverfix from origin.
Switched to a new branch 'sf'
git branch에 -u나 --set-upstream-to 옵션을 붙여서 설정$ git branch -u origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
git branch -vv 사용$ git branch -vv
iss53 7e424c3 [origin/iss53: ahead 2] forgot the brackets
master 1ae2a45 [origin/master] deploying index fix
* serverfix f8674d9 [teamone/server-fix-good: ahead 3, behind 1] this should do it
testing 5ea463a trying something new
fetch) 시점을 바탕으로 계산한다는 점이다.git pull 은 대부분 git fetch 를 실행하고 나서 자동으로 git merge 를 수행하는 것 뿐이다.fetch와 merge 로 명시적으로 사용하는 것이 좋다.git push --delete를 사용하여 리모트 브랜치를 삭제$ git push origin --delete serverfix
To https://github.com/schacon/simplegit
- [deleted] serverfix
[두 개의 브랜치로 나누어진 커밋 히스토리]

merge를 사용하는 것이지만 다른 방식으로 rebase가 있다.$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
Rebase할 브랜치가 합칠 브랜치가 가리키는 커밋을 가리키게 하고 아까 저장해 놓았던 변경사항을 차례대로 적용[C4의 변경사항을 C3에 적용하는 Rebase 과정]

master 브랜치를 Fast-forward 시킨다.$ git checkout master
$ git merge experiment
[master 브랜치를 Fast-forward시키기]

Merge와 Rebase 둘 다 합치는 관점에서는 서로 다를게 없지만 Rebase가 좀 더 깨끗한 히스토리를 만든다.Rebase하는 리모트 브랜치는 직접 관리하는 것이 아니라 그냥 참여하는 브랜치일 것이다.🎲 다른 토픽 브랜치에서 갈라져 나온 토픽 브랜치 같은 히스토리가 있다고 가정
[다른 토픽 브랜치에서 갈라져 나온 토픽 브랜치]

🎲 이 때 테스트가 덜 된 server 브랜치는 그대로 두고 client 브랜치만 master로 합치려는 상황
$ git rebase --onto master server client
master 브랜치로부터 server 브랜치와 client 브랜치의 공통 조상까지의 커밋을 client 브랜치에서 없애고 싶을 때 사용master 브랜치에서 client 브랜치를 기반으로 새로 만들어 적용[다른 토픽 브랜치에서 갈라져 나온 토픽 브랜치를 Rebase 하기]

master 브랜치로 돌아가서 Fast-forward 시킬 수 있다.$ git checkout master
$ git merge client
[master 브랜치를 client 브랜치 위치로 진행 시키기]

git rebase <basebranch> <topicbranch>로 checkout 하지 않고 바로 server 브랜치를 master 브랜치로 Rebase 할 수 있다.$ git rebase master server
[master 브랜치에 server 브랜치의 수정 사항을 적용]

master 브랜치를 Fast-forward 시킨다.$ git checkout master
$ git merge server
master 브랜치에 통합됐기 때문에 더 필요하지 않으면 client 나 server 브랜치는 삭제해도 된다.$ git branch -d client
$ git branch -d server
[최종 커밋 히스토리]

🎲 새 커밋을 서버에 Push 하고 동료 중 누군가가 그 커밋을 Pull 해서 작업을 한다고 가정
git rebase로 바꿔서 Push 해버리면 동료가 다시 Push 했을 때는 동료는 다시 Merge 해야 한다.Merge한 내용을 Pull하면 내 코드는 엉망이 된다.[저장소를 Clone 하고 일부 수정함]

Merge 하고 나서 서버에서 PushFetch, Merge 한다.[Fetch 한 후 Merge 함]

Push했던 팀원은 Merge 한 일을 되돌리고 다시 Rebase한다.Fetch하고 나면 아래와 같은 상태가 된다.[한 팀원이 다른 팀원이 의존하는 커밋을 없애고 Rebase 한 커밋을 다시 Push 함]

git pull로 서버의 내용을 가져와서 Merge하면 같은 내용의 수정사항을 포함한 Merge 커밋이 아래와 같이 만들어진다.[같은 Merge를 다시 한다]

git log로 히스토리를 확인해보면 같은 커밋이 두 개 있다. (C4, C4')Rebase로 커밋을 정리했어야 했다.🎲 어떤 팀원이 강제로 내가 한 일을 덮어썼다고 가정
[한 팀원이 다른 팀원이 의존하는 커밋을 없애고 Rebase 한 커밋을 다시 Push 함]

Merge 하는 대신 git rebase teamone/master를 실행하면 Git은 아래와 같은 작업을 한다.Merge 커밋이 아닌 것을 결정Merge할 브랜치에 덮어쓰이지 않은 커밋 결정teamone/master 브랜치에 적용[강제로 덮어쓴 브랜치에 Rebase 하기]

동료가 생성했던 C4와 C4' 커밋 내용이 완전히 같은 때만 이렇게 동작된다.
▶️ 커밋 내용이 아예 다르거나 비슷하다면 커밋이 두개 생긴다.
git pull --rebase로 Rebase 할 수도 있다.
Push 하기 전에 정리하려고 Rebase 하는 것과 절대 공개하지 않고 혼자 Rebase 하는 경우도 괜찮다.
▶️ 하지만, 이미 공개하여 사람들이 사용하는 커밋을 Rebase하면 틀림없이 문제가 생긴다.
Rebase 할 수도 있지만, 리모트 등 어딘가에 Push로 내보낸 커밋에 대해서는 절대 Rebase 하지 말아야 한다.