Git command 정리
Branching and Merging
- fast-forward merge
- no-ff
- three-way merge
- conflict 해결
Stashing
- stash
Rebasing
- rebase
- cherry-pick
변경사항 취소하기
- checkout
- restore
- reset
- revert
- reflog
Fetching and Pulling
- fetch
- pull
Git 협업 워크플로우
- fork
- clone
fast-forward merge

master에서 분기한 feature 브랜치에서 X, Y 커밋이 생성되고, master 브랜치에서는 분기된 시점 이후로 더 이상 추가적인 수정 사항이 없는 경우입니다.
feature의 커밋을 master 브랜치에 merge 하고자 하는 경우 fast-forward merge를 사용할 수 있습니다.
$ git switch master
$ git merge feature
feature 브랜치의 수정 사항은 master 브랜치의 이력을 포함하고 있기 때문에 별다른 충돌이 발생하지 않아 손쉽게 병합할 수 있습니다. 이 경우 master 브랜치에는 별다른 merge 히스토리가 남지 않으며, master가 feature의 히스토리를 따라 잡아 HEAD는 Y 커밋을 가리키게 됩니다.
no-ff

fast-forward merge와는 다르게 master에서 feature 브랜치가 분기한 후, master 브랜치에서 추가적인 수정 사항이 발생한 경우입니다. 이 때는 단순히 master 브랜치를 feature 브랜치에 포함시켜 merge 할 수 없으며, 새로운 merge commit E를 생성합니다.
merge 이력이 남기 때문에 master 브랜치가 어떤 브랜치로부터 merge 되었는지를 확인할 수 있습니다.
또한 fast-forward merge가 가능한 경우라도 no-ff 옵션을 지정하여 새로운 merge commit을 생성할 수도 있습니다.
$ git switch master
$ git merge --no-ff feature
non fast-forward merge를 실행하면 브랜치가 그대로 남기 때문에 브랜치의 작업 내역과 병합 내역을 관리하는데 더 유용할 수 있습니다.
three-way merge
three-way merge는 git이 병합을 처리하는 방법입니다. non fast-forward 병합에서 살펴보았듯이 병합하고자 하는 두 개의 브랜치가 각각 master에서 분기되어 fast-forward merge를 할 수 없는 경우 git은 three-way merge를 수행합니다.
병합하고자 하는 두 브랜치의 스냅샷과 두 브랜치의 공통 조상의 스냅샷을 비교하여 새로운 커밋을 만들어 병합을 진행합니다.
만약 마지막 두 개의 브랜치만 비교한다고 가정해봅시다.
아래 나의 브랜치와 다른 브랜치에서 공통으로 존재하는 b 코드에 대해서만 원본이 b라는 것을 알 수 있지만, 나머지 변경 사항 a, c, d는 a가 원본인지 a'가 원본인지를 알 수 없습니다. 그래서 이 코드의 충돌 여부도 판단할 수 없습니다.
하지만 두 브랜치의 공통 조상인 base 브랜치와 함께 비교한다면, a의 경우 a'가 원본으로부터 변경된 브랜치라는 것을 알 수 있고 c의 경우 공통 코드를 두 브랜치 모두 수정했기에 충돌이 발생할 것이라고 판단할 수 있습니다. 3개의 스냅샷 비교를 통해서 merge commit 판단을 보다 명확하게 결정할 수 있습니다.
conflict 해결
두 브랜치를 병합하는 과정에서 동일한 코드를 수정한 커밋이 존재하는 경우 충돌이 발생합니다.
$ git merge feature
Auto-merging myfile.txt
CONFLICT (content): Merge conflict in myfile.txt
Automatic merge failed; fix conflicts and then commit the result.
def hello
<<<<<<< HEAD
puts 'hola world'
=======
puts 'hello mundo'
>>>>>>> feature
end
이 때 직접 충돌을 해결하고 난 뒤 다시 병합을 진행하거나, 병합을 취소할 수도 있습니다.
$ git merge --abort
Git - 고급 Merge
Merge 작업할 때 공백 처리 옵션을 사용하면 Git이 꽤 잘해준다. 하지만, Git이 자동으로 해결하지 못하는 때도 있다. 이럴 때는 외부 도구의 도움을 받아 해결한다. 예를 들어 Git이 자동으로 해결해
git-scm.com
stash
master에서 분기된 feature 브랜치에서 작업을 하다가 다시 master 브랜치로 switch 하는 경우, feature 브랜치에서 변경 사항을 commit 하지 않았다면 master 브랜치로 변경 사항이 따라옵니다.
하지만 feature의 변경 사항이 master 브랜치와 충돌하는 경우에는 switch에 실패합니다.
이 때, feature의 변경 사항은 아직 commit하고 싶지 않지만 master 브랜치로 switch하여 작업을 진행해야 한다면 stash 를 사용해 변경 사항들을 잠시 저장해 놓을 수 있습니다.
$ git status
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: index.html
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: lib/simplegit.rb
working directory와 staging area에 있는 변경 사항을 stash 로 저장합니다.
$ git stash
Saved working directory and index state \
"WIP on master: 049d078 added the index file"
HEAD is now at 049d078 added the index file
(To restore them type "git stash apply")
이제 working directory가 깨끗해졌으므로 다른 브랜치로 switch 할 수 있고, stash된 변경 사항을 꺼내 현재 브랜치에 다시 적용할 수도 있습니다. stash list로 저장한 목록을 확인합니다.
$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash apply로 현재 브랜치에 저장해두었던 변경 사항을 적용합니다.
$ git stash apply
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: index.html
modified: lib/simplegit.rb
no changes added to commit (use "git add" and/or "git commit -a")
apply는 staged 상태였던 파일을 자동으로 stage 해주지 않으므로, --index 옵션을 사용해 staged 상태를 그대로 적용할 수도 있습니다.
$ git stash apply --index
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: index.html
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: lib/simplegit.rb
apply는 브랜치를 적용할 뿐 stash 스택에서 제거하지는 않으므로, 브랜치 적용 후 스택 삭제를 바로 하기 위해서는 pop을 사용합니다.
$ git stash pop
Git - Stashing과 Cleaning
작업하던 저장소가 완전 지저분해져서 Git에게 진짜로 강제로 정리하도록 해야 하는 경우가 생길 수 있다. 예를 들어 Git 버전관리 데이터가 포함된 디렉토리를 복사해왔거나 서브모듈 디렉토리
git-scm.com
rebase
rebase는 두 가지 기능을 가지고 있습니다.
merge를 대신하여 사용할 수 있는 병합 기능과 git history를 정리하고 삭제할 수 있는 기능입니다.
먼저 merge 대신 사용할 수 있는 병합 기능을 살펴보겠습니다.
아래 그림과 같이 여러 명이 협업하는 프로젝트에서 master 브랜치로부터 feature 브랜치를 따서 작업하는 중이었다고 가정해봅시다. 이 때 버그 픽스 등으로 인해 master에 새로운 커밋 C가 추가되었고, feature 브랜치에서도 해당 커밋의 코드가 필요한 상황입니다.

