remote repository에 있는 code를 clone하면 다음과 같은 과정이 동작하게 되는 것이다.
master
|
v
commit1 ---> commit2 ---> commit3
위의 github repo를 clone하도록 하자. local에 다음과 같이 생기게 될 것이다.
master
|
v
commit1 ---> commit2 ---> commit3
^
|
origin/master
내 local repo로 clone해오면 master
branch가 생기는 것을 볼 수 있다. 이 master
branch가 remote repo의 master
branch이다. 그런데 origin/master
와 같은 branch도 추가된 것을 볼 수 있다. 이는 remote repo에는 볼 수 없던 것인데, local repo에만 생긴다. 이러한 branch를 일컫어 원격 추적 branch라고 한다.
원격 추적 branch는 remote에 있는 master branch의 상태에 대한 참조로, 마음대로 움직이게 할 수 없다. 이는 system에서 사용하는 것으로 origin에 있는 master branch에 대한 마지막 commit을 포인팅하기 위한 bookmark라고 생각하면 된다.
즉, 하나의 pointer로 처음 시작할 때는 master
branch와 동일한 commit을 가리키지만, 개발자가 local repo에서 master
branch로 commit을 추가하고 작업을 이으면, master
branch가 가리키는 HEAD commit은 앞으로 가는 것에 비해, origin/master
는 처음 clone하거나 fetch, pull한 시점의 commit만 포인팅한다.
원격 추적 브랜치는 origin/master
처럼 remote/branch
라는 이름 형식을 가진다. 현재 내 local repo의 원격 추적 branch가 무엇인지 알고 싶다면 git branch -r
을 사용하면 된다.
git branch -r
origin/HEAD -> origin/master
origin/master
origin/master
라는 원격 추적 branch를 볼 수 있다.
만약 local에서 master
branch를 두 번 commit하였다면, 다음과 같이 된다.
master
|
v
commit1 ---> commit2 ---> commit3 ---> commit4 ---> commit5
^
|
origin/master
원격 추적 branch인 origin/master
는 가만히 있는 것을 볼 수 있다.
이제 실제 실습을 해보면서 실제로 그렇게 동작하는 지 확인해보도록 하자. github에서 remote repository로 demo
를 만들도록 하자. 만들었다면 local에서 다음의 명령어를 통해 github repo에 code를 넣도록 하자.
echo "test" >> test.txt
git init
git add test.txt
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/colt/demo.git
git push -u origin main
이제 두 가지의 commit을 더 만들어보도록 하자.
echo "test2" >> ./test.txt
git add ./test.txt
git commit -m "second commit"
echo "test3" >> ./test.txt
git add ./test.txt
git commit -m "second commit"
이제 다음과 같은 상황이 된 것이다.
main
|
v
commit1 ---> commit2 ---> commit3
^
|
origin/main
원격 추적 branch인 origin/main
보다 local branch인 main
가 두 개의 commit을 더 앞서고 있는 상황이다. 이를 확인할 수 있는 방법으로 git status
를 입력해보도록 하자.
it status
On branch main
Your branch is ahead of 'origin/main' by 2 commits.
(use "git push" to publish your local commits)
2 commits
가 원격 추적 브랜치인 origin/main
보다 앞서고 있다는 것이다.
원격 추적 브랜치인 origin/main
도 브랜치이기 때문에 checkout
이 가능하다.
git checkout origin/main
text.txt
를 확인하면 test
밖에 없는 것을 볼 수 있다. 이는 두번째, 세번째 commit들이 없는 것을 볼 수 있다.
원격 추적 브랜치인 origin/main
은 아직도 첫번째 commit에만 있는 것이다. 원격 추적 브랜치는 remote repo의 HEAD를 따라간다. 따라서, git push
를 통해서 업데이트해주도록 하자.
git checkout main
git push origin main
git status
로 확인해보도록 하자.
git status
On branch main
Your branch is up to date with 'origin/main'.
원격 추적 브랜치가 local main
branch에 따라온 것을볼 수 있다.
원격 추적 branch는 clone할 당시에 github에 있는 branch들의 정보를 담고 있다고 했다. 이를 통해서 github에서 clone 시에 각종 branch로 이동해 작업을 진행할 수 있다.
먼저 test에 사용될 remote repo를 보도록 하자.
https://github.com/Colt/remote-branches-demo
여기에 들어가면 catus.txt
와 rose.txt
가 main
branch에 있는 것을 볼 수 있다.
해당 repo를 clone해보도록 하자.
git clone https://github.com/Colt/remote-branches-demo
clone 후에 현재 어떤 branch에 있는 지 확인해보도록 하자.
git branch
* main
remote repo로 돌아가서 github에 어떤 branch들이 있는 지 보도록 하자.
main
fantasy
food
more-fantasy
morefood
movies
6개의 branch들이 있는 것을 알 수 있다. 위에서 git branch
명령어로 확인했듯이, 해당 branch들은 내 local에 없는 것을 알 수 있다. 그런데, github에 있는 food
branch로 가서 apple.txt
라는 파일들 보고싶다면 어떻게해야할까??
이것을 가능하게 해주는 것이 바로 원격 추적 branch이다. 우리는 원격 추적 branch를 통해서 github에 있는 branch로 이동이 가능하고, 파일 수정, 추가가 가능한 것이다. 원격 추적 branch가 우리 local에 있는 지 확인해보도록 하자.
git branch --all
* main
remotes/origin/HEAD -> origin/main
remotes/origin/fantasy
remotes/origin/food
remotes/origin/main
remotes/origin/more-fantasy
remotes/origin/morefood
remotes/origin/movies
모든 원격 추적 branch들이 있는 것을 알 수 있다. 이 원격 추적 branch를 통해서 우리는 local의 branch로 접근이 가능한 것이다.
가령 main
branch는 내 local에 있는 개인 branch이지만, 이 branch는 원격 추적 branch로 remotes/origin/HEAD
를 따른다. remotes/origin/HEAD
는 또한 remotes/origin/main
을 따르고 이는 origin/main
을 의미한다. 따라서, 내 local의 main branch가 origin/main
을 따르듯이, origin/food
branch는 내 local의 food
branch를 따를 것이다.
그런데, 우리의 local에는 food
branch가 없다. food
branch가 없지만 git switch
를 통해서 food
branch를 만들고 이를 origin/food
branch를 따르도록 할 수 있다.
git switch food
Branch 'food' set up to track remote branch 'food' from 'origin'.
Switched to a new branch 'food'
이제 apple.txt
파일을 확인해보도록 하자.
cat ./apple.txt
___
_/`.-'`.
_ _/` . _.'
..:::::.(_) /` _.'_./
.oooooooooo\ \o/.-'__.'o.
.ooooooooo`._\_|_.'`oooooob.
.ooooooooooooooooooooo&&oooooob.
.oooooooooooooooooooo&@@@@@@oooob.
.ooooooooooooooooooooooo&&@@@@@ooob.
doooooooooooooooooooooooooo&@@@@ooob
doooooooooooooooooooooooooo&@@@oooob
dooooooooooooooooooooooooo&@@@ooooob
dooooooooooooooooooooooooo&@@oooooob
`dooooooooooooooooooooooooo&@ooooob'
`doooooooooooooooooooooooooooooob'
`doooooooooooooooooooooooooooob'
`doooooooooooooooooooooooooob'
`doooooooooooooooooooooooob'
`doooooooooooooooooooooob'
jgs `dooooooooobodoooooooob'
`doooooooob dooooooob'
`"""""""' `""""""'
food branch로 이동된 것을 볼 수 있다.
git status
로 현재 food
branch가 어떤 원격 추적 branch를 따라가고 있는 지 확인해보도록 하자.
git status
On branch food
Your branch is up to date with 'origin/food'.
nothing to commit, working tree clean
원격 추적 branch인 origin/food
를 따르는 것을 볼 수 있다.
이렇게 원격 추적 branch의 이름과 동일한 branch로 이동하면 local에서도 remote repo의 원격 추적 branch를 얻어낼 수 있는 것이다.
사실 git switch
가 나오기 이전에는 다음과 같은 명령어를 통해서 원격 추적 branch를 따랐다.
git checkout --track origin/food
해당 명령어는 git switch
에 비해서 불편하기 때문에 이제는 자주 사용되지 않는다. 물론 git checkout food
와 같이 입력만해도 원격 추적 branch를 찾아주고 checkout해주지만 git switch
를 더 사용하도록 하자.
github에 다음의 remote repo가 있다고 하자.
commit1 ---> commit2
해당 remote repo를 내 local로 clone하면 다음과 같이 된다.
master
|
v
commit1 ---> commit2
^
|
origin/master
local에서 작업을 좀 더 해보도록 하자.
master
|
v
commit1 ---> commit2 ---> local commit3
^
|
origin/master
origin/master
와 master
가 분리된 것을 볼 수 있다. 그런데, 만약 github에 있는 remote repo에서 다른 사람이 다음과 같이 commit을 했다고 하자.
commit1 ---> commit2 ---> commit3 ---> commit4 ---> commit5
이제 내 local의 원격 추적 bracnh인 origin/master
와 remote repo의 master와는 완전히 다른 길을 걷고 있다. 어떻게 해야할까?
이러한, 충돌 문제를 해결해주는 것이 바로 git fetch
와 git pull
이다.
(worksapce)
Working Directory | Stating Area |local Repository | remote Repository |
-------------------------------------------------------------------------------
git add ------> | git commit ---> | git push ---> | |
| | | <---- git fetch |
| | | |
<--------------------------git push------------------------------
git fetch
와 git pull
은 remote에서 local로 file을 업데이트하는데, git fetch
는 local repository에 올리는 대신에 git pull
은 바로 working directory로 올린다.
즉, git pull
시에는 remote repo의 변경 사항들이 바로 내 working directory에 반영이되어 code를 수정시키는 반면에 git fetch
는 local repository로 가서 내 local code들을 바로 수정시키지 않는다.
fetching
은 remote repo에서 changes(변화들)을 다운로드하지만, 자동으로 우리의 working directory file들에 merge되지 않는다. 이는 우리의 local code에 강제적으로 merge를 시키지 않고, 내 local repo에서 다른 이들이 개발한 변화 내용들을 볼 수 있도록 한다.
git fetch <remote>
git fetch
만 해도 보통 git fetch origin
과 동일하다.
branch를 지정해서 가져올 수도 있다.
git fetch <remote> <branch>
branch
를 생략하면 모든 원격 추적 branch들을 update한다.
이제 우리의 문제 상황을 보도록 하자.
commit1 ---> commit2 ---> commit3 ---> commit4 ---> commit5
master
|
v
commit1 ---> commit2 ---> local commit3
^
|
origin/master
여기서 git fetch
를 사용하면 내 local working directory에는 code 변화가 적용되지 않으므로, 다음과 같이 된다.
origin/master
|
v
commit3 ---> commit4 ---> commit5
/
/
commit1 ---> commit2 ---> local commit3
^
|
master
내 local commit3에는 영향이 없다. 왜냐하면 내 local working directory에 code를 부어놓지 않기 때문이다.
git fetch
를 사용하는 대표적인 두 가지 경우들이 있다.
1. git fetch
를 통해서 내 local에는 없는 remote repo의 branch 가져오기
2. git fetch
를 통해서 내 local에는 없는 commit 사항들 local repository로 가져오기 (단, working directory와 병합 X)
먼저 첫번째 경우를 알아보도록 하자. 실습을 위해서 새로운 github repo를 하나 만들어보도록 하자. 새로운 github repo를 만들었다면, local repo를 만들기 위해서 아래의 script를 입력하도록 하자.
echo "# demo" >> README.md
git init
git add README.md
git commit -m "commit1"
git branch -M main
git remote add origin https://github.com/colt/demo.git
git push -u origin main
첫번째 'commit1'이 만들어졌다. 먼저 github를 통해서 새로운 branch를 하나 만들어보도록 하자.
main
branch --> 'View all branches' --> 'New branch' --> 'test'
test
branch가 만들어졌으면 test
branch로 이동하고 github에서 README.md
파일을 수정하도록 하자. README.md
파일을 클릭한 다음 가운데 오른쪽에 있는 'Edit this file'을 클릭
# demo
# test
맨 아래에 있는 Commit changes
에 다음의 description을 추가하도록 하자. 'commit changes' 입력
완료가 되었다면, 이제 내 local pc에서 해당 branch와 commit 사항을 확인해보도록 하자. 현재의 상황을 정리하면 다음과 같다.
- github
main
|
v
commit1
\
----> commit2
^
|
test
-----------------------------------------------------------
- local repo
main
|
v
commit1
^
|
origin/main
위와 같은 상황이다. commit2
를 가진 test
branch를 얻어오기 위해서 git fetch
를 사용해보도록 하자.
이전에도 말했듯이 git fetch
를 사용하면 모든 원격 추적 branch들을 가져온다고 했다. 따라서, github에 있는 test
branch에 대한 원격 추적 branch를 가져오게 되는 것이다.
git fetch
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 354 bytes | 354.00 KiB/s, done.
From https://github.com/colt/demo
* [new branch] test -> origin/test
origin/test
를 잘 가져온 것을 볼 수 있다. 이제 test
branch로 이동해보도록 하자.
git switch test
Branch 'test' set up to track remote branch 'test' from 'origin'.
Switched to a new branch 'test'
성공적으로 test
branch로 이동하게 되었다. README.md
를 확인해보도록 하자.
cat ./README.md
# demo
# test
test
branch가 가진 commit이 적용되어 #test
가 README에 추가된 것을 볼 수 있다.
이렇게 git fetch
를 통해서 github에 있는 원격 추적 branch를 안전하게 가져올 수 있다. 안전하게 가져온다는 말은 내가 local working directory에 수정한 사항들에 대해서 변동없이 가져올 수 있다는 것이다.
현재의 local repo의 commit 사항을 정리하면 다음과 같이 된다.
- github
main
|
v
commit1
\
----> commit2
^
|
test
-----------------------------------------------------------
- local repo
main
|
v
commit1
^ \
| --------> commit2
| ^
origin/main |
origin/test
이제 git fetch
의 두 번째 사용 용도인 내 local에는 없는 commit 사항들을 가져오는 경우에 대해서 해보도록 하자.
먼저 local repo에서 main
branch로 이동시켜주도록 하자.
git switch main
Switched to branch 'main'
Your branch is up to date with 'origin/main'.
다음으로 새로운 commit인 'commit2'를 만들어보도록 하자.
echo "local commit2" >> ./README.md
git add ./README.md
git commit -m "local commit2"
이제 다음과 같이 된 것이다.
- local repo
main
|
v
commit1 -------> local commit2
^ \
| --------> commit2
| ^
origin/main |
origin/test
다음으로 github에 main
branch로 새로운 commit2를 추가해보도록 하자.
github repo 접속 -> README.md 클릭 -> 'Edit this file' 클릭 -> 'commit2' 추가
# demo
commit2
맨 아래의 'commit changes' -> 'commit2' 입력 -> 'Commit changes' 클릭
현재 아래와 같은 상황이 된 것이다.
- github
main
|
v
commit1 ---> commit2
\
----> commit2
^
|
test
-----------------------------------------------------------
- local repo
main
|
v
commit1 -------> local commit2
^ \
| --------> commit2
| ^
origin/main |
origin/test
local의 origin/main
과 원격지인 github
의 main
과 서로 다른 commit을 가리기키고 있다. 그런데, 내 local repo에서도 github의 commit2와는 다른 local commit2가 있다.
따라서, 무턱대고 git pull
을 해버리면 remote repo에 있는 commit2
가 local에 있는 code들을 건드릴 수 있다는 것이다.
git fetch
를 통해서 remote github에 있는 commit2
를 안전하게 local repository로 이동시키도록 하자.
git fetch
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
잘 가져와졌는 지를 확인하기 위해서 origin/main
으로 가보도록 하자.
git checkout origin/main
cat ./README.md
# demo
commit2
문제없이 fetch를 통해서 github에 있는 main
branch commit을 가져올 수 있었다.
정리하면 현재의 상황은 다음과 같은 것이다.
- github
main
|
v
commit1 ---> commit2
\
----> commit2
^
|
test
-----------------------------------------------------------
- local repo
origin/main
|
v main
----> commit2 |
/ v
commit1 -------------> local commit2
\
--------> commit2
^
|
origin/test
다시 local repo의 main
branch로 가서 README.md
가 github에 있는 commit2
영향을 받았는 지 아닌지 확인해보도록 하자.
git checkout main
cat ./README.md
# demo
local commit2
github에서의 commit2
가 local에 있는 local commit2
에는 영향을 미치지 않는 것을 볼 수 있다.
git pull
과 git fetch
는 모두 remote repo에 있는 code를 가져온다는 특징이 있지만, 가장 큰 차이점 중 하나는 git pull
의 경우 head branch를 업데이트한다는 것이다. 즉, working directory를 바꾼다는 것이다.
따라서, 다음과 같이 정리할 수 있다.
git pull = git fetch + git merge
원격 추적 branch를 git fetch
로 local repo에 가져온 다음에 git merge
를 통해, working directory에 연결하는 것이다.
사용 방법은 다음과 같다.
git pull <remote> <branch>
가령 git pull origin main
이라면 origin
에서 main
branch를 가져와서 내가 현재 있는 branch에 병합하는 것이다. 만약 main
이 아니라 demo
에 있었다면 demo
branch에 remote repo의 main
branch를 가져와 merge시키는 것이다.
github에 새로운 repository인 demo
를 만들고, 우리의 local에 다음의 명령어를 입력하도록 하자.
mkdir demo
cd demo
echo "# demo" >> README.md
git init
git add README.md
git commit -m "commit1"
git branch -M main
git remote add origin https://github.com/colt/demo.git
git push -u origin main
작업이 끝났다면, github에 새로운 commit을 추가해보도록 하자.
github repo 접속 -> README.md 클릭 -> 'Edit this file' 클릭 -> 'commit2' 추가
# demo
remote commit2
맨 아래의 'commit changes' -> 'commit2' 입력 -> 'Commit changes' 클릭
우리의 local에는 새로운 commit을 만들어 추가해보도록 하자.
echo "test" > ./test.txt
git add ./test.txt
git commit -m "local commit2"
두번째 commit2가 만들어진 것을 볼 수 있다.
현재까지의 상황을 정리한 것은 다음과 같다.
- github
main
|
v
commit1 ---> commit2
-----------------------------------------------------------
- local repo
main
|
v
commit1 ---> local commit2
^
|
origin/test
이제 git pull origin main
을 통해 github
에 있는 commit2
를 가져와보도록 하자.
git pull origin main
cat ./test.txt
test
git fetch
와 달리 local working directory에 test.txt
가 업데이트 된 것을 볼 수 있다. 이렇듯 git pull
의 경우 바로 local working directory에 code를 반영한다는 점이 있다.
그런데, git pull 시에 local working directory에 바로 반영된다면, 내가 만든 commit과 conflict(충돌)이 발생시킬 수도 있다.
다음의 사례를 확인해보자.
main
|
v
github: commit1 --> commit2 --> commit3
origin/main
|
v
local: commit1
^
|
main
다음과 같은 상황이 있다고 하자. git pull
을 하게되면 commit2
와 commit3
가 들어오게 된다. 이때 충돌이 발생하개되면, 충돌을 해결하여 그 변경 사항을 기록하는 새로운 commit
이 생기게된다.
main
|
v
github: commit1 --> commit2 --> commit3
| |
| |
| |
v v
local: commit1 --> commit2 --> commit3 --> commit4
^ ^
| |
origin/main main
가령 다음의 경우에서 commit2
와 commit3
이 들어왔을 때 merge에서 conflict가 발생했다면 conflict에 대한 수정이 필요하다. 이 수정 사항에 대해 반영된 결과가 바로 commit4
인 것이다.
이제 충돌 상황을 만들어보도록 하자. 먼저 github로 가서 'demo' repository를 만들자. 'demo' repository를 만들었다면, 다음의 code를 입력하여 local repo를 만들도록 하자.
mkdir ./demo
cd ./demo
echo "# demo" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/colt/demo.git
git push -u origin main
다음으로 local working directory에서 'commit2'를 만들어보도록 하자.
echo "local commit2" >> ./README.md
git add ./README.md
git commit -m "add local commit2"
github로 가서 README.md
를 수정해주도록 하자.
github repo 접속 -> README.md 클릭 -> 'Edit this file' 클릭 -> 'remote commit2' 추가
# demo
remote commit2
맨 아래의 'commit changes' -> 'remote commit2' 입력 -> 'Commit changes' 클릭
remote commit2
가 생성되었다면, 이제 git pull
을 통해서 local working directory로 가져와보도록 하자.
git pull origin main
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 348 bytes | 348.00 KiB/s, done.
From https://github.com/colt/demo
* branch main -> FETCH_HEAD
f238289..6db8d73 main -> origin/main
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.
다음과 같이 된다. 이는 automerge에 실패했다는 것을 나타낸다. README.md
로 가보도록 하자.
# demo
<<<<<<< HEAD
local commit2
=======
remote commit2
>>>>>>> 6db8d733ec7281265132df8af4fdf98a3af73fcd
둘 중 하나를 선택하거나, 둘 다 선택하거나, 둘 다 선택하지 않거나해야한다. 우리의 경우 둘 다 있도록 설정해주자.
# demo
local commit2
remote commit2
해당 merge에 대한 변경사항을 commit을 통해 반영해주어야 한다. 따라서, 다음의 명령어가 필요하다.
git add ./README.md
git commit -m "merge commit3"
merge commit을 만든 것이다. 해당 동작을 그려보면 다음과 같다.
main
|
v
github: commit1 --> remote commit2
|
|
|
v
local: commit1 --> local commit2 --> merge commit3
^ ^
| |
origin/main main
아주 이쁘게 merge commit3가 만들어진 것을 볼 수 있다.
git pull
의 동작을 정리하면 다음과 같다.
1. git fetch
로 remote repo의 변경 사항을 local repository로 가져오기 (local working directory에는 반영X)
2. git merge
로 local working directory와 local repository의 diff에 대해서 합병 시도
2-1. 충돌이 발생했다면 merge 작업을 수동으로 해준 뒤에 merge commit을 작성
2-2. commit을 remote repo에 push
3. 끝 (충돌이 없었다면 merge commit을 만들 지 않아도 된다.)
즉, git pull
은 remote repo에 있는 변경 사항을 현재 head branch에 병합을 시킨다는 것이다.