1. 깃 기초
https://git-scm.com/book/ko/v2/시작하기-Git-기초
-깃은 VCS(Version Control System)의 한 종류로, Distributed Version Control System(DVCS)이다.
-다른 VCS는 시간순으로 파일을 관리하지만 깃은 상태 변화에 따라 관리한다. 깃은 파일이 존재하는 그 순간을 중요하게 여기며, 데이터를 스냅샷의 스트림처럼 취급한다.
-프로젝트의 모든 히스토리는 로컬 디스크 내 깃 디렉토리에서 관리되며 조회가 매우 빠르다.
-관리되는 파일의 상태에는 Modified, Staged, Committed 세 가지가 있다. (아래에서 다시 세분화된다)
-워킹 트리는 깃 디렉토리에서 프로젝트의 특정 버전을 checkout 한 것이다.
2. 깃 저장소
https://git-scm.com/book/ko/v2/Git의-기초-Git-저장소-만들기
2-1) 새 로컬 디렉토리에 깃 버전관리를 직접 적용하기
$ git init : 깃 저장소(git repository)에 필요한 기초 파일이 존재함
$ git add <파일이름> : 현재 working 영역을 staging 영역에 추가함 (staging 내 파일만 깃 커밋 할 수 있음)
$ git add -A / git add . : 디렉토리 내 전체 파일을 추가
$ git restore --staged <파일 이름> : 해당 파일을 staging 영역에서 내림
$ git restore <파일 이름> : 해당 파일을 가장 최근 커밋 시점으로 되돌림
$ git commit -m 'initial commit' : staging 영역을 history 영역에 추가함 (이때부터 해당 파일의 버전 관리를 시작함)
* 'git commit'을 바로 입력하면 설정된 에디터가 나타나는데(git config --global core.editor 명령어로 확인), 커밋 메시지를 입력하고 저장하면 됨
2-2) 기존의 다른 깃 저장소를 불러오기
$ git clone <url> : 해당 프로젝트의 히스토리를 전부 받아온 후 가장 최신 버전을 워킹 디렉토리에 checkout 함
* .gitignore 파일 : 깃의 관리를 받지 않을 파일을 등록함
-작성 규칙:
- *.a 확장자가 .a인 모든 파일 적용
- #으로 시작하는 라인은 무시
- /로 시작하면 현재 파일만 적용하고 하위 디렉토리의 같은 파일 이름은 적용하지 않음
- /로 끝나면 디렉토리를 표현
3. 수정하고 저장하기
https://git-scm.com/book/ko/v2/Git의-기초-수정하고-저장소에-저장하기
-워킹 디렉토리 내 파일은 크게 Tracked(관리대상임)와 Untracked(관리대상이 아님)로 나뉜다.
-Tracked 파일은 깃이 보고 있는 파일이며, 다시 Unmodified(수정하지 않음), Modified(수정함), Staged(커밋으로 저장소에 기록예정) 상태로 나뉜다.
$ git status : 위와 같이 파일의 상태를 확인함
$ git log : 깃 히스토리를 확인함
$ git log --online --decorate --graph --all
$ git log --follow [파일이름]: 해당 파일의 변경 사항이 담긴 모든 커밋을 표시함. (파일 이름 변경도 표시됨)
$ git log branchA..branchB: 브랜치A에 없는 브랜치B의 모든 커밋 히스토리를 보여줌
$ git diff: 스테이지에 올라가지 않은 변경 사항을 보여줌.
$ git diff --staged: 스테이지에 올라갔지만 아직 커밋하지 않은 변경 사항을 보여줌.
$ git diff branchA branchB: 브랜치B에는 있지만 브랜치A에는 없는 것의 변경 내용(diff)를 표시함. (branch 간 상태 비교)
Stash : 현재 작업 중인 파일을 git에서 제공하는 stack에 임시로 저장하는 기능
$ git stash: 변경사항을 스택에 임시 저장하고 현재 작업 내역에서 지움.
$ git stash push: 위 명령어와 동일하지만 현재 작업 중인 디렉토리를 HEAD 커밋과 매치함
$ git stash -m 'message': 메시지와 함께 추가함
$ git stash list: 스택에 임시 저장된 목록을 보여줌.
$ git stash show <stash id>: stash를 자세하게 보여줌
$ git stash apply: 스택에 저장된 변경 사항을 현재 작업으로 불러옴.
$ git stash pop: 스택에 저장된 변경 사항을 불러오고 스택에서 제거함.
$ git stash drop: 스택 최상단에 저장된 내역을 삭제함.
$ git stash drop <n>: n위치에 있는 stash를 제거
$ git stash clear: 모든 stash를 제거
=> 리모트 저장소의 업데이트를 받거나 다른 브랜치로 변경할 때, 만약 당장 작업 내용을 커밋 할 준비가 안되었으면
1. git stash로 현재 작업 파일을 임시 저장한 후
2. git pull(or git switch) 등을 진행하고
3. git stash pop으로 원래 작업 파일을 가져옴
4. 만약 충돌이 발생하면 바로 해결하면 됨
커밋 되돌리기
$ git revert <커밋 ID> : 해당 커밋을 현재 커밋에 반영하여 새로운 커밋을 생성함
*커밋 히스토리를 보존하기 때문에 안전하게 커밋을 되돌리는 방법임
*깃 히스토리가 지저분해진다는 단점이 존재
*현재 커밋을 바탕으로 이전의 커밋을 적용하는 것이기 때문에 이전 커밋이 완벽하게 반영되지 않을 수 있음
$ git commit --amend : 현재 HEAD가 가리키고 있는 커밋을 수정함
* git commit --amend -m '~' : 바로 직전 커밋의 메시지를 수정함
$ git cherry-pick <커밋 ID> : 해당 커밋을 현재 브랜치로 가져옴
* 브랜치 간에 커밋을 옮김 (복붙)
* 실수로 다른 브랜치에 커밋하거나, 다른 브랜치의 커밋을 현재 브랜치에 가져올 때 유용함
$ git reset [파일 이름]
$ git reset --soft [커밋 ID] : 커밋 내용만 되돌리고 working 및 staging 영역의 내용은 유지됨
$ git reset --mixed [커밋 ID] : 기본 값으로 설정되어 있으며(git reset [커밋 ID]), staging 영역 및 현재 커밋한 내용을 되돌림 (워킹 디렉토리의 변경 사항은 유지하고, 언스테이징?)
$ git reset --hard [커밋 ID] : working, staging, 커밋된 내용 모두 되돌림
4. 깃 브랜치
https://git-scm.com/book/ko/v2/Git-브랜치-브랜치란-무엇인가
-깃의 최고 장점이 바로 브랜치 모델이다.
-다른 브랜치에 영향을 주지 않는 독립적인 워크스페이스 개념이라고 볼 수 있다.
-브랜치는 프로젝트의 히스토리를 여러 갈래로 분리한 도로 같은 것이다.
-실제 자료는 커밋이고 브랜치는 커밋을 관리하는 포인터 같은 것이다.
$ git branch : 브랜치 목록을 보여줌
$ git branch -a : 브랜치와 원격 브랜치도 함께 보여줌
$ git branch -v : 브랜치와 마지막 커밋 메시지를 함께 보여줌
$ git branch -vv : 브랜치 목록과 트래킹 정보,진행 상황(ahead: 로컬에만 존재하는 커밋 수, behind : 서버에만 존재하는 커밋 수) 등의 정보를 보여줌. 이 명령어를 사용하기 전에 git fetch --all 먼저 실행하여 서버로부터 최신 데이터를 받아 온 후 추적 상황을 확인해야함
$ git branch <branch> : 새 브랜치 생성
$ git branch -d <branch> : 브랜치 삭제
$ git branch -D <branch> : 현재 커밋을 무시하고 삭제
$ git push origin -d <branch> : 현재 커밋을 무시하고 삭제 (지워진 브랜치를 푸시하면 원격 브랜치가 지워짐)
$ git checkout <branch> : 해당 브랜치를 체크아웃함
* 만약 커밋하지 않은 파일이 체크아웃할 브랜치와 충돌이 일어나면 브랜치 변경이 불가능함
$ git checkout -b <branch> : 새 브랜치를 생성한 후 체크아웃함
* $ git switch -c <branch>
* $ checkout vs. switch : 처음에는 파일을 체크한다는 목적으로 checkout 명령어가 사용됨. 나중에 브랜치도 체크한다는 개념도 합쳐지면서 checkout의 사전적 의미가 희미해져서 이를 명확히 하기 위해 switch 명령어가 추가됨.
(https://dev.to/swislokdev/checkout-or-switch-gj4)
$ git merge <branch2> : 현재 가리키는 브랜치를 해당 브랜치<branch2>와 병합함
*fast-forward : 머지 할 브랜치<branch2>가 가리키는 커밋이 현재 브랜치의 커밋에 기반한 것이면 현재 브랜치 포인터는 merge 과정 없이 해당 브랜치의 커밋으로 이동함
*3-way merge : 머지 하려는 두 브랜치의 마지막 커밋 2개(c4, c5)와 공통 조상(c2)을 사용하여 새 커밋(c6:merge 커밋)을 만듦
*merge conflict : 머지하려는 두 브랜치의 커밋이 같은 부분의 변경사항을 가진다면 merge가 불가능하다. 충돌이 난 부분을 수동으로 해결한 후 커밋하면 머지가 완료된다
5. 리모트 브랜치
-리모트 브랜치를 추적하는 레퍼런스를 리모트 트래킹 브랜치라 부른다.
*리모트 트래킹 브랜치를 로컬 브랜치로 checkout 하면 자동으로 트래킹 브랜치가 만들어짐. (git pull/push만 써줘도 됨)
-로컬에 존재하고 임의로 조작할 수 없다. 리모트 서버에 연결할 때마다 리모트 브랜치의 업데이트 내용에 따라 자동으로 갱신된다.
-이름은 <remote>/<branch 이름> 형식으로 되어 있다.
*예) origin/main : origin 저장소의 main 브랜치를 말함
*만약 git clone으로 어떤 리모트 저장소를 내려 받으면 깃은 자동으로 해당 데이터를 가리키는 origin/main 브랜치를 생성한다. 그리고 같은 데이터를 가리키는 로컬의 main 브랜치를 함께 생성한다. 실제 작업은 이 브랜치에서 진행된다
$ git remote : 현재 프로젝트에 등록된 리모트 저장소 이름을 확인함
*git remote -v : 저장소 이름과 url을 함께 보여줌
$ git remote show <branch> : 리모트와 로컬 브랜치에 대해서 많은 여러 정보를 확인함
$ git remote add <remote name> <url> : 현재 워킹 디렉토리에 새 리모트 저장소를 추가함
*clone 한 리모트 저장소는 자동으로 origin 이름으로 추가됨
$ git push <remote> <branch> : 해당 브랜치를 리모트 저장소에 업로드함
*$ git push origin dev(local):dev(remote) : 로컬의 dev 브랜치를 리모트 저장소의 dev 브랜치로 푸시함
*다른 사람이 먼저 push 한 내역이 있다면, 이 작업을 내 로컬에 가져와 merge를 해야 내가 push를 할 수 있다!!!
$ git push --delete <branch> : 서버의 리모트 브랜치를 삭제함
$ git fetch : 리모트 환경의 모든 브랜치 소스를 내려 받음
$ git fetch <origin> : 리모트 서버의 origin 브랜치와 로컬의 origin/main 브랜치를 동기화 함
pull 하기 전에 리모트 브랜치에 업데이트가 있는 지 확인하고 현재 로컬 브랜치와 비교(diff)할 때 주로 사용함.
*$ git merge origin/main : 현재 작업 중인 로컬 브랜치를 origin 서버 저장소의 main 브랜치에서 내려 받은 데이터와 병합함
*$ git checkout -b dev origin/main : 리모트 트래킹 브랜치에서 시작하는 새 dev 브랜치를 로컬에 생성함.
*$ git branch -u origin/main : 현재 작업 중인 브랜치가 origin의 main 브랜치를 트래킹함 (현재 브랜치가 해당 리모트 트래킹 브랜치의 로컬 트래킹 브랜치가 되는데 이를 upstream 브랜치라 함)
$ git pull : 리모트 저장소 브랜치에서 데이터를 받아와 자동으로 현재 위치한 로컬 브랜치와 merge함
*로컬의 트래킹 브랜치에서 실행된다. 트래킹 브랜치는 처음에 clone 할 때와 리모트 트래킹 브랜치에서 새 브랜치를 만들 때 생긴다 (이때 리모트 서버는 하나만 존재하고 로컬에는 트래킹 브랜치가 없어야함)
6. rebase
-단어 그대로 base를 변경한다.
-아래 그림은 experiment 브랜치를 master 브랜치에 리베이스하는 과정이다.
-experiment는 대상 브랜치인 master와 공통 조상인 c2로 이동 후, 다시 대상 브랜치가 가리키는 c3로 이동한다. 그리고 c3에서 이전의 c4를 차례로 적용한다.
-기존에 experiment 브랜치가 가리키는 커밋 c4의 조상은 c2에서 c3로 rebase 되었다. 이는 experiment 브랜치에서 변경된 사항(c4)을 master 브랜치에서 작업한 것처럼 보이게 한다.
-리베이스는 커밋 히스토리를 깨끗하게 만든다. 일을 병렬로 진행해도 마치 선형으로 수행한 것처럼 보이게 한다.
-merge와 rebase는 최종 결과물은 같지만 커밋 히스토리는 다르게 나타난다.
-리베이스는 커밋 히스토리를 수정하기 때문에 리모트 저장소에서는 사용을 자제하는 것이 좋다.
$ git rebase --abort : 리베이스 실행을 취소함
$ git rebase --continue : 충돌이 일어나면 리베이스 하려는 브랜치의 모든 커밋마다 충돌을 해결해줘야한다
*리베이스 충돌 발생 -> 수정/해결 -> git add . -> git rebase --continue -> 충돌 -> 수정/해결 -> git add . -> git rebase --continue -> ... 반복