# Git 컨셉과 명령어 동작

* [**Git이란?**](#git)
* [**Git의 구성요소**](#git-1)
* [**Git 명령어별 동작**](#git-2)
* [**Git 명령어 사용 사례**](#git-4)



## Git이란?

> 버전 관리 시스템은 파일 변화를 시간에 따라 기록했다가 나중에 특정 시점의 버전을 다시 꺼내올 수 있는 시스템

### 저장소(서버) 위치

`로컬 버전 관리`: RCS, 변경되는 부분만을 관리

`중앙집중식 버전 관리`: 개인별 Snapshot, 별도의 중앙 서버(ex. CVS, Subversion)

`분산식 버전 관리`: 전체 정보 저장, Cloud 사용(Github) (ex. Git)



### 저장 방식

`델타 기반 관리`:  파일의 변경 데이터를 관리 (이미지 참조)

<figure><img src="https://2477703702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F0zNEVFEcc7Tv0qxrOi2Y%2Fuploads%2FnpQWvPKQ8e1dWoDoZhM6%2Fdeltas.png?alt=media&#x26;token=4726331d-b886-480a-8553-230247c23a23" alt=""><figcaption></figcaption></figure>



`스냅샷의 스트림처럼 관리`: 시간순으로 프로젝트의 스냅샷을 저장 (이미지 참조)

<figure><img src="https://2477703702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F0zNEVFEcc7Tv0qxrOi2Y%2Fuploads%2FMDXoHE5aZg9Y3NdrEClN%2Fsnapshots.png?alt=media&#x26;token=0ccfddeb-5cac-4d03-82e8-1952a5d91bd2" alt=""><figcaption></figcaption></figure>



{% hint style="info" %}
> 스냅샷이 너무 무거워지지않나요?

이전 상태의 파일에 대한 링크만 저장하므로 가벼움
{% endhint %}



## GIt 구성요소

3가지 상태로 구성되어있다.

`Committed`: 로컬에 저장된 상태

`Modified`: 수정하였지만 로컬 데이터에 저장하지 않은 상태

`Staged`: 수정한 파일을 커밋할 것이라 표시한 상태



### Git의 단계

3가지 단계가 있고 위의 상태와 1:1 매핑된다.

`Git Directory(Repository)` = Committed

* Git을 clone하거나 init할 때 만들어진다

`Working Tree` = Modified

* 특정 버전을 checkout(Git Directory안의 파일)

`Staging Area` = Staged

* index라고도 하며 커밋할 내용을 표시한 파일

## Git 명령어별 동작

`git init`: .git이라는 디렉토리를 만든다.

```console
// .git 내부구조
config 
description
HEAD -> 현재 checkout한 브랜치
hooks/
info/
objects/ -> 모든 컨텐츠를 저장
refs/ -> 커밋 개체의 포인터(브랜치, 태그, 리모트) 저장
index -> staging area의 정보를 저장
```



`git reset`

<figure><img src="https://2477703702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F0zNEVFEcc7Tv0qxrOi2Y%2Fuploads%2FK3mO4NeksOiZnVrt2xr4%2Freset-start.png?alt=media&#x26;token=644948d8-481c-4ce3-b51d-1edc28ff5206" alt=""><figcaption></figcaption></figure>

1. 가리키는 커밋(HEAD)를 되돌린다. (reset --soft는 여기까지)
2. Index를 현재 커밋이 가리키는 스냅샷으로 업데이트 (reset --mixed는 여기까지, reset의 기본 옵션)
3. Working Directory를 index 상태로 업데이트(reset --hard는 여기까지)

> checkout는 reset --hard + Working Directory의 안전을 보장



`git rebase`

`Merge`는 두 브랜치의 마지막 커밋 2개와 공통 조상의 커밋으로 3-way merge를 하는 것이 일반적이다.

<figure><img src="https://2477703702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F0zNEVFEcc7Tv0qxrOi2Y%2Fuploads%2FCckKXhw4g4NMa0Mz3mSO%2Fbasic-rebase-2.png?alt=media&#x26;token=dc68c2f0-bb80-4dc1-bafa-76cfaf5a277d" alt=""><figcaption></figcaption></figure>

`Rebase`는 공통 커밋(조상)으로 이동하고 지금까지의 커밋을 임시로 저장해둔 다음 Rebase할 브랜치에 차례대로 저장하여 선형을 만든다.

<figure><img src="https://2477703702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F0zNEVFEcc7Tv0qxrOi2Y%2Fuploads%2FnzlvSvY4bI5BRPXJYk9N%2Fbasic-rebase-4.png?alt=media&#x26;token=5c01455e-6f45-4d92-bb53-842952b744be" alt=""><figcaption></figcaption></figure>



## Git 명령어 사용 사례

### 커밋 메세지 수정

커밋 자체가 수정되면서 추가로 수정사항을 밀어넣게 되며 해시값이 바뀐다.

```console
git commit --amend
```



### Merge 리셋 <a href="#undoing_merges" id="undoing_merges"></a>

1. 로컬은 커밋을 옮기는 방법을 사용한다

```
git reset --hard HEAD~
```

2. 원격은 커밋을 되돌린다.

```console
git revert -m 1 HEAD
```



### Add/Commit/Push 취소

#### Add 취소

* Staging -> UnStaging

```javascript
git reset HEAD
```



#### Commit 취소

* 마지막 커밋을 취소(로컬)

```
git reset HEAD^
```



#### Push 취소

* 기록을 남기기 위해서는 `revert` **(권장)**
* 강제로 덮어쓰기는 `reset`



## Reference

* [https://www.git-scm.com/docs](https://www.git-scm.com/docs)
* [https://www.atlassian.com/ko/git/tutorials/setting-up-a-repository/git-init](https://www.atlassian.com/ko/git/tutorials/setting-up-a-repository/git-init)
* [https://gmlwjd9405.github.io/2018/05/25/git-add-cancle.html](https://gmlwjd9405.github.io/2018/05/25/git-add-cancle.html)
