[Git] Git 쉽게 이해하기 및 명령어 정리
by 한만섭
- 1. Git이란?
- 2. git 설치
- 3. 저장소 만들기
- 4. git 공간
- 5. 파일의 상태
- 6. 파일 핸들링
- 7. 브랜치
- 8. stash
- 9. 병합
- 10. 히스토리 수정하기
- 11. 원격(remote) 저장소
1. Git이란?
가장 대표적인 version control system
으로써, 특정 시점에서의 파일들의 상태를, 저장하고 보여줄 수 있습니다.
version control system
을 사용해야하는 이유는 아래 이미지 처럼 문서의 버전을 관리하지 않기 위해서 입니다.
위와 같이 버전을 관리하게 되면 관리가 매우 어렵게 됩니다. 문서가 아니라 거대한 프로젝트를 개발할 때는 더욱 버전 관리가 복잡해지는데, 버전 관리를 간단한? 명령어 혹은 클릭을 통해서 할 수 있도록 도와주는 시스템이라고 생각하면 좋을 것 같습니다.
1.1. 특징
- 개별파일 또는 프로젝트 전체를 이전 상태로 되돌릴 수 있고,
- 시간에 따른 변경 사항을 비교해 볼 수 있고,
- 누가 문제를 일으켰는지 추적할 수 있고,
- 누가 언제 만들어낸 이슈인지도 알 수 있습니다.
1.2. 장점
- 빠르다
- 다른 버전 관리 시스템과 다르게 데이터를 파일 시스템 스냅샷 형태로 취급하기 때문에 크기가 작습니다.
- 파일이 달라지지 않았다면 성능을 위해 새로 저장하지 않습니다. 이전 상태의 파일에 대한 링크만 저장
-
거의 모든 명령을 로컬에서 실행합니다.
- 거의 모든 명령이 로컬의 파일과 데이터만 사용합니다. 프로젝트의 히스토리 조회 를 로컬에서 합니다.
- 무결성
- 파일을 이름으로 저장하지 않고 내용과 디렉토리 구조를 이용해서 체크섬과 같은 해시 형태로 저장합니다.
2. git 설치
뭔가를 배울 때 사전적인 정의만 공부하는 것보다 직접 해보는 것이 더 효율적이기 때 설치를 해보고 사용해보며 이해해보겠습니다.
2.1. 설치 확인
git --version
2.2. 환경설정
git config --global user.name 아이디
git config --global user.email 이메일 주소
2.3. 환경설정 확인
git config --list
2.4. zsh 설치
zsh
는 터미널의 가독성을 높혀주고, tab
키로 자동 완성 기능을 제공해줍니다.
-
brew 설치
$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
-
Zsh 설치
brew install zsh
-
oh my zsh
$ curl -L https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh | sh
-
iterm2
brew cask install iterm2
3. 저장소 만들기
Git
, 즉 버전 관리 시스템을 사용하기 위해선 우선 버전을 관리할 저장소를 만들어야합니다. 저장소를 만드는 방법에는 두가지가 있습니다.
- 로컬 프로젝트를 git 저장소로 생성
- 다른 서버에 있는 저장소를
clone
하기
3.1 로컬 프로젝트를 git 저장소로 만들기
생성한 프로젝트를 git저장소로 만들기
$ mkdir git-study
$ cd git-study
$ git init
로컬 저장소와 원격 저장소 연결
$ git remote add oriign <remote-url>
원격 저장소 url
을 변경
$ git remote set-url origin <remote-url>
3.2 다른 서버에 있는 저장소 clone 하기
아래 명령어는 프로젝트 히스토리를 전부 받아옵니다.
$ git clone <remote-url>
clone
을 했을 경우 해당 원격 저장소와 로컬 저장소가 자동으로 연결이 되어있습니다.
4. git 공간
로컬 저장소를 생성했고, 로컬 저장소와 원격 저장소를 연결했다면 git을 직접 사용해볼 수 있습니다. 하지만, 그 전에 git의 구조를 머릿속에 그릴 수 있도록 git이 어떻게 구성되어 있는지 알아보겠습니다.
4.1 Working Directory (작업 디렉토리)
- 개발자가 파일을 추가/수정/삭제하는 공간
- 쉽게말해
.git
파일을 제외한 프로젝트 디렉토리(로컬 저장소) 내의 모든 공간이Working Directory
입니다.
4.2 Staging Area (인덱스)
- 보통
index
라고 하며repository
와Working Directory
사이에 있는 공간 - 파일이 커밋 되기 전에 모여있는 임시 저장 공간으로써 모든 파일은 이 공간을 거쳐 저장소로 옮겨지게 됩니다.
4.3 git directory (Repository)
- git이 프로젝트의 메타데이터와 객체 대이터베이스를 저장하는 곳 (
.git
) Staging Area
에 커밋 가능한 스냅샷 형태로Stage
해서Commit
하면 영구적인 스냅샷으로 저장됩니다.
Working Directory
에서 파일을 수정한다.Staging Area
에 파일을Stage
해서 커밋 할 스냅샷을 만든다.Staging Area
에 있는 파일들을 커밋해서 영구적인 스냅샷으로 저장한다.
5. 파일의 상태
저장소에 존재하는 파일들에는 Working Directory
에 존재하는지, Stage가
되었는지, modified
되었는지 같은 각 파일마다의 상태가 존재합니다.
untracked(추적되지 않음)
- 추적되지 않은 파일은 모두 작업 디렉토리(
Working Directory
)에 있는 상태이며, 인덱스 혹은 저장소에 한번도 들어간 적이 없거나 (ignored
)된 상태의 파일입니다.
unmodified(수정되지 않음)
- 저장소에 저장된 파일이 수정되지 않은 상태를 말합니다.
modified(수정됨)
- 이미 커밋 되었던 파일이 수정됐음을 뜻합니다.
staged/indexed (인덱싱됨)
- 수정된 파일이 인덱스에 포함됐다면(
staging Area
에 올라갔다면 )staged
된 상태입니다. - 워킹 디렉토리의 모든 파일은 크게
Tracked
(관리대상임)와Untracked
(관리대상이 아님)로 나뉩니다. - 처음 저장소를
clone
하면 모든 파일은Tracked
이면서Unmodified
상태이다. 파일을checkout
하고나서 아무것도 수정하지 않았기 때문입니다. - 마지막 커밋 이후, 어떤 파일을 수정했다면 그 파일은
Modified
상태가 됩니다. 실제 커밋을 하기 위해서는 수정한 파일을Staged
상태로 만들고,Staged
상태의 파일을 커밋합니다. 이런 라이프 사이클을 계속 반복합니다.
Untracked
에서git add .
를 하게되면Tracked
되며,Staged
상태가 됩니다.Unmodified
에서 파일을 수정하면Modified
상태가 됩니다.Modified
에서stage
할 수 있고,Staged
에서 커밋을 하게 되면 더이상 수정한 것이 없기 때문에Unmodified
로 가게됩니다.Unmodified
에서 파일을 삭제하면Untracked
상태가 됩니다.
.gitignore
혹은 .git/info/exclude
에 설정되어 있는 패턴에 의해 파일 또는 폴더가 무시될 수 있습니다.
Mac
환경에서 폴더 생성시 자동 추가되는 .DS_store
파일을 .gitignore
에 추가해보겠습니다.
$ echo .DS_Store >> .gitignore
6. 파일 핸들링
6.1 status
-
파일의 상태를 확인하는 명령어
$ git status
-
README.md` 파일을 생성하고, status 명령어를 입력하여 파일의 상태를 확인
-
파일을 만드는 명령어
$ echo >> README.md
-
파일의 상태를 확인해보면
README.md
는 한번도Staged
된적 없기 때문에Untracked
인 것을 확인할 수 있습니다.$ git status On branch master Initial commit Untracked files: (use "git add <file>..." to include in what will be committed) README.md nothing added to commit but untracked files present (use "git add" to track)
6.2 add
-
add
명령은 다음 커밋에 추가하기 위해 파일을staged
상태로 만듭니다.# 특정 파일만 add $ git add <path/file-name> # 모든 파일을 add $ git add --all $ git add -A $ git add .
-
아까 만든
README.md
파일을staged
상태로 만들어보면$ git add README.md $ git status On branch master Initial commit Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: README.md
-
Changes to be committed
> 커밋될 변경 사항들에README.md
가 추가된 것을 볼 수 있습니다. -
README.md
파일은tracked
이면서staged
상태입니다. -
아직 커밋 되지 않은 파일들이 staged 상태인지 명시적으로 확인하는 방법
$ git ls-files --stage 100644 15618ef3d06b41c09e9db5b5d0c9b8beee7183a4 0 README.md
-
README.md
파일을 수정하고 다시 저장한 후에 확인해보면$ git ls-files --stage 100644 501ead6f115ad5367ca9094e3dd43d05ae8df0a6 0 README.md
-
해시가 변경된 것을 확인할 수 있다.
git
의 특징인 무결성 때문입니다.git
은 파일을 저장할 때 이름이 아닌 체크섬, sha와 같은 해시형태로 저장하기 때문입니다.
6.3 Commit
커밋은 파일, 폴더의 추가, 변경 사항을 저장소에 기록하는 것입니다.
커밋하기
$ git commit -m "커밋 로그 메세지 작성"
파일을 수정하고 add
와 commit
커밋 로그 메세지를 한번에 실행하면 아래와 같습니다.
$ git commit -a -m "커밋 로그 메세지 작성"
조회하기
커밋이 만들어질 때 생기는 SHA-1
해시 값을 커밋 해시 혹은 커밋 id라고 부릅니다. 아래 명령어를 통해 현재까지의 커밋을 조회할 수 있습니다.
$ git log
위 명령어만 사용하면 가시적으로 복잡한 커밋 로그를 보여주기 때문에 아래처럼 여러 옵션을 사용하면 가시적으로 좋은 커밋 히스토리를 조회할 수 있습니다.
$ git log --all --oneline --decorate --graph -10
* 2af686a (HEAD -> master) README.md 파일 수정1
* d002a64 README.md 파일 추가
-all
: 모든 브랜치oneline
: 한줄로decorate
: 어떤 커밋을 가르키는지graph
: 그래프 형태로-10
: 최근 10개 이내로- 모든 브랜치를 한줄로 보며 커밋을 하이라이팅해주고 그래프 형태로 10개만 보여달라.
히스토리를 조회하는 것 외에도 diff
명령어를 통해서 비교하는 것도 가능합니다.
인덱스와 작업 디렉토리 비교
수정하고 인덱스 하지 않은 변경 사항과 인덱스를 비교해줍니다. add
명령어도 하기 전에
$ git diff
저장소와 인덱스
add
를 했다면 --cached
옵션을 통해 수정된 저장소와 인덱스를 비교할 수 있습니다.
$ git diff --cached
커밋과 커밋
$ git diff <커밋>.. <커밋>
6.4 파일 삭제
git rm
명령은 작업 디렉토리와 인덱스로부터 파일을 제거해줍니다.
$ echo >> rm_test
$ git rm rm_test
fatal: pathspec ' rm_test' did not match any files
Untracked
파일을 없애려고 하자 git
에서 에러를 발생시켰다. 해당 파일은 작업 디렉토리에만 존재하는 파일이기 때문입니다.
추적하지 않는 (스테이징 이전) 파일을 삭제할 경우 git 명령어 없이 rm을 사용하면 됩니다.
$ rm rm_test
인덱스에 추가된 파일 삭제하기
$ git add rm_test
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: rm_test
$ git rm rm_test
error: the following file has changes staged in the index:
rm_test
(use --cached to keep the file, or -f to force removal)
인덱스에는 추가 되었지만 커밋하지 않은 파일이기 때문에, 인덱스에서만 제거할 것이면 --cached
$ git rm --cached rm_test
인덱스와 작업 디렉토리에서 강제로 삭제를 진행할 것이면 -f
옵션을 사용하라는 메세지를 출력합니다.
$ git rm -f rm_test
**커밋한 파일 지우기 **
$ git commit -m"add rm_test"
[master fe05a8f] add rm_test
1 file changed, 1 insertion(+)
create mode 100644 rm_test
$ git rm rm_test
rm 'rm_test'
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: rm_test
6.5 파일 이름 변경하기
$ git mv <변경전 file-name> <변경 후 file-name>
파일 이름 변경 후 파일 히스토리 확인해보기
$ git commit -m"rename test => text"
[master 680dc7f] rename test => text
1 file changed, 0 insertions(+), 0 deletions(-)
rename test => text (100%)
$ git log text
commit 680dc7ff5cebe918c96433b93f7ff73fd55a1eea
Author: joohee.moon <nt10547@nts-corp.com>
Date: Wed Nov 30 15:37:51 2016 +0900
rename test => text
이름 변경 전 히스토리 보기
$ git log --follow text
commit 680dc7ff5cebe918c96433b93f7ff73fd55a1eea
Author: joohee.moon <nt10547@nts-corp.com>
Date: Wed Nov 30 15:37:51 2016 +0900
rename test => text
commit bcd6501142014d4c0797b2e7f206a944ef91eff6
Author: joohee.moon <nt10547@nts-corp.com>
Date: Wed Nov 30 15:35:36 2016 +0900
test
7. 브랜치
7.1 브랜치란?
- 커밋 그래프에 존재하는 수많은 커밋중에 하나를 가르키고 있는 포인터 같은 존재입니다.
- 안전하게 격리된 상태에서 무언가를 만들 때 사용됩니다.
- 활성 브랜치는 현재 브랜치를 나타냅니다.
- 새로운 커밋이 발생하면 브랜치는 새로운 커밋을 향하게 되는데 이것이 브랜치의 끝입니다.
7.2 브랜치의 활용
-
브랜치 만들기
$ git branch <branch-name>
-
브랜치 나열하기
새로운 브랜치를 만들었지만
HEAD
라는 포인터는 아직master
를 가르킵니다.$ git branch develop * master
-
브랜치 이동하기
$ git checkout <branch-name>
-
브랜치 만들면서 이동하기
$ git checkout -b <branch-name>
-
커밋하지 않은 상태에서 브랜치 이동하기
develop
에서 작업한 내용을 저장하고master
로checkout
하려고 할 때 실패합니다. 해당 경우는 이동하려고하는 브랜치와 현재 브랜치의 내용이 서로 다를 때 에러가 발생합니다. 서로 다르지 않다면 수정하고도 브랜치 이동이 가능합니다.$ git checkout develop Switched to branch 'develop' # newfile을 열어 수정1을 입력하고 저장한다. $ git checkout master error: Your local changes to the following files would be overwritten by checkout: newfile Please commit your changes or stash them before you switch branches. Aborting
-
브랜치 삭제
브랜치를 삭제할 때는 병합되지 않은 브랜치는 에러를 발생 시킵니다.
$ git brnach -d <branch-name>
-
병합되지 않은 브랜치 삭제
$ git branch -D <branch-name>
-
모든 브랜치 푸쉬
$ git push origin --all
8. stash
stash
는 작업 디렉토리와 인덱스의 현재 상태를 안전하게 저장할 수 있는 명령어입니다.
checkout
명령어를 쓰고싶은데 수정한 부분이 있어서 불가능할 때 작성하던 코드를 안전한 스택에 쌓아두는 방법입니다.- 저장한 내용을 다른 브랜치로 옮기는 것도 충분히 가능합니다.
-
다른 브랜치에서 작업을 한 후에 다시 돌아와 복구하여 작업을 이어갈 수 있습니다.
-
추적중이지 않은 파일 stash
$ echo >> newfile_1 $ git stash No local changes to save $ git status On branch develop Untracked files: (use "git add <file>..." to include in what will be committed) newfile_1 nothing added to commit but untracked files present (use "git add" to track)
stash
는 기본적으로tracked
파일을 기반으로 하기 때문에, 제대로 동작하지 않은 것을 확인할 수 있습니다. -
추적중이지 않은 파일 강제 stash
작업 디렉토리와 인덱스의 상태가 저장되었습니다.
$ git stash –u Saved working directory and index state WIP on develop: c462496 add newfile HEAD is now at c462496 add newfile
stash list
-
stash area
에 저장되어 있는 것을 목록으로 확인하기$ git stash list stash@{0}: WIP on develop: c462496 add newfile
-
stash
에 이름을 붙여 저장하기$ git stash save <stash-name>
New file_2를 만들고, 이름 붙여 저장하고 ,
stash area
확인해보기$ git stash save –a add newfile_2 Saved working directory and index state On develop: add newfile_2 $ git stash list stash@{0}: on develop: add newfile_2 stash@{1}: WIP on develop: c462496 add newfile
이름을 정하지 않으면
stash
시점의 마지막 커밋 메세지로 저장되기 때문에 이름을 정해주는 것이 좋습니다.
**stash show **
-
stash
에 대한 자세한 내용 확인하기git show stash@{0}
stash pop
-
stash area
(스택)의 Top값을 현재 브랜치에 적용한 뒤 목록에서 제거합니다.$ git stash pop On branch develop Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: newfile_2 Dropped refs/stash@{0} (5791ac1e23141b31e2e0413912f6ad5c03127d25) $ git stash list stash@{0}: WIP on develop: c462496 add newfile
stash apply
-
stash area
의 Top값을 목록에서 제거하지 않습니다.$ git stash apply
-
pop
,apply
를 사용할 때 충돌이 난다면 충돌을 해결해줘야 다른 동작을 할 수 있습니다.
**stash drop **
-
id를 입력해주면 해당하는 stash를 삭제합니다.
$ git stash drop stash@{0}
**stash clear **
-
전부 삭제
$ git stash clear
9. 병합
브랜치는 커밋을 가리키는 포인터입니다. 서로 다른 두개의 브랜치가 가르키고 있는 커밋을 합치는 것을 병합( merge
)라고합니다.
9.1 3-way merge
-
master
브랜치에서develop
브랜치 만들기$ git checkout -b develop
-
커밋하나 남기기
$ git commit -m "develop commit"
-
master
로checkout
$ git checkout master
-
feature/1
브랜치 만들기$ git checkout -b feature/1
-
커밋하나 남기기
$ git commit -m "feature/1 commit"
-
병합 시키기
$ git checkout develop $ git merge feature/1
9.2 fast-forward merge
-
위와 같이
master
에서bugFix
를merge
할 경우master
는bugfix
와 이어져 있기 때문에 앞으로 당겨오기만 하면 됩니다. -
위에서 3-way merge를 했을 때
feature/1
가develop
과 이어져 있지만 뒤에 위치한 경우feature/1
에서develop
을merge
할 경우 그저 앞으로 당겨지기만 하면 되기 때문에fast-forward
방식 병합입니다.$ git checkout feature/1
$ git merge develop
git log --oneline --decorate --all --graph -10
**충돌 해결 **
- 같은 파일을 두 브랜치에서 수정하고 merge하면, git은 해당 부분을 자동으로 merge하지 못하기 때문에 충돌이 발생합니다. 충돌이 발생하면 개발자가 직접 충돌을 해결한 후에 다시
add
,commit
하면 됩니다.
10. 히스토리 수정하기
Git은 버전 관리 시스템으로써 커밋을 통해 버전별 코드를 저장할 수 있습니다. 하지만 이외에도 아래와 같은 기능으로 버전 관리를 좀 더 효과적으로 할 수 있게 해줍니다.
- 커밋은 순서, 커밋메세지, 커밋한 파일을 변경할 수 있습니다.
- 여러개의 커밋을 하나로 합치거나(
squash
) 하나의 커밋을 여러개의 커밋으로 분리할 수도 있습니다. - 커밋 전체를 삭제할 수도 있습니다.
- 해시 값이 변경되기 때문에 원격 저장소에 저장된 커밋은 건드리지 않는 것이 좋습니다.
amend
-
마지막 커밋을 수정할 수 있는 명령어
$ git commit --amend
-
vi에디터를 통해서 커밋메세지를 변경
10.1 reset
reset
명령어는 HEAD
를 이전 상태로 되돌리고 과거부터 시작합니다.
그렇다면 HEAD
가 무엇인지 부터 알아야 합니다. HEAD
는 현재 브랜치의 마지막 커밋의 스냅샷입니다. 더 간단히 생각하면 현재 브랜치의 최신 버전인 것 같습니다. 즉, reset
은 최신버전에 문제가 생겨서 이전 버전로 되돌려야할 떄 사용하는 것입니다.
reset
에는 3가지 mode
가 존재합니다.
작업 디렉토리를 유지하며 되돌리기 (--mixed
)
--mixed
로 입력하지 않아도 기본으로 적용되는 모드이다. 작업디렉토리는 유지하면서 인덱스를 HEAD와 함께 되돌립니다.
$ git reset 커밋 해시
작업 디렉토리, 인덱스를 유지하며 되돌리기 (--soft
)
현재의 인덱스 상태와 작업 디렉토리 내용을 그대로 보존한채 커밋만 취소힙니다.
$ git reset --soft 커밋 해시
작업 디렉토리, 인덱스를 유지하지 않고 되돌리기 (--hard
)
작업했던 내용을 의도적으로 모두 버릴 때 사용합니다. 실수를 해서 특정 커밋으로 되돌리고 싶을 경우에 사용할 것 같습니다.
revert
reset
에서 남지 않는 취소 이력을 만들면서 취소하는 경우에 사용합니다.
$ git revert <커밋 id>
$ git revert <커밋 id> ^..<커밋 id>
$ git revert –m 1 <merge 커밋 id>
10.2 cherry-pick
- 브랜치를 통째로 merge하지 않고, 특정 커밋을 반영하고 싶을 때 사용합니다.
merge
방식과 마찬가지로conflict
해줘야합니다.merge
방식은 해당 브랜치의 이력이 통째로 다 넘어가는 것 입니다.cherry-pick
은 원하는 커밋만pick
해서 내 브랜치에 반영하고 싶은 경우에 사용합니다.
cherry-pick 실습
{width:”400”}
// master branch
master1
master2
master3
// develop branch
dev1
dev2
dev3
master
브랜치에서 develop
브랜치의 특정 커밋 내용만 cherry-pick
을 해보겠습니다.
현재 HEAD
인 master3
커밋에서 dev2
커밋만 반영해보도록 하겠습니다.
$ git cherry-pick 90cd9f7
위 화면처럼 master3
커밋에 dev2
커밋 내용을 합치려고 하니 충돌이 발생했습니다. 현재 git의 상태를 확인해보겠습니다.
✘ macpro@macproui-MacBookPro ~/Desktop/git-study master ●✚ gst
현재 브랜치 master
현재 90cd9f7 커밋을 뽑아 내고 있습니다.
(충돌을 바로잡고 "git cherry-pick --continue"를 실행하십시오)
(뽑기 작업을 취소하려면 "git cherry-pick --abort"를 사용하십시오)
병합하지 않은 경로:
(해결했다고 표시하려면 "git add <파일>..."을 사용하십시오)
양쪽에서 수정: new_file
커밋할 변경 사항을 추가하지 않았습니다 ("git add" 및/또는 "git commit -a"를
사용하십시오)
충돌이 발생했을 경우 아래의 두 선택지가 있습니다.
# 충돌 해결 후 cherry-pick 완료하기
git cherry-pick --continue
# cherry-pick 취소하고 충돌 전 상태로 돌아가기
git cherry-pick --abort
충돌을 잘 해결 했다면 --continue
옵션을 통해 넘어갈 것이고, 충돌 해결하는 과정에서 문제가 생겼거나 잘못된 cherry-pick
을 했다면 --abort
옵션으로 되돌릴 수 있습니다. 이처럼 취소하거나 넘어가는 선택은 이후에 정리할 rebase
에서도 등장합니다.
저는 충돌을 해결하고 --continue
를 해보도록 하겠습니다.
# 충돌 해결 후
$ git add {충돌 파일}
$ git cherry-pick --continue
커밋 히스토리를 확인해보겠습니다.
* ffba3c7 (HEAD -> master) cherry-pick으로 master에 dev2반영
* 2fc8e72 master3
* a478e9c master2
| * fb5680e (develop) dev3
| * 90cd9f7 dev2
| * 0e58cfc dev1
|/
* 502ff54 master1
* cd81d07 add new_file
* 1ba010f remove rm_test
* dba90fd add rm_test
잘 적용된 것을 확인할 수 있습니다.
cherry-pick
을 적용한 커밋도 되돌리고 싶다면 reset
명령어를 이용해서 되돌릴 수 있습니다.
git reset --hard 2fc8e72
* 2fc8e72 (HEAD -> master) master3
* a478e9c master2
| * fb5680e (develop) dev3
| * 90cd9f7 dev2
| * 0e58cfc dev1
|/
* 502ff54 master1
* cd81d07 add new_file
* 1ba010f remove rm_test
* dba90fd add rm_test
* 3eb3830 1
10.3 rebase
git에서 브랜치를 합치는 2가지 방법 중에 하나인 rebase
입니다. 단어 그대로 re
+ base
“베이스를 바꾸다” 입니다. rebase
를 사용하는 방법, 왜 사용하는지, 단점에 대해서 얘기해보겠습니다.
이해를 돕기 위해서 merge
와 rebase
를 비교하며 설명하도록 하겠습니다.
Merge
위와 같이 master branch
와 3개의 commit
이 있는 상황에서
git checkout -b dev
위 명령어를 통해서 새로운 dev branch
를 만들고 dev branch
로 checkout
을 하고 커밋을 2번 하게 되면 그래프는 아래처럼 됩니다.
위 상황에서 dev branch
를 master branch
로 병합하는 명령어를 실행해봅니다.
git checkout master
git merge dev
commit2
를 가르키고 있던 master
branch가 merge
를 통해서 fast-forward
되어서 dev
와 같은 branch
를 가리킵니다.
이렇게 보면 merge
만 사용해도 이상적인 commit graph를 나타낸다고 생각할 수 도 있습니다. 하지만 제가 branch를 만들어서 작업을 하는 도중 팀원이 커밋을 진행했을 경우 아래와 같은 그래프를 나타납니다.
위 상황에서 master
로 checkout
을 해서 merge
를 해주게 되면 그래프가 복잡해지는 상황이 나옵니다.
git checkout master
git merge dev
이렇게 하게되면 fast-forward
가 아닌 3-way merge
를 하며 merge commit을 남기게 됩니다.
Rebase
그렇다면 rebase
를 사용하면 어떻게 되는지 확인해보겠습니다.
위에서 얘기했듯이 rebase
란 base를 다시 지정하다라는 의미입니다. 현재 dev
branch의 root는 commit2
로서 master
가 가르키고 있는 commit
보다 뒤에 위치합니다.
여기서 아래와 같은 명령어를 사용해봅니다.
git checkout dev
git rebase master
# 위와 동일
git rebase master dev
위 명령어는 dev
branch의 base를 master
로 사용하겠다는 의미입니다.
그러면 위와 같은 형태의 그래프가 나타나게 됩니다.
이렇게 되면 master
브랜치에서 dev
브랜치를 병합하면 fast-forward
으로 깔끔하게 병합이 됩니다.
’ 그래서 rebase
를 왜 써야하는건데? ‘ 라고 생각이 들 수도 있습니다. 이유를 생각해보면 Git은 버전 관리 시스템이고, 버전을 보기 쉽게 관리하는 것이 좋은 것은 당연한 것 같습니다. 이렇게 적고보니 무조건 rebase
로 브랜치 병합을 해야하는 것 처럼 보이지만 rebase
를 조심해야하는 상황이 있습니다.
로컬에서만 사용하기
rebase
를 통해 원격 저장소에 올라간 커밋을 변경하게 될 경우 다른 팀원 모두가 힘들어질 수 있습니다.
rebase
를 하게 되면 커밋을 수정하기 때문에 rebase
전의 커밋에서 수정하던 팀원은 원격저장소에 올리기 위해 다시merge
를 해야하고 그렇게 올린 커밋을 제가 다시 pull
하게 될경우 엉킬 수 있습니다. 이런 문제점을 막기 위해 로컬에서 rebase
로 정리를 한 후에 원격저장소에 올리는 것이 맞습니다.
merge , rebase에 대한 생각
둘 중에서 뭐가 좋다라고 말할수는 없지만 많은 브랜치를 통해 개발하는 특성상 프로젝트의 규모가 커질수록 merge방식을 사용한다면 버전 관리가 어려워질 것 같습니다. rebase를 이해하고 사용한다면 가시적인 측면에서 더 좋은 히스토리를 남길 수 있을 것 같습니다.
10.4 squash
간단히 여러 개의 커밋을 하나의 커밋으로 합칠 수 있는 기능입니다. 위에서 정리한 rebase
처럼 커밋 히스토리를 더욱 깔끔하게 만들 수 있습니다.
dev3
커밋부터 dev1
커밋까지 합치기
* 2fc8e72 (master) master3
* a478e9c master2
| * fb5680e (HEAD -> develop) dev3
| * 90cd9f7 dev2
| * 0e58cfc dev1
|/
* 502ff54 master1
* cd81d07 add new_file
* 1ba010f remove rm_test
* dba90fd add rm_test
* 3eb3830 1
git checkout develop
git rebase -i HEAD~3
pick 0e58cfc dev1
pick 90cd9f7 dev2
pick fb5680e dev3
# 아래처럼 수정
pick 0e58cfc dev1
s 90cd9f7 dev2
s fb5680e dev3
충돌이 발생하지 않았기 때문에 커밋 메세지를 수정하는 곳으로 넘어왔습니다.
# 커밋 3개가 섞인 결과입니다.
# 1번째 커밋 메시지입니다:
dev1
# 커밋 메시지 #2번입니다:
dev2
# 커밋 메시지 #3번입니다:
dev3
# 변경 사항에 대한 커밋 메시지를 입력하십시오. '#' 문자로 시작하는
"~/Desktop/git-study/.git/COMMIT_EDITMSG" 28L, 730C
* 3a89a53 (HEAD -> develop) dev1
| * 2fc8e72 (master) master3
| * a478e9c master2
|/
* 502ff54 master1
* cd81d07 add new_file
* 1ba010f remove rm_test
* dba90fd add rm_test
* 3eb3830 1
위 그래프를 보시면 dev2
,dev3
은 없어진 것을 볼 수 있습니다. 하지만 코드는 dev2
,dev3
내용이 적용되어있습니다. squash
를 통해 불필요한 커밋을 합쳐서 좀 더 보기 좋은 커밋 히스토리를 만들 수 있을 것 같습니다.
s
11. 원격(remote) 저장소
-
원격 저장소 조회
$ git remote -v
-
원격 저장소 목록 조회
$ git remote show origin
-
원격 저장소에
push
$ git push origin <branch-name>
-
원격 저장소에 강제로
push
$ git push origin <branch-name> -f
-
원격 저장소에 브랜치 삭제
$ git push origin :<branch-name>
11.1 fetch
- 로컬 저장소에는 없지만, 원격 저장소에는 있는 데이터를 모두 가져옵니다.
- 이 때 로컬의 파일 내용은 변경되지 않고 그대로 남습니다. 원격 저장소의 데이터를 가져와서 저장해두고 사용자가
merge
하도록 준비만 해둡니다. - 원격 저장소의 모든 브랜치를 로컬에서 접근할 수 있어서 언제든지
merge
를 하거나 내용을 살펴볼 수 있습니다.
11.2 pull
원격 저장소 브랜치에서 fetch
뿐만 아니라 merge
까지 수행합니다.
-
$ git push
을 사용할 때 다른 팀원이 해당 브랜치에 커밋 후 푸쉬를 해서 내가 작업하던 커밋이 뒤쳐져 있을 때 아래와 같은 에러가 발생합니다.! [rejected] <branch-name> -> <branch-name> (non-fast-forward) error: failed to push some refs to '<repository-url>.git' hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Merge the remote changes (e.g. 'git pull') hint: before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.
이 때
pull
명령어를 사용하라는 힌트를 줍니다.pull
을 사용하면merge
커밋이 생성되기 때문에 이와 같은 상황에서는 아래 명령어를 통해 원격 저장소에 있는 최신 커밋을pull
한 후에 그 커밋으로rebase
를 해줌으로써 불필요한 커밋 생성을 줄일 수 있습니다.$ git pull --rebase
-
pull
$ git pull origin foo $ git fetch origin foo; git merge origin foo
Subscribe via RSS