Git이란?
Git은 분산 버전 관리 시스템(DVCS, Distributed Version Control System)으로, 여러 사람이 동시에 작업하는 프로젝트에서 중요한 파일의 변경사항을 추적하고 관리하는 데 사용됩니다.
Git을 사용하면 여러 사용자가 동시에 다양한 작업을 할 수 있으며, 이를 통해 코드의 역사를 관리하고 문제가 발생했을 때 이전 상태로 복구할 수 있습니다.
Git의 주요 특징
- 분산형 구조
- Git은 분산형 버전 관리 시스템입니다.
- 이는 각 개발자가 전체 리포지토리의 사본을 로컬에 저장하고 작업할 수 있음을 의미합니다.
- 이러한 구조는 네트워크에 의존하지 않고도 작업을 계속할 수 있도록 하며, 여러 개발자가 동시에 작업을 진행하는 것을 용이하게 합니다.
- 스냅샷 방식
- Git은 파일 변경사항을 추적하는 대신 각 커밋마다 파일 상태의 ‘스냅샷’을 취합니다.
- 이는 Git이 특정 시점의 프로젝트 상태를 쉽게 복원할 수 있음을 의미하며, 또한 이전 상태로 롤백하는 것을 간단하게 합니다.
- 내구성과 무결성
- Git은 ‘체크섬’이라는 메커니즘을 사용하여 모든 정보의 무결성을 유지합니다.
- 체크섬은 특정 정보의 고유한 해시 값을 생성하며, 이를 통해 데이터 손상이나 오류를 쉽게 감지할 수 있습니다.
- 또한, Git의 모든 작업은 로컬 디스크에 저장되므로, 데이터의 손실 위험이 매우 낮습니다.
- 브랜칭과 병합
- Git의 강력한 기능 중 하나입니다.
- 개발자는 새로운 기능 개발이나 버그 수정을 위해 쉽게 새로운 브랜치를 생성하고, 작업이 완료되면 그 브랜치를 원래 브랜치로 병합할 수 있습니다.
- 이는 다양한 작업을 동시에 진행하거나, 여러 개발자가 협업하는 것을 용이하게 합니다.
- 이력 조회와 조작
- Git은 프로젝트 이력을 조회하고 조작하는 데 강력한 도구를 제공합니다.
- git log 명령을 통해 이전 커밋을 조회할 수 있으며, git rebase 또는 git cherry-pick 같은 명령을 통해 커밋 이력을 재정렬하거나 선택적으로 적용할 수 있습니다.
Git 설치
- Git을 사용하려면 먼저 컴퓨터에 설치해야 합니다. 다양한 운영 체제에 따라 설치 방법이 다르지만, 공식 Git 홈페이지(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이 프로젝트의 메타데이터와 버전 정보를 저장하는 곳입니다.
git clone
- 기존 저장소 복제 명령어
- 원격 저장소를 로컬에 복제합니다. 이 명령어는 GitHub와 같은 원격 저장소에서 프로젝트를 시작할 때 주로 사용됩니다.
1
| git clone https://github.com/user/repo.git
|
위 명령어는 원격 저장소(‘https://github.com/user/repo.git’) 로부터 프로젝트를 복제합니다.
git add
- 스테이징 명령어
- 원하는 파일의 변경사항을 스테이징 영역에 추가하는데 사용됩니다. 이 단계에서는 아직 변경 사항이 커밋되지 않았지만, 곧 커밋될 준비가 된 상태가 됩니다.
위 명령어는 Git으로 추적하고 있는 모든 변경된 파일을 스테이징 영역에 추가합니다
git commit
- 커밋 생성 명령어
- 스테이징 영역에 있는 파일들의 변경사항을 확정하고, 이를 저장소에 기록하는 작업을 수행합니다. 각 커밋에는 고유한 ID가 부여되며, 이를 통해 특정 변경사항을 추적하거나 이전 상태로 되돌릴 수 있습니다.
1
| git commit -m "Commit message"
|
git push
- 원격 저장소에 변경사항 전송 명령어
- 로컬 저장소의 변경사항을 원격 저장소에 전송합니다. 이는 협업을 위해 다른 사용자와 작업 결과를 공유하거나, 원격 저장소를 백업 목적으로 사용할 때 유용합니다.
- 위 명령어는 현재의
master
브랜치를 origin
이라는 원격 저장소에 업로드합니다. origin
은 원격 저장소의 기본 별칭입니다.
git fetch
- 원격 저장소의 변경사항 확인 명령어
- 원격 저장소의 변경사항을 로컬에 가져오지만, 현재의 브랜치에는 자동으로 병합하지 않습니다. 이 명령어를 사용하면 원격 저장소의 최신 변경사항을 로컬에서 확인할 수 있지만, 해당 변경사항이 로컬의 현재 브랜치에 어떤 영향을 미칠지 직접 확인하고 병합할 수 있습니다.
1
| git fetch origin master
|
위 명령어는 origin
원격 저장소의 master
브랜치의 최신 변경사항을 가져옵니다. 하지만 이 변경사항은 현재 브랜치에 자동으로 병합되지 않습니다.
git merge
- 브랜치 병합 명령어
git merge
명령어를 사용하면 현재 브랜치에 다른 브랜치의 변경사항을 병합할 수 있습니다. git fetch
를 통해 가져온 원격 저장소의 변경사항을 현재 브랜치에 병합하려면 이 명령어를 사용하면 됩니다.
1
| git merge origin/master
|
위 명령어는 origin/master
브랜치의 변경사항을 현재 브랜치에 병합합니다. 이는 git fetch
명령어로 가져온 원격 저장소의 변경사항을 병합하는 경우에 사용됩니다.
git pull
- 원격 저장소의 변경사항 받아오기 명령어
- 원격 저장소의 최신 변경사항을 로컬 저장소에 받아오는 작업을 수행합니다. 다른 사용자가 원격 저장소에 새로운 커밋을 푸시한 경우, 이 명령어를 통해 그 변경사항을 로컬 저장소에 반영할 수 있습니다.
위 명령어는 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
라는 이름의 새로운 브랜치를 만들려면 다음과 같이 실행됩니다
위 명령어 수행 시 new-feature
라는 이름의 새로운 브랜치가 생성되고, 현재 브랜치의 상태를 그대로 복사해옵니다.
- 새로 생성한 브랜치로 전환하기 위해선
git checkout
명령어를 사용합니다
1
| git checkout new-feature
|
브랜치의 병합
- 브랜치에서의 작업을 완료하고 그 변경사항을
master
브랜치에 반영하려면, 브랜치를 병합해야 합니다. - 브랜치를 병합하기 위해선 먼저 대상 브랜치로 이동해야 합니다.
- 일반적으로
master
브랜치에 다른 브랜치를 병합하므로, 먼저 master
브랜치로 이동합니다
그런 다음 git merge
명령어를 사용하여 브랜치를 병합한다. 예시에서는 new-feature
브랜치를 병합하므로, 다음과 같이 실행한다
이렇게 하면 new-feature
브랜치의 모든 변경사항이 master
브랜치에 반영됩니다.
Git Checkout
Checkout이란?
git checkout
은 Git에서 굉장히 중요하고 다양한 용도로 사용되는 명령어로 다음 두 가지 주요한 기능에서 사용됩니다.- 브랜치를 전환(switching branches)
- 파일을 특정 커밋 상태로 복원(checking out files)
브랜치를 전환(switching branches)
git checkout
명령어는 브랜치 간의 전환을 수행하는 데 사용됩니다.- 예를 들어, 현재
master
브랜치에서 작업하고 있고 feature
브랜치로 전환하려면, 다음과 같이 git checkout
명령어를 사용할 수 있습니다.
위 명령어를 실행하면, 작업 디렉토리의 파일들은 feature
브랜치의 최신 커밋 상태로 변경됩니다.
파일을 특정 커밋 상태로 복원(checking out files)
git checkout
명령어는 특정 파일을 이전 커밋의 상태로 되돌리는 데도 사용됩니다.- 예를 들어,
file.txt
를 가장 최근의 커밋 상태로 되돌리려면 다음과 같이 git checkout
명령어를 사용할 수 있습니다.
1
| git checkout -- file.txt
|
위 명령어를 실행하면, file.txt
는 가장 최근의 커밋 상태로 되돌아갑니다. 이렇게 하면 워킹 디렉토리의 변경 사항은 모두 사라지므로 주의해야 합니다.
- 또한,
git checkout
명령어는 새로운 브랜치를 생성하면서 동시에 그 브랜치로 전환하는 기능도 제공합니다. -b
옵션을 사용하여 이를 수행할 수 있습니다.
1
| 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를 인위로 발생시켜보고 해결하는 과정을 살펴보자
브랜치를 병합하는 과정에서 Conflict 발생 예시
- 새로운 Git 저장소를 만들어본다.
1
2
3
| mkdir test-repo
cd test-repo
git init
|
- 새로운 파일을 만들어 첫 번째 커밋을 생성한다.
1
2
3
| echo "Hello, Git" > hello.txt
git add hello.txt
git commit -m "Initial commit"
|
- 새로운 브랜치를 만들고 체크아웃하여 작업한다.
1
2
| git branch new-branch
git checkout new-branch
|
- 새 브랜치에서 파일을 수정하고 커밋한다.
1
2
3
| echo "Hello, new branch" >> hello.txt
git add hello.txt
git commit -m "Update from new branch"
|
master
브랜치로 돌아가서 동일한 파일을 다른 방식으로 수정한다.
1
2
3
4
| git checkout master
echo "Hello, master branch" >> hello.txt
git add hello.txt
git commit -m "Update from master branch"
|
- 이제 두 브랜치를 병합하려고 시도하면, 두 브랜치에서 동일한 파일이 수정되었기 때문에 충돌이 발생한다.
- 이 때
git status
를 실행하면 충돌이 발생한 파일을 확인할 수 있다.
- 충돌이 발생한 파일을 열어보면 Git에서 어떤 부분이 충돌했는지 표시해주는 것을 볼 수 있다. 이 부분을 원하는 형태로 수정하고 다시 커밋하면 충돌이 해결된다.
1
2
3
| # hello.txt를 열어서 수정
git add hello.txt
git commit -m "Resolve conflict"
|
원격 Repo 사용 시 동일 파일에 대한 변경사항 적용으로 인한 Conflict 발생 예시
A의 컴퓨터에서
- 먼저 A가
file.txt
를 수정하고 이 변경사항을 커밋하고 푸시한다
1
2
3
4
| echo "A의 변경사항" > file.txt
git add .
git commit -m "A의 변경사항"
git push origin master
|
B의 컴퓨터에서
- 동시에 B도
file.txt
를 수정했다고 가정한다. B는 그의 변경사항을 커밋하려고 한다
1
2
3
| echo "B의 변경사항" > file.txt
git add .
git commit -m "B의 변경사항"
|
- 그러나 B가 이제 이 변경사항을 푸시하려고 하면, 이미 A의 변경사항이 GitHub에 반영되어 있기 때문에 충돌이 발생한다
리모트 버전이 B의 로컬 버전보다 앞서기 때문에 이 명령은 거부될 것입니다
- B는 이 충돌을 해결하기 위해 먼저 최신 변경 사항을 pull해야 한다
1
| 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이 가능하다
- 이제
file.txt
를 열면, Git은 충돌하는 부분을 표시한다. B는 이 파일을 수정하여 충돌을 해결하고 다시 커밋해야 한다
1
2
3
| # file.txt를 열어서 수정
git add .
git commit -m "B의 충돌 해결"
|
- 이제 B는 변경사항을 다시 푸시할 수 있다
이 예시는 같은 컴퓨터에서 A, B의 별도의 디렉토리를 생성하여 테스트 가능하다
번외
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
포인터는 그 새로운 커밋을 가리키게 됩니다.
Comments powered by Disqus.