feature 브랜치에서 C 커밋을 포함시키기 위해 merge를 수행한다면 아래와 같이 새로운 merge commit이 발생할 것입니다.
만약 이 프로젝트가 규모가 상당히 크고 여러 사람이 협업하며 master 브랜치에 계속 새로운 커밋을 추가하고 있는 상황이라면, merge를 진행했을 시 쓸데없는 merge commit이 여러 개 존재하여 전체적인 커밋 히스토리를 파악하는데 어려움이 있을 것입니다.
이런 상황에서 merge를 대신하여 사용할 수 있는 명령어가 rebase입니다.
rebase는 feature 브랜치 전부를 master 브랜치의 제일 최신 커밋으로부터 시작될 수 있도록 베이스를 새로 설정해줄 수 있습니다. 위 예시와 같은 상황에서 merge 대신 rebase를 진행하면 아래 그림과 같이 됩니다.
쓸데없는 merge commit Z가 사라졌고 feature 브랜치는 master의 최신 커밋인 C로부터 시작하여 linear한 히스토리를 갖게 되었습니다. 추후 master 브랜치에 또 다른 커밋이 발생한다고 해도 rebase를 통해 깔끔하게 히스토리를 관리할 수 있고 중요한 내용에 집중할 수 있게 됩니다.
실행은 rebase를 진행하고자 하는 브랜치에서 진행합니다.
$ git switch feature
$ git rebase master
confilct가 발생한다면 rebase를 취소하거나 editor를 통해 conflict 해결 후 rebase를 계속 진행할 수도 있습니다.
혹은 rebase 진행 중 conflict가 발생한다면 어느 쪽 코드를 적용할 것인지 옵션을 통해 설정할 수도 있습니다.
$ git switch feature
$ git rebase -X ours(theirs) master
하지만 어느 쪽을 나타내는 ours와 theirs 옵션은 rebase와 merge에서 서로 반대의 의미를 가집니다.
ours | theirs | |
merge | feature | master |
rebase | master | feature |
이유는 rebase 명령어를 실행했을 때 HEAD를 master로 재설정하기 때문입니다. master가 HEAD로 설정되고 난 후 master의 최신 커밋으로부터 feature의 커밋 히스토리를 재생성하기 때문에 ours는 master가 되고 theirs는 feature를 가리키게 됩니다.
다음으로는 rebase의 두 번째 기능인 git history 정리 기능을 알아보겠습니다.
-i 옵션을 통해 interactive rebase를 사용할 수 있습니다.
아래의 커밋 히스토리 예시를 살펴보겠습니다. 맨 위가 가장 최신 커밋입니다.
2029e20 my cat made this commit
655204d fix another navbar typo
2a45e71 fix navbar typos
4ff2290 add top navbar
6e39a76 whoops forgot to add bootstrap js script
240827f add bootstrap
519aab6 add basic HTML boilerplate
cbee26b I added project files
0e19c7a initial commit
위의 히스토리 중 수정하고 싶은 것이 몇 가지 있습니다.
- 8번 라인 커밋을 다른 커밋 메세지 형태와 맞춰 현재형으로 변경하고 싶음
- 5번 라인에서 js script를 깜빡해서 추가한 커밋을 6번 라인 add bootstrap에 포함하고 싶음
- 2, 3번 라인 커밋은 단순 오타 수정 커밋이므로 4번 라인 add top navbar 커밋에 포함시키고 싶음
- 1번 라인 커밋은 잘못된 커밋으로 삭제하고 싶음
interactive rebase를 통해 커밋 히스토리를 수정할 수 있습니다. 되돌아갈 지점을 선택합니다.
$ git rebase -i HEAD~8
그러면 editor를 통해서 되돌아간 지점까지의 커밋 히스토리를 볼 수 있습니다.
pick cbee26b I added project files
pick 519aab6 add basic HTML boilerplate
pick 240827f add bootstrap
pick 6e39a76 whoops forgot to add bootstrap js script
pick 4ff2290 add top navbar
pick 2a45e71 fix navbar typos
pick 655204d fix another navbar typo
pick 2029e20 my cat made this commit
# Rebase 0e19c7a..3b23c17 onto 0e19c7a (8 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
commit hash 앞의 command를 수정하여 히스토리를 변경할 수 있습니다.
- pick : 해당 커밋을 그대로 사용함
- reword : 해당 커밋의 커밋 메세지 내용을 수정함
- squash : 커밋 내용을 앞의 커밋에 포함시킴고, 커밋 메세지도 같이 합침
- fixup : 커밋 내용을 이전 커밋에 포함시키고 커밋 메세지는 버림
- drop : 해당 커밋을 삭제함
따라서 아까 원했던 수정사항을 적용하기 위해서는,
- 8번 라인 커밋을 다른 커밋 메세지 형태와 맞춰 현재형으로 변경하고 싶음
- 5번 라인에서 js script를 깜빡해서 추가한 커밋을 6번 라인 add bootstrap에 포함하고 싶음
- 2, 3번 라인 커밋은 단순 오타 수정 커밋이므로 4번 라인 add top navbar 커밋에 포함시키고 싶음
- 1번 라인 커밋은 잘못된 커밋으로 삭제하고 싶음
아래처럼 command를 바꿔준 후 editor를 저장 후 종료하면 됩니다.
reword cbee26b I added project files
pick 519aab6 add basic HTML boilerplate
pick 240827f add bootstrap
fixup 6e39a76 whoops forgot to add bootstrap js script
pick 4ff2290 add top navbar
fixup 2a45e71 fix navbar typos
fixup 655204d fix another navbar typo
drop 2029e20 my cat made this commit
# Rebase 0e19c7a..3b23c17 onto 0e19c7a (8 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
rebase를 사용하면 안되는 경우
- 다른 사람들이 이미 커밋을 pull한 경우
- 이미 공유한 코드를 rebase하는 것은 금지
- 다른 사람들에게 없는 커밋만 rebase 해야 함
cherry-pick
cherry-pick은 다른 브랜치의 특정 커밋 내용만 현재의 작업 브랜치에 포함시키고 싶을 때 사용합니다.
featureB 브랜치에서 featureA 브랜치의 X 커밋 내용만 적용하고자 하는 경우입니다.
$ git switch featureB
$ git cherry-pick X
위의 예시처럼 한 개의 커밋만 적용할 수도 있고 커밋 해시를 나열하여 한 번에 여러 개의 커밋을 적용할 수도 있습니다.
Git - git-cherry-pick Documentation
git cherry-pick master Apply the change introduced by the commit at the tip of the master branch and create a new commit with this change. git cherry-pick ..master git cherry-pick ^HEAD master Apply the changes introduced by all commits that are ancestors
git-scm.com
checkout
checkout은 switch branch 외에도 restore working tree의 기능을 가지고 있습니다. checkout이 한 명령어에 여러 기능을 가지고 있기 때문에 checkout의 역할을 분리하기 위해 switch와 restore 명령어가 추가되었습니다.
변경사항을 취소하기 위한 checkout의 기능을 살펴보겠습니다.
D까지 커밋 후 작업을 하던 도중 B 커밋의 코드가 궁금하면 어떻게 봐야할까요? 이 때 checkout을 사용하면 특정 커밋의 코드 상태로 되돌아갈 수 있습니다.
$ git checkout HEAD~2
또는
$ git checkout {B 커밋 hash}
이 때 git은 HEAD가 분리되었다는 경고를 줍니다.
Note: switching to '618674e'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
HEAD라는 것은 특정 브랜치의 최신 커밋을 가리키는데 현재 checkout으로 인해 HEAD가 최신 브랜치 상태가 아닌 특정 커밋을 바라보고 있는 분리된 상태라는 안내입니다.
이 상태에서는 B 커밋을 베이스로 한 새로운 브랜치를 생성하거나, 아니면 B에서의 코드를 복사하여 최신 커밋에 적용하는 등의 작업을 할 수 있습니다.
checkout 시 working directory 및 staging area는 유지되지만, 커밋 내용은 checkout한 커밋을 가리키고 있습니다. checkout 또는 switch로 언제든지 최종 커밋한 코드 상태로 되돌아올 수 있습니다.
$ git checkout master
또는
$ git switch master
전체 코드를 되돌리는 것 뿐만 아니라 특정 파일만 이전 커밋 상태로 되돌릴 수도 있습니다.
$ git checkout HEAD~1 cat.txt
혹은 working directory의 모든 수정사항을 삭제하고 원래의 최신 커밋으로 돌아가고 싶을 때도 사용할 수 있습니다.
$ git checkout .
restore
checkout의 기능을 분담하기 위해 새로 만들어진 명령어입니다.
위에서 이미 설명한 것처럼 특정 파일을 원래 모습으로 복원하거나,
$ git restore cat.txt
이전 커밋의 상태로 복원할 수도 있습니다.
$ git restore --source HEAD~1 cat.txt
checkout에는 없는 restore의 또 다른 기능은, 스테이지된 파일을 언스테이지하는 것입니다.
git status를 확인하는 경우에 restore에 대한 안내를 자주 볼 수 있습니다.
git add 를 통해 staged 하였지만, commit 내용에 포함하고 싶지 않은 경우 unstaged 하기 위해 사용합니다.
$ git restore --staged cat.txt
reset
reset은 커밋을 취소하고 싶은 경우 특정 커밋으로 되돌아갈 수 있는 기능입니다.
예를 들어, file.txt 파일 하나를 수정하고 커밋하는 것을 세 번 반복하면, 히스토리는 아래 그림과 같을 것입니다.
HEAD는 브랜치의 마지막 커밋 스냅샷, Index는 다음에 커밋할 스냅샷, 즉 staging area입니다.
Reset 명령은 정해진 순서대로 세 개의 트리를 덮어써 나가다가 옵션에 따라 지정한 곳에서 멈춥니다.
- HEAD가 가리키는 브랜치를 옮긴다. (--soft 옵션이 붙으면 여기까지)
- Index를 HEAD가 가리키는 상태로 만든다. (--mixed 옵션, default)
- 워킹 디렉토리를 Index의 상태로 만든다. (--hard 옵션이 붙으면 여기까지)
- HEAD가 가리키는 브랜치를 옮긴다. (--soft 옵션이 붙으면 여기까지)
reset은 가장 먼저 HEAD가 가리키는 브랜치가 가리키는 커밋을 바꿉니다. 현재 master 브랜치에서 작업하고 있고, git reset 9e5e6a4 명령을 실행한다면 master 브랜치가 `9e5e6a4`를 가리키게 합니다.
2. Index를 HEAD가 가리키는 상태로 만든다. (--mixed 옵션, default)
reset의 default 옵션을 통해서 Index(Staging area)를 HEAD가 가리키는 스냅샷으로 업데이트 할 수 있습니다.
3. 워킹 디렉토리를 Index의 상태로 만든다. (--hard 옵션이 붙으면 여기까지)
--hard 옵션이 붙는 경우, working directory까지 HEAD가 가리키는 커밋으로 업데이트합니다. 이 때 working directory에서 작업하고 있던 내용은 모두 사라집니다. 커밋한 적이 있는 변경 사항은 reflog를 통해 되돌릴 수 있지만, 커밋을 한 적이 없다면 덮어씌워진 데이터는 복원할 수 없습니다.
특정 경로를 주어 일부 파일만 변경하는 것도 가능합니다.
예를 들어, git reset file.txt 명령을 실행한다고 가정하면 이는 git reset --mixed HEAD file.txt를 짧게 쓴 것입니다. 결과적으로 file.txt를 HEAD가 가리키는 최신 커밋 상태로 업데이트 합니다.
특정 커밋을 명시하면, HEAD가 아니라 특정 커밋에서 파일을 가져옵니다. git reset eb43bf file.txt 를 실행하면 default가 --mixed이므로 staging area까지만 eb43bf 커밋의 file.txt 로 업데이트 합니다.
checkout 명령어도 HEAD를 특정 커밋으로 이동시킬 수 있다고 위에서 설명했습니다.
그럼 checkout과 reset에는 어떤 차이가 있는것일까요?
첫번째로, reset --hard 명령과는 달리 checkout은 working directory를 안전하게 다룹니다. reset --hard 시 working directory의 작업 내용을 모두 업데이트하지만, checkout은 일단 merge 를 시도해본 뒤 변경하지 않은 파일만 업데이트 합니다.
두 번째 중요한 차이점은 어떻게 checkout 명령이 HEAD를 업데이트 하는가입니다. reset 명령은 HEAD가 가리키는 브랜치 자체를 움직이지만(브랜치 Refs를 업데이트하지만), checkout 명령은 HEAD 자체를 다른 브랜치(혹은 커밋)로 옮깁니다(detached HEAD).
아래에 어떤 명령이 어떤 트리에 영향을 주는지에 대한 요약표입니다. 명령이 HEAD가 가리키는 브랜치를 움직인다면 “HEAD” 열에 “REF” 라고 적혀 있고 HEAD 자체가 움직인다면 “HEAD” 라고 적혀 있습니다. 'WD Safe?'에 *NO*라고 적혀 있다면 워킹 디렉토리에 저장하지 않은 내용이 안전하지 않기 때문에 해당 명령을 실행하기 전에 한 번쯤 더 생각해보아야 합니다.
Git - Reset 명확히 알고 가기
지금까지 reset 명령을 실행하는 기본 형태와 사용 방법을 살펴봤다. reset 명령을 실행할 때 경로를 지정하면 1단계를 건너뛰고 정해진 경로의 파일에만 나머지 reset 단계를 적용한다. 이는 당연한
git-scm.com
revert
revert는 reset과 마찬가지로 커밋에서 변경 사항을 취소합니다.
하지만 reset이 커밋을 완전히 제거하고 마치 처음부터 없던 커밋인 것처럼 브랜치 포인터를 뒤로 이동시킨다면, revert는 변경 사항을 취소하는 새로운 커밋을 생성합니다.
코드에서 특정 변경 사항을 취소하고 싶지만, 취소한 커밋 내용을 히스토리로 남기고 싶은 경우 revert를 사용합니다.
revert는 내가 커밋한 코드를 다른 사람들이 이미 공유해 간 경우 사용합니다. 이미 공유된 커밋을 되돌리고자 하는 경우 revert를 통해서 취소한 커밋과 취소했다는 revert 히스토리를 남길 수 있습니다.
하지만 내 로컬에서만 작업한 내용을 취소하고자 하는 경우에는 reset을 사용하는 것이 더 깔끔한 history를 남길 수 있습니다.
$ git revert {commit hash}
reflog
Git은 브랜치의 끝에서, 혹은 특정 참조(HEAD)에서 변경사항이 일어난 것들을 모두 기록하고 파일로 저장해 둡니다.
Repository의 .git/logs/ 경로에서 기록을 확인할 수 있습니다.
reflog는 오직 내 로컬 레포지토리의 참조 목록 변경 사항만 기록합니다. 또한 이 기록은 90일 동안만 유지됩니다.
이 기록들을 통해서 혹여나 실수를 하거나 깃 로그에서 더 이상 볼 수 없게 된 커밋의 해시에 접근해야할 때 유용하게 사용할 수 있습니다.
아래 명령어를 통해서 특정 참조의 로그를 볼 수 있습니다.
# HEAD 기록 확인
$ git reflog show HEAD
# 브랜치 기록 확인
$ git reflog show {branch_name}
git log와 비슷하지만 다릅니다. checkout, commit, clone 등 브랜치에서 일어난 모든 변경 작업에 대한 기록을 볼 수 있습니다.
log를 확인할 때 name@{qualifier} 형태로 특정 log를 지정할 수 있습니다.
- 순서 참조
HEAD를 예로 들면 HEAD@{5} 형식으로 특정 로그를 가리킬 수 있습니다. 하지만 HEAD~2 와 HEAD@{2} 는 서로 다릅니다. HEAD~2는 조부모 커밋을 가리키지만, HEAD@{2}는 커밋이 아닐 수 있는 2번째 작업 전의 로그를 가리킵니다.
- 시간 및 날짜
feature@{one.week.ago} main@{2.days.ago} 등의 시간 참조를 통해 해당 시간 대의 로그를 특정할 수도 있습니다.
이러한 지정자를 통해서 내가 원하는 특정 로그의 코드 상태로 되돌아갈 수도 있습니다.
만약 reset을 통해서 브랜치의 참조 자체를 이전 커밋으로 변경했다고 가정해봅시다.
취소된 마지막 커밋 38eb946은 git log 히스토리에서도 찾아볼 수 없게 되고, 어떠한 커밋에서도 참조되지 않는 그냥 떠다니는 커밋이 됩니다.
만약 이 reset 작업이 잘못되었고, 38eb946 커밋으로 다시 되돌아가고 싶을 때는 Reflog를 사용하여 바로잡을 수 있습니다.
git reflog show {branch or ref} 를 통해서 log에서 사라졌던 reset 전의 38eb946 해시 값을 찾은 뒤,
git reset --hard 38eb946 를 통해 전체 코드를 해당 커밋 상태로 되돌릴 수 있습니다.
fetch
원격 respository로부터 코드를 clone 하여 내 로컬 repository에서 작업을 진행할 수 있습니다.
로컬에서 작업 중 원격 repo에 새로운 커밋이 추가된 경우, 내 로컬에 원격의 변경 사항을 가져오고자 할 때 fetch와 pull을 사용합니다.
fetch와 pull은 아래와 같은 차이점이 있습니다.
fetch는 깃허브에서 최신 정보를 가져오지만, 현재의 내 working directory에서 작업한 내용과 합치고 싶지는 않을 때 사용합니다.
pull은 깃허브에서 최신 정보를 가져오고, 현재의 내 working directory와 자동으로 merge 해주는 기능까지 포함합니다.
fetch를 통해 local repository에 원격 브랜치의 변경 사항을 가져올 수 있습니다.
$ git fetch <remote>
$ git fetch origin master
$ git checkout origin/master
checkout을 통해 내가 작업 중인 master를 망치지 않고 origin/master의 코드를 확인할 수 있습니다.
브랜치에 연결된 원격 저장소 확인은 아래 명령으로 할 수 있습니다.
$ git branch -vv
pull
pull도 원격 저장소에서 변경 사항을 가져오는데 사용됩니다. 가장 큰 차이점은 HEAD 브랜치를 fetch 후 현재의 working directory와 자동 Merge 하여 업데이트 한다는 것입니다.
$ git pull origin master
자동으로 merge 하므로 현재의 작업과 원격 저장소의 코드가 충돌할 수도 있습니다.
fork
github가 제공하는 기능으로서, 다른 사람의 레포지토리를 개인 공간에 복사할 수 있는 기능입니다. 이 복사본을 원본 저장소의 fork라고 부릅니다.
코드 기여자가 수천, 수만인 대형 오픈 소스 프로젝트 같은 프로젝트에서 사용할 수 있습니다. 수천 명의 기여자 전부를 프로젝트에 초대할 수는 없기 때문입니다.
오픈 소스 프로젝트를 내 개인 저장소에 forking 한 후, 내가 개선한 코드를 오픈 소스에 반영하고자 하면 원본 저장소에 pull request를 요청합니다.
clone
clone은 기존 로컬 또는 원격 레포지토리를 대상으로 복제본을 만들어 줍니다. 편의상 복제하면 원본 레포지토리를 가리키는 origin이라는 원격 연결을 자동으로 만듭니다.
$ git clone {git URL}
fork와 clone을 통해 프로젝트 담당자가 수천, 수만의 개발자들에게 레포지토리 접근 권한을 부여하지 않고도 함께 손쉽게 협업할 수 있습니다.