많은 협업에서 프로젝트의 변경 사항을 관리할 때, 매번 새로운 파일로 저장하거나 공유를 하는 것은 작업의 효율에 큰 문제를 일으키기 마련이다. 이러한 문제를 해결하기 위해 파일의 변화를 기록하여 그 최신 버전을 공유하고 과거 버전으로 돌이킬 수 있게 해주는 시스템이 개발되었는데, 이를 버전 관리 시스템 (Version Control System)이라 한다. 그 중 현재 널리 사용되고 있는 Git을 중점적으로 배웠기에 그 내용을 이 글에서 정리한다.
버전 관리 (Version Control) : 파일의 변화를 기록하는 것.
버전 관리 시스템 (Version Control System, 이하 VCS) :
파일의 변화를 기록하여 추후 특정 버전으로 불러올 수 있는 시스템. SVN, PerForce, Git
버전 관리가 어디서 이뤄지는지에 따라 구분할 수 있다.
- 로컬 : 내 컴퓨터에서만 버전 관리
- 중앙집중형 : 서버에서만 버전 관리
- 분산형 : 내 컴퓨터와 서버에서 같이 버전 관리
델타 기반 : 대부분의 VCS는 각 파일의 변화를 시간 순으로 관리.
스냅샷(snapshot) : 특정 시점에 스토리지의 파일 시스템을 포착해 보관하는 기술
Git은 데이터를 파일 시스템 스냅샷의 연속, 스트림(흐름)으로 취급한다.
Git은 상태를 저장할 때마다 파일이 존재하는 그 순간을 중요하게 여긴다.
Git은 파일을 아래 세 가지 상태로 관리한다.
Committed : 버전 관리를 한 상태
Modified : 수정이 일어난 상태
Staged : 커밋하겠다고 한 상태
수정한 파일을 곧 커밋할 것이라고 표시한 상태를 의미한다.
커밋(commit) : 확정하다. 파일의 변화를 확정.
각 상태의 작업
작업 디렉토리에서 파일을 수정 -> Modified
스테이지 영역에 파일을 추가하여 커밋할 스냅샷을 만든다. -> Staged
스테이지 영역에 있는 파일들을 커밋해서 Git 디렉토리에 영구적인 스냅샷으로 저장한다. → Committed
git init : git 시작, 레포지토리 생성. Initialize repository
git commit : 커밋을 한다.
git branch 브랜치명 : 브랜치를 새로 만든다.
-f 브랜치명 위치 : 브랜치의 위치를 지정, 브랜치를 위치로 강제로 이동.
-d 브랜치명 : 브랜치를 삭제
-u 추적할-원격-브랜치명 (추적시킬-브랜치명) : 브랜치가 원격 브랜치를 추적하도록 설정. 후술.
git checkout 위치 : HEAD를 위치로 옮긴다.
-b 브랜치명 (위치) : 브랜치를 새로 만들고 선택, HEAD를 옮긴다. 위치 또한 지정 가능하다. 원격에서의 사용은 후술
HEAD : 현재 작업 중인 커밋의 위치를 의미한다.
git merge 브랜치명 : 브랜치를 현재 HEAD에 합친다. (merge : 병합하다)
git rebase 위치 : 현재 HEAD로부터 위치까지의 위치의 위에 커밋을 접목한다.(덧붙인다) HEAD가 위치의 부모에 있다면, 위치로 이동하여 최신 상태가 된다.
git log : 커밋의 위치(해시, hash)를 모두 표시한다.
위치^ : 위치의 한 커밋 위를 상대적으로 가리킨다. 상대 참조. ex) HEAD^ - HEAD 한 커밋 위
위치~수 : 위치의 수만큼의 위를 상대적으로 가리킨다. 상대 참조. ex) HEAD~3 - HEAD 세 커밋 위
위치^수 : 위치에서 한 커밋 올라갈 때, 부모가 다수 있을 때 부모를 골라 올라갈 수 있다. ex) HEAD^2 - HEAD 두 번째 부모 한 커밋 위
위치~수^수^ : 상대 참조는 복수의 참조를 한번에 사용할 수 있다.
git reset 위치 : 위치까지 변경내역을 되돌리는 명령어. 애초에 커밋하지 않은 것처럼 예전 커밋으로 브랜치를 옮긴다. * 로컬 (본인의 컴퓨터) 브랜치에서만 사용할 것
git revert 위치 : 위치까지 변경내역을 되돌리고 공유하는 명령어. 리모트 (다른 사람의 컴퓨터) 브랜치에서 사용한다. * 반드시 한 커밋씩 되돌릴 것. 아니면 충돌한다.
git cherry-pick 커밋(위치) 커밋 ... : 현재 위치(HEAD) 아래에 있는 일련의 커밋들에대한 복사본을 만들겠다는 명령어. rebase처럼 HEAD에 커밋을 덧붙인다. git rebase -i처럼 사용할 수 있다.
git tag 태그명 커밋 : 영구적으로 움직이지 않는 태그를 커밋에 다는 명령어. 태그에 checkout해도 태그에 직접 커밋은 할 수 없다. HEAD가 분리될 뿐.
git describe (위치) : 위치에서 가장 가까운 부모 태그까지, 몇 커밋 멀리 있는지와 묘사하고 있는 커밋의 해시(위치)를 표시한다. 위치를 지정하지 않으면 지금 checkout된 곳을 사용한다.(HEAD)
git status : repository 상태 확인
git add 파일명 : git에 파일 추가, 파일명에 .을 입력하면 디렉토리 내 전부를 추가 → staged 상태로 변환, 스테이징 영역 추가
git log : 커밋 기록 표시 - 다양한 옵션으로 커스터마이징 가능
--all : 모든 브랜치 표시
너무 길면 q, w, e로 표시 조절 가능
e : 아래로 / w : 위로 / q : 나가기
git restore 파일명 : 변화 내용 복구
git config : git 설정, Configure repository
표시 내용 CONFLICT : 충돌이 일어났다는 의미.
git clone : 원격 저장소의 복사본을 로컬에 생성할 때 사용하는 명령어. * 실제로 사용하는 방법은 좀 다르다.
원격 브랜치 origin/main : 원격 저장소의 상태를 반영, 추적하는 특수한 브랜치
브랜치의 이름은 main, 원격 저장소의 이름은 origin.
체크 아웃을 하게 되면 분리된 HEAD 모드로 가게 된다.
git fetch : 원격 저장소에서 업데이트된 내용을 가져오는 명령어.
원격 저장소에는 있지만 로컬에는 없는 커밋들을 다운로드 받고 원격 브랜치가 가리키는 곳을 업데이트한다.
로컬 상태는 전혀 바꾸지 않는다.
작업 전에 반드시 하게 될 작업이 될 것.
git remote 이름 URL : Github 등의 원격 저장소와 연결. 원격 저장소의 이름은 관습적으로 origin을 많이 쓴다.
git remote : 현재 원격 저장소의 이름을 표시한다.
- 로그를 git adog로 모든 브랜치를 예쁘게 한 줄에 그래프로 표시하기 위한 설정 명령.
git config --global alias.adog "log --all --decorate --oneline --graph”
실사용 : git adog
- 브랜치 명은 다른 커밋의 주소 등과 겹쳐서는 안된다.
- HEAD로 지정된 브랜치는 강제 이동이 되지 않는다.
git checkout -b 추적시킬-브랜치명 추적할-원격-브랜치명
git branch -u 추적할-원격-브랜치명 (추적시킬-브랜치명)
git push 원격-저장소 위치
git push 원격-저장소 로컬-위치:원격-위치
로컬 위치로 가서 모든 커밋을 수집하고, 원격 저장소의 원격 위치에 부족한 커밋을 채워넣는다.
":" colon refspec(콜론 참조스펙) : git이 알아낼 수 있는 위치
git fetch 원격-저장소 위치
위의 push와 같이, 원격의 위치로 가서 모든 커밋을 수집하고, 로컬 저장소에 부족한 커밋을 원격 브랜치 밑에 채워넣는다. 다음과 같이 콜론 참조스펙도 사용 가능하다.
git fetch 원격-저장소 원격-위치:로컬-위치
딱히 인자가 없다면, 모든 원격 브랜치로 커밋들을 내려받는다.
git pull 원격-저장소 위치
pull은 fetch에 merge를 더한 것
fetch와 마찬가지로 원격의 위치로 가서 모든 커밋을 수집하고, 로컬 저장소에 부족한 커밋을 원격 브랜치 밑에 채워넣고 merge한다. 콜론 참조스펙도 사용 가능하다.
cd : change directory, 디렉토리 변경
mkdir 디렉토리명 : make directory, 디렉토리 생성
ls : list, 디렉토리 안 모두 표시
touch 파일명 : 빈 파일 생성
clear : 지우기
rm -f 파일명 : 파일 삭제
vi 파일명 : vim editor 열기
vim editor
vim editior 안에서 i : insert mode, 텍스트 작성 모드
vim editior 안에서 Esc : 명령어 모드
:wq : vim editior에서 나가기
효율적인 협업을 위한 브랜치 관리 전략에는 Git Flow, Github Flow, Gitlab Flow 등이 있다.
main 브랜치는 항상 안정적이고 최신의 상태여야 한다. 실수로 main 브랜치에 푸시하지 않도록 주의한다.
브랜치 이름은 다른 사람이 봐도 어떤 작업인지 한눈에 알 수 있도록 명확히 이름을 짓는다.
원격에도 자주 푸시해 어떤 작업을 하는지 다른 사람에게 공유한다.
피드백이나 도움이 필요한 경우 혹은 병합할 준비가 됐다면 Pull Request를 연다. Pull Request는 코드 리뷰를 도와주는 시스템으로 어떤 변화가 있는지 확인할 수 있고, 코드에 대해 토론할 수 있으며, 간단한 충돌도 해결할 수 있다.
팀원들과 충분한 논의를 거친 후에 main으로 병합할 수 있도록 한다.
(Advanced) main에 푸시 됐거나 병합된 것은 즉각 배포되어야 한다.
Git 실습을 하며, 수없이 많은 충돌이 일어났다. 이를 해결하기 위해 다음과 같은 규칙을 배웠고, 실천했다.
main 브랜치에서 기능 관련 브랜치를 만들고 체크아웃한다.
기능을 개발할 때마다 커밋하면서 푸시를 한다.
2-1. 개발 및 커밋하기 전 git fetch를 통해서 원격 저장소를 항상 최신화한다.
2-2. main에 새로운 기능이 추가되었다면 내 브랜치로 병합한다.
2-3. 커밋하기 전 항상 내 코드를 스스로 리뷰한다.
기능 개발이 끝났다면 Pull Request를 연다.
3-1. Pull Request 도중 충돌이 발생했을 경우 로컬에서 충돌 해결한 다음 푸시한다.
다른 사람들한테 Pull Request를 열었다고 알려준다.
해당 팀원의 코드를 리뷰한다.
리뷰가 끝났다면 병합한다.
병합이 끝나면 원격 브랜치를 삭제한다.
- Require a pull request before merging
- Require approvals : 전체 팀원 - 1 만큼 설정.
- Require review from Code Owners : 팀장(레포지토리 주인)의 리뷰를 반드시 받아야 한다.
- Include administrator : 팀장도 리뷰를 받아야 한다.
타입[범위]: 제목
[본문]
[꼬릿말]
Feat: 새로운 기능의 추가
Fix: 버그 수정
Docs: 문서 수정
Style: 스타일 관련 기능(코드 포맷팅, 세미콜론 누락, 코드 자체의 변경이 없는 경우)
Refactor: 코드 리펙토링
Chore: 빌드 업무 수정, 패키지 매니저 수정 등 잡다한 일(ex .gitignore 수정 같은 경우)
제목은 명령문을 사용하며 50자 이내로 제한한다.
본문은 구현 방법보다는 변경한 내용과 이유를 설명한다.
한 줄 당 72자 내로 작성하며 양에 구애받지 않고 최대한 상세히 작성한다.
선택사항
보통 Github Issue 추적을 위해 사용한다.
“유형: #이슈 번호” 형식을 사용하며, 여러 개의 이슈 번호를 적을 때는 쉼표로 구분한다.
유형은 Resolves, See also를 사용한다.