본문 바로가기

Dev_Tools/Git

Git

 

 

1. 깃 기초

https://git-scm.com/book/ko/v2/시작하기-Git-기초

-깃은 VCS(Version Control System)의 한 종류로, Distributed Version Control System(DVCS)이다.

-다른 VCS는 시간순으로 파일을 관리하지만 깃은 상태 변화에 따라 관리한다. 깃은 파일이 존재하는 그 순간을 중요하게 여기며, 데이터를 스냅샷의 스트림처럼 취급한다.

-프로젝트의 모든 히스토리는 로컬 디스크 내 깃 디렉토리에서 관리되며 조회가 매우 빠르다.

-관리되는 파일의 상태에는 ModifiedStaged, 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 과정 없이 해당 브랜치의 커밋으로 이동함

master merge hotfix

    *3-way merge : 머지 하려는 두 브랜치의 마지막 커밋 2개(c4, c5)와 공통 조상(c2)을 사용하여 새 커밋(c6:merge 커밋)을 만듦

master merge iss53

    *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 브랜치에서 작업한 것처럼 보이게 한다.

 

experiment rebase master

-리베이스는 커밋 히스토리를 깨끗하게 만든다. 일을 병렬로 진행해도 마치 선형으로 수행한 것처럼 보이게 한다.

-merge와 rebase는 최종 결과물은 같지만 커밋 히스토리는 다르게 나타난다.

-리베이스는 커밋 히스토리를 수정하기 때문에 리모트 저장소에서는 사용을 자제하는 것이 좋다.

 

$ git rebase --abort : 리베이스 실행을 취소함

$ git rebase --continue : 충돌이 일어나면 리베이스 하려는 브랜치의 모든 커밋마다 충돌을 해결해줘야한다

    *리베이스 충돌 발생 -> 수정/해결 -> git add . -> git rebase --continue -> 충돌 -> 수정/해결 -> git add . -> git rebase --continue -> ... 반복