owned this note
owned this note
Published
Linked with GitHub
# Git

- Git은 분산 버전 관리 시스템(DVCS, Distributed Version Control System)이다.
- 여러 사람이 동시에 작업하는 프로젝트에서 중요한 파일의 변경사항을 추적하고 관리하는데 사용된다
- 여러 사용자가 동시에 다양한 작업을 할 수 있으며, 이를 통해 코드의 역사를 관리하고 문제가 발생했을 때 이전 상태로 복구할 수 있다.
## Git의 주요 특징
- 분산형 구조
- Git은 분산형 버전 관리 시스템이다.
- 이는 각 개발자가 전체 리포지토리의 사본을 로컬에 저장하고 작업할 수 있음을 의미한다.
- 이러한 구조는 네트워크에 의존하지 않고도 작업을 계속할 수 있도록 하며, 여러 개발자가 동시에 작업을 진행하는 것을 용이하게 한다.
- 스냅샷 방식
- Git은 파일 변경사항을 추적하는 대신 각 커밋마다 파일 상태의 '스냅샷'을 취한다.
- 이는 Git이 특정 시점의 프로젝트 상태를 쉽게 복원할 수 있음을 의미하며, 또한 이전 상태로 롤백하는 것을 간단하게 한다.
- 내구성과 무결성
- Git은 '체크섬'이라는 메커니즘을 사용하여 모든 정보의 무결성을 유지한다.
- 체크섬은 특정 정보의 고유한 해시 값을 생성하며, 이를 통해 데이터 손상이나 오류를 쉽게 감지할 수 있다.
- 또한, Git의 모든 작업은 로컬 디스크에 저장되므로, 데이터의 손실 위험이 매우 낮다.
- 브랜칭과 병합
- Git의 강력한 기능 중 하나.
- 개발자는 새로운 기능 개발이나 버그 수정을 위해 쉽게 새로운 브랜치를 생성하고, 작업이 완료되면 그 브랜치를 원래 브랜치로 병합할 수 있다.
- 이는 다양한 작업을 동시에 진행하거나, 여러 개발자가 협업하는 것을 용이하게 한다.$$
- 이력 조회와 조작
- Git은 프로젝트 이력을 조회하고 조작하는 데 강력한 도구를 제공한다.
- git log 명령을 통해 이전 커밋을 조회할 수 있으며, git rebase 또는 git cherry-pick 같은 명령을 통해 커밋 이력을 재정렬하거나 선택적으로 적용할 수 있다.
## Git 설치
- Git을 사용하려면 먼저 컴퓨터에 설치해야 한다. 다양한 운영 체제에 따라 설치 방법이 다르지만, 공식 Git 홈페이지([https://git-scm.com/](https://git-scm.com/))에서 자세한 안내를 찾을 수 있다.
## Git의 작업 영역

- Git의 기본 작업 영역은 크게 세 개의 영역으로 나뉜다
- 작업 디렉토리(Working Directory)
- 스테이징 영역(Staging Area, 또는 Index)
- 리포지토리(Repository)
### 작업 디렉토리(Working Directory)
- 사용자가 실제로 파일을 만들거나 수정하는 곳.
- 이곳에서 진행한 변경사항은 Git의 추적 대상이 되지 않고, Git은 이곳의 변경사항을 'untracked' 또는 'modified' 상태라고 인식한다.
- 사용자가 어떤 변경사항을 Git에게 알리고 싶다면, 이 변경사항을 스테이징 영역으로 이동시켜야 한다.
### 스테이징 영역(Staging Area, 또는 Index)
- Git이 관리하는, 다음 커밋에 포함될 변경사항들을 보관하는 곳.
- 사용자가 작업 디렉토리에서 파일을 수정한 후, 그 변경사항을 커밋하기 위해 준비하려면 `git add` 명령어를 사용하여 해당 변경사항을 스테이징 영역으로 옮긴다.
- 이 작업을 통해 변경사항이 커밋할 준비가 된다.
### 리포지토리(Repository)
- Git이 프로젝트의 변경 이력을 저장하는 곳.
- 리포지토리는 작업을 수행하는 개발자의 로컬 시스템에 위치할 수도 있고, 원격 서버에 위치할 수도 있다.
- 로컬 리포지토리(Local Repository)
- 개발자의 컴퓨터에 있는 저장소로, 개발자가 직접 작업하는 공간.
- 로컬 리포지토리에는 작업 디렉토리, 스테이징 영역, 그리고 변경 이력이 포함되어 있다.
- 스테이징 영역에서 준비한 변경사항을 실제로 커밋하려면 `git commit` 명령어를 사용한다. 이 명령어는 변경사항을 로컬 리포지토리에 저장한다.
- 원격 리포지토리(Remote Repository)
- 원격 서버에 위치한 저장소로, 팀원 간에 공유되는 공간.
- 원격 리포지토리는 여러 사람이 함께 작업할 수 있도록 해주며, 각자의 작업을 합칠 수 있는 중앙 집중식 저장소의 역할을 한다.
- 로컬에서 작업한 내용을 원격 리포지토리에 공유하려면 `git push` 명령어를 사용하며, 원격 리포지토리의 최신 변경사항을 로컬로 가져오려면 `git pull` 또는 `git fetch` 명령어를 사용한다.
## Git 기본 명령어
### git init
- Git 저장소 초기화 명령어
- 새로운 Git 저장소를 생성한다. 현재 디렉토리에 `.git`라는 하위 디렉토리를 생성하는데, 이 디렉토리는 Git이 프로젝트의 메타데이터와 버전 정보를 저장하는 곳이다.
```bash
git init
```
### git clone
- 기존 저장소 복제 명령어
- 원격 저장소를 로컬에 복제한다. 이 명령어는 GitHub와 같은 원격 저장소에서 프로젝트를 시작할 때 주로 사용된다.
```bash
git clone https://github.com/user/repo.git
```
> 위 명령어는 원격 저장소('https://github.com/user/repo.git')로부터 프로젝트를 복제한다.
### git add
- 스테이징 명령어
- 원하는 파일의 변경사항을 스테이징 영역에 추가하는데 사용된다. 이 단계에서는 아직 변경 사항이 커밋되지 않았지만, 곧 커밋될 준비가 된 상태이다.
```bash
git add .
```
> 위 명령어는 Git으로 추적하고 있는 모든 변경된 파일을 스테이징 영역에 추가한다
### git commit
- 커밋 생성 명령어
- 스테이징 영역에 있는 파일들의 변경사항을 확정하고, 이를 저장소에 기록하는 작업을 수행한다. 각 커밋에는 고유한 ID가 부여되며, 이를 통해 특정 변경사항을 추적하거나 이전 상태로 되돌릴 수 있다.
```bash
git commit -m "Commit message"
```
### git push
- 원격 저장소에 변경사항 전송 명령어
- 로컬 저장소의 변경사항을 원격 저장소에 전송한다. 이는 협업을 위해 다른 사용자와 작업 결과를 공유하거나, 원격 저장소를 백업 목적으로 사용할 때 유용하다.
```bash
git push origin master
```
> - 위 명령어는 현재의 `master` 브랜치를 `origin`이라는 원격 저장소에 업로드한다.
> - `origin`은 원격 저장소의 기본 별칭이다.
### git fetch
- 원격 저장소의 변경사항 확인 명령어
- 원격 저장소의 변경사항을 로컬에 가져오지만, 현재의 브랜치에는 자동으로 병합하지 않는다. 이 명령어를 사용하면 원격 저장소의 최신 변경사항을 로컬에서 확인할 수 있지만, 해당 변경사항이 로컬의 현재 브랜치에 어떤 영향을 미칠지 직접 확인하고 병합할 수 있다.
```bash
git fetch origin master
```
> 위 명령어는 `origin` 원격 저장소의 `master` 브랜치의 최신 변경사항을 가져온다. 하지만 이 변경사항은 현재 브랜치에 자동으로 병합되지 않는다.
### git merge
- 브랜치 병합 명령어
- `git merge` 명령어를 사용하면 현재 브랜치에 다른 브랜치의 변경사항을 병합할 수 있다. `git fetch`를 통해 가져온 원격 저장소의 변경사항을 현재 브랜치에 병합하려면 이 명령어를 사용하면 된다.
```bash
git merge origin/master
```
> 위 명령어는 `origin/master` 브랜치의 변경사항을 현재 브랜치에 병합한다. 이는 `git fetch` 명령어로 가져온 원격 저장소의 변경사항을 병합하는 경우에 사용된다.
### git pull
- 원격 저장소의 변경사항 받아오기 명령어
- 원격 저장소의 최신 변경사항을 로컬 저장소에 받아오는 작업을 수행한다. 다른 사용자가 원격 저장소에 새로운 커밋을 푸시한 경우, 이 명령어를 통해 그 변경사항을 로컬 저장소에 반영할 수 있다.
```bash
git pull origin master
```
> 위 명령어는 `origin` 원격 저장소의 `master` 브랜치에서 최신의 변경사항을 가져와 현재의 브랜치에 병합한다.
> `git pull` 명령어는 사실 `git fetch`와 `git merge`의 조합과 같다고 볼 수 있다. `git pull` 명령어를 실행하면 Git은 원격 저장소의 변경사항을 가져오고(`git fetch`), 그런 다음 가져온 변경사항을 현재의 브랜치에 병합한다(`git merge`).
## Git 브랜치(Branch)
### Git 브랜치란?
- 특정 작업을 독립적으로 수행하기 위한 도구다
- 프로젝트에서 다른 부분을 독립적으로 변경하거나 새로운 기능을 추가하고 싶을 때, 그 작업을 위한 새로운 브랜치를 생성할 수 있다.
- 기본적으로 모든 Git 저장소는 `master`라는 기본 브랜치를 가지고 있다.
- `master` 브랜치는 일반적으로 프로젝트의 "정식" 버전을 의미하며, 여기에서 새로운 브랜치를 만들어 작업을 진행하고 다시 `master`로 병합하는 방식으로 작업이 진행된다.
### 브랜치의 생성
새로운 브랜치를 생성하는 것은 간단하다. `git branch` 명령어에 브랜치 이름을 추가하면 된다.
예를 들어, `new-feature`라는 이름의 새로운 브랜치를 만들려면 다음과 같이 실행된다
```bash
git branch new-feature
```
> 위 명령어 수행 시 `new-feature`라는 이름의 새로운 브랜치가 생성되고, 현재 브랜치의 상태를 그대로 복사해온다.
- 새로 생성한 브랜치로 전환하기 위해선 `git checkout` 명령어를 사용한다
```bash
git checkout new-feature
```
### 브랜치의 병합
- 브랜치에서의 작업을 완료하고 그 변경사항을 `master` 브랜치에 반영하려면, 브랜치를 병합해야 한다.
- 브랜치를 병합하기 위해선 먼저 대상 브랜치로 이동해야 한다.
- 일반적으로 `master` 브랜치에 다른 브랜치를 병합하므로, 먼저 `master` 브랜치로 이동한다
```bash
git checkout master
```
그런 다음 `git merge` 명령어를 사용하여 브랜치를 병합한다.
예시에서는 `new-feature` 브랜치를 병합하므로, 다음과 같이 실행한다
```bash
git merge new-feature
```
이렇게 하면 `new-feature` 브랜치의 모든 변경사항이 `master` 브랜치에 반영된다.
## Git Checkout
### Checkout이란?
- `git checkout`은 Git에서 굉장히 중요하고 다양한 용도로 사용되는 명령어로 다음 두 가지 주요한 기능에서 사용된다.
1. 브랜치를 전환(switching branches)
2. 파일을 특정 커밋 상태로 복원(checking out files)
#### 브랜치를 전환(switching branches)
- `git checkout` 명령어는 브랜치 간의 전환을 수행하는 데 사용된다.
- 예를 들어, 현재 `master` 브랜치에서 작업하고 있고 `feature` 브랜치로 전환하려면, 다음과 같이 `git checkout` 명령어를 사용할 수 있다.
```bash
git checkout feature
```
> 위 명령어를 실행하면, 작업 디렉토리의 파일들은 `feature` 브랜치의 최신 커밋 상태로 변경된다.
#### 파일을 특정 커밋 상태로 복원(checking out files)
- `git checkout` 명령어는 특정 파일을 이전 커밋의 상태로 되돌리는 데도 사용된다.
- 예를 들어, `file.txt`를 가장 최근의 커밋 상태로 되돌리려면 다음과 같이 `git checkout` 명령어를 사용할 수 있다.
```bash
git checkout -- file.txt
```
> 위 명령어를 실행하면, `file.txt`는 가장 최근의 커밋 상태로 되돌아간다. 이렇게 하면 워킹 디렉토리의 변경 사항은 모두 사라지므로 주의해야 한다.
- 또한, `git checkout` 명령어는 새로운 브랜치를 생성하면서 동시에 그 브랜치로 전환하는 기능도 제공한다. `-b` 옵션을 사용하여 이를 수행할 수 있다.
```bash
git checkout -b new-branch
```
> 위 명령어는 `new-branch`라는 이름의 새 브랜치를 생성하고, 바로 그 브랜치로 전환한다.
**`git checkout` 명령어는 매우 강력하며 다양한 상황에 유용하게 사용할 수 있다. 다만, 워킹 디렉토리의 파일들을 변경하는 명령어이므로 사용할 때 주의해야 한다.**
## Git 충돌(Conflict)
- Git에서 '충돌'은 두 개 이상의 변경 사항이 서로 충돌하는 경우를 의미한다.
- 일반적으로 두 개의 브랜치에서 동일한 파일의 동일한 부분을 변경하고 이를 병합하려고 할 때 발생한다.
- Git은 자동으로 브랜치를 병합할 수 없으므로, 이런 상황을 '충돌'이 발생했다고 한다.
### Conflict 해결
충돌이 발생하면, 먼저 `git status`를 실행하여 충돌이 발생한 파일을 확인할 수 있다. 그 후 해당 파일을 열어 보면, Git은 충돌이 발생한 부분을 특별한 방식으로 표시해준다.
```
<<<<<<< HEAD
변경사항1
=======
변경사항2
>>>>>>> branch-name
```
- 이 표시에서 `<<<<<<< HEAD`와 `=======` 사이에 있는 내용은 현재 브랜치(HEAD)에서의 변경 사항을, `=======`와 `>>>>>>> branch-name` 사이에 있는 내용은 병합하려는 브랜치에서의 변경 사항을 나타낸다.
- 충돌을 해결하려면, 이 충돌 표시를 포함한 모든 코드를 적절한 최종 코드로 대체해야 한다.
- 이는 일반적으로 두 변경 사항 중 하나를 선택하거나, 두 변경 사항을 적절히 조합하는 방식으로 수행된다.
- 충돌이 해결되면, `git add`를 사용하여 파일을 스테이징하고, `git commit`을 사용하여 병합 과정을 완료한다.
### 충돌 방지
- **병합 전에 브랜치 업데이트하기**: `git pull` 명령어를 사용하여 최신 변경사항을 가져온 후에 병합하는 것이 좋다.
- **작은 단위로 자주 커밋하고 푸시하기**: 작은 단위로 자주 커밋하고 푸시하면 변경사항을 빠르게 공유할 수 있고, 충돌이 발생할 가능성을 줄일 수 있다.
- **브랜치 전략 사용하기**: Git Flow, GitHub Flow 등의 브랜치 전략을 사용하면 브랜치 간의 작업을 분리하고, 충돌을 관리하는 데 도움이 될 수 있다.
### Git Conflict를 인위로 발생시켜보고 해결하는 과정을 살펴보자
<details>
<summary>브랜치를 병합하는 과정에서 Conflict 발생 예시</summary>
<div markdown="1">
1. 새로운 Git 저장소를 만들어본다.
```bash
mkdir test-repo
cd test-repo
git init
```
2. 새로운 파일을 만들어 첫 번째 커밋을 생성한다.
```bash
echo "Hello, Git" > hello.txt
git add hello.txt
git commit -m "Initial commit"
```
3. 새로운 브랜치를 만들고 체크아웃하여 작업한다.
```bash
git branch new-branch
git checkout new-branch
```
4. 새 브랜치에서 파일을 수정하고 커밋한다.
```bash
echo "Hello, new branch" >> hello.txt
git add hello.txt
git commit -m "Update from new branch"
```
5. `master` 브랜치로 돌아가서 동일한 파일을 다른 방식으로 수정한다.
```bash
git checkout master
echo "Hello, master branch" >> hello.txt
git add hello.txt
git commit -m "Update from master branch"
```
6. 이제 두 브랜치를 병합하려고 시도하면, 두 브랜치에서 동일한 파일이 수정되었기 때문에 충돌이 발생한다.
```bash
git merge new-branch
```
7. 이 때 `git status`를 실행하면 충돌이 발생한 파일을 확인할 수 있다.
```bash
git status
```
8. 충돌이 발생한 파일을 열어보면 Git에서 어떤 부분이 충돌했는지 표시해주는 것을 볼 수 있다. 이 부분을 원하는 형태로 수정하고 다시 커밋하면 충돌이 해결된다.
```bash
## hello.txt을 열어서 수정
git add hello.txt
git commit -m "Resolve conflict"
```
</div>
</details>
<details>
<summary>원격 Repo 사용 시 동일 파일에 대한 변경사항 적용으로 인한 Conflict 발생 예시</summary>
<div markdown="1">
**A의 컴퓨터에서**
1. 먼저 A가 `file.txt`를 수정하고 이 변경사항을 커밋하고 푸시한다
```bash
echo "A의 변경사항" > file.txt
git add .
git commit -m "A의 변경사항"
git push origin master
```
**B의 컴퓨터에서**
1. 동시에 B도 `file.txt`를 수정했다고 가정한다. B는 그의 변경사항을 커밋하려고 한다
```bash
echo "B의 변경사항" > file.txt
git add .
git commit -m "B의 변경사항"
```
2. 그러나 B가 이제 이 변경사항을 푸시하려고 하면, 이미 A의 변경사항이 GitHub에 반영되어 있기 때문에 충돌이 발생한다
```bash
git push origin master
```
> 리모트 버전이 B의 로컬 버전보다 앞서기 때문에 이 명령은 거부될 것이다
3. B는 이 충돌을 해결하기 위해 먼저 최신 변경 사항을 pull해야 한다
```bash
git pull --no-rebase origin master
```
> - `--no-rebase` 옵션 사용 이유 : `git pull` 명령어는 기본적으로 `fetch` 와 `merge` 를 한번에 수행하는 명령어인데, `merge` 를 어떻게 진행할지에 대한 default 지시를 따로 설정하지 않을 시에 명령이 수행되지 않을 수 있다.
> - `pull.rebase` 설정을 전역적으로 지정하도록 지시하는 명령어 : `git config --global pull.rebase false`
> - 이 설정을 적용한 경우에는 `--no-rebase` 옵션 없이 pull이 가능하다
4. 이제 `file.txt`를 열면, Git은 충돌하는 부분을 표시한다. B는 이 파일을 수정하여 충돌을 해결하고 다시 커밋해야 한다
```bash
## file.txt을 열어서 수정
git add .
git commit -m "B의 충돌 해결"
```
5. 이제 B는 변경사항을 다시 푸시할 수 있다
```bash
git push origin master
```
> 이 예시는 같은 컴퓨터에서 A, B의 별도의 디렉토리를 생성하여 테스트 가능하다
</div>
</details>
## 번외
### Git의 포인터
Git에서는 여러가지 유형의 포인터를 사용하여 특정 커밋을 참조한다.
#### HEAD
- 현재 작업 중인 커밋을 가리킨다. - 보통 이는 작업 중인 브랜치의 가장 최근 커밋을 가리킨다.
> 예를 들어 `git checkout` 명령을 통해 다른 커밋이나 브랜치로 이동하면, `HEAD`는 그 커밋이나 브랜치를 가리키게 된다
#### 브랜치 포인터
- 각 브랜치는 그 브랜치의 가장 최근 커밋을 가리키는 포인터를 가진다. - 예를 들어, `master` 브랜치는 `master` 브랜치의 가장 최근 커밋을 가리키는 포인터를 의미한다.
> 예를 들어 `git commit` 명령을 통해 새로운 커밋을 만들면, `master` 브랜치 포인터는 그 새로운 커밋을 가리키게 된다.
#### 태그 포인터
- Git의 태그는 특정 커밋을 가리키는 포인터. - 보통 이는 중요한 커밋에 대한 참조로 사용된다.
> 예를 들어, `v1.0`라는 태그를 만들면, 그 태그는 생성될 때 지정된 커밋을 항상 가리키게 된다.
#### 상대 참조
- Git에서는 `HEAD~1`(HEAD의 부모 커밋), `HEAD~2`(HEAD의 부모의 부모 커밋), `HEAD^`(HEAD의 부모 커밋), 등과 같은 방식으로 상대 참조를 사용할 수 있다.
- 이는 현재 `HEAD`를 기준으로 특정 커밋을 가리키는 데 사용된다.
> 예를 들어, `HEAD~1`는 현재 커밋의 직전 커밋을, `HEAD~2`는 현재 커밋의 2단계 전 커밋을 가리킨다.
#### 리모트 트래킹 브랜치 포인터
- 원격 리포지토리의 브랜치를 로컬에서 추적할 때 사용되는 포인터.
> 예를 들어, `origin/master`는 원격 리포지토리 `origin`의 `master` 브랜치를 가리킨다. 만약 원격 리포지토리에 새로운 커밋이 푸시되면, `origin/master` 포인터는 그 새로운 커밋을 가리키게 된다.
---