Git 기초
Chapter 1. Git이란?#
Git은 분산형 버전 관리 시스템이다.
버전 관리 시스템#
일반적으로 개발자가 시도해 볼 수 있는 가장 기초적인 버전 관리 방식은 아래와 같다.
이 방법은 가장 단순하고 간편하지만, 진행 상황이 중구난방이라 잘 알아보기도 어려울 뿐더러, 중간에 무언가 잘못되기 쉽다. 예를 들어, 디렉토리를 잘못 복사하거나, 이름을 잘못 지정하거나, 심지어 실수로 디렉토리를 통채로 지워버릴 수도 있다.
이를 극복하기 위해 버전 관리 시스템(VCS - Version Control System)이 사용된다. 버전 관리 시스템은 버전 데이터베이스에 특정 버전의 파일들의 변경 정보를 기록하여 필요할 때 해당 버전의 파일로 복구할 수 있도록 하는 기능을 지원한다.
VCS에는 로컬 VCS(Local VCS)와 중앙집중형 VCS(CVCS - Centralized VCS), 분산형 VCS (DVCS - Distributed Version Control System)이 있다. 로컬 VCS는 버전 정보를 로컬 컴퓨터에 저장한다. CVCS와 DVCS는 프로젝트를 다른 개발자와 협업하여 프로젝트를 진행할 수 있도록 해 준다. CVCS는 모든 버전 정보를 중앙 서버에서 관리하고 개별 사용자는 특정 버전의 파일만 받아서 작업을 하는 방식으로, 서버에 문제가 생기면 모든 사용자가 작업을 할 수 없고 서버 파일이 손상되면 프로젝트의 모든 버전 기록을 잃을 수 있다는 치명적인 단점이 있다. 이와 달리, VCCS는 개별 사용자가 중앙 서버에 저장된 저장소를 전체 히스토리와 함께 통째로 복제해 오는 방식이다. Git이 이 방식의 버전 관리 시스템에 해당한다.
Git에서는 버전별 스냅샷, 즉 위 그림의 Version 1
, Version 2
등에 해당하는 체크포인트를 커밋(Commit)
이라고 한다. 이 용어는 매우 중요하니 꼭 기억하고 있어야 한다.
리포지토리(저장소)를 저장할 중앙 서버는 개인이 직접 구축할 수도 있고, 외부 호스팅 서비스를 이용할 수도 있다. 가장 대표적인 Git 저장소 호스팅 서비스 제공자가 바로 Github이다. Github는 Git 저장소 제공 이외에도 효율적인 협업을 위한 이슈 트래커 및 토론 기능과 CI/CD를 위한 Github Actions 등 다양한 서비스를 제공한다.
Github에 저장소를 등록하고 사용하기 위해서는 ssh 키가 필요하다. 아래 링크를 참조하여 ssh 키를 생성하고 계정에 등록할 수 있다.
https://docs.github.com/ko/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent
저장소 복제#
원격 저장소를 로컬 컴퓨터로 복제해 오는 방법은 매우 간단하다. 명령줄에 아래와 같이 입력하면 된다.
git clone <remote url>
<remote url>
은 원격 저장소의 URL이다. https://github.com/user0/repo0/
형식의 https 주소 혹은
git@github.com:user0/repo0-git
형식의 ssh 주소가 될 수 있다.
현재 디렉토리에 저장소 이름으로 된 새로운 폴더가 생성되고, 그곳에 버전 히스토리를 포함한 모든 파일을 서버로부터 다운로드하고, 작업 공간을 최신 커밋의 파일들로 설정한다.
최신 정보 받아오기#
다른 사용자가 업데이트한 최신 버전 기록을 받아오려면 아래와 같이 입력한다.
git pull
현재 작업 중이던 파일들의 내용이 최신 버전으로 업데이트된다.
Info
사실 git pull
은 git fetch
와 git merge
두 명령어를 하나로 합친 것이다. 이 두 명령어에 대해서는
나중에 자세히 살펴보도록 하자.
Chapter 2. Git 저장소의 구조#
Git Lifecycle#
Git에 의해 관리되는 저장소의 파일들이 가질 수 있는 상태는 아래와 같이 4가지로 구분된다.
- Untracked
- Staged (= Added)
- Modified
- Committed (= Unmodified)
Untracked는 파일이 Git에 의해 관리되고 있지 않은 상태를 의미한다. 파일이 로컬 작업 공간에는 존재하지만, Git이 이 파일의 변경 사항에 대해서는 아무런 신경을 쓰지 않는다. 사용자가 작업 공간에서 새로 생성한 파일은 Untracked 상태가 된다.
Staged는 새로 생성하거나 수정한 파일을 곧 커밋할 것이라고 표시한 것을 말한다.
Modified는 커밋되어 있는 파일이나 스테이징 되어 있는 파일 중 아직 추가 또는 커밋되지 않은 변경사항이 있는 파일들을 의미한다. Committed 혹은 Staged 상태의 파일을 수정한 뒤 로컬 작업 공간에 저장하면 Modified 상태로 변경된다.
Committed는 데이터가 로컬 데이터베이스에 안전하게 저장되어 있는 상태를 의미한다. Staged 된 파일을 커밋하면 Committed 상태가 된다.
Git 저장소 영역#
위에서 설명한 파일들의 상태들은 아래 Git 저장소의 세 가지 영역과 밀접한 관련이 있다.
- Working Directory (= Working Tree)
- Staging Area (= Index)
- Repository (= .git Directory)
Working Directory는 사용자가 직접 작업하는 로컬의 파일들이 있는 작업 공간이다.
Staging Area 또는 Index는 곧 커밋될 파일, 즉 Staged 상태의 파일들이 존재하는 공간이다.
Repository는 커밋이 완료된, 즉 Committed 상태의 파일들이 존재하는 공간이다.
Working Directory의 파일들을 커밋하기 위해 Staging Area에 올리는 것을 추가(Add) 또는 스테이징(Staging)이라고 한다. 또한, 스테이징된 파일을 커밋하여 데이터베이스에 특정 버전 기록으로 남기는 것을 커밋(Commit)이라고 한다. 반대로, 특정 버전의 커밋으로 로컬 Working Directory를 설정하는 것을 체크아웃(Checkout)이라고 한다.
Git 저장소의 파일들의 상태 변화와 그에 따른 저장소 영역 이동에 대해서 잘 이해하고 있어야 한다.
예를 들어, 사용자가 Git 저장소에 새로운 파일을 추가하면 그 파일은 Untracked 상태이며, Working Tree에만 존재하고
Staging Area나 Repository에는 존재하지 않는다. 사용자가 그 파일을 모두 작성한 후 커밋을 위해 스테이징하였다면,
해당 파일은 Staged 상태이며, Working Directory와 Staging Area에 존재하지만 아직 Repository에는
들어가지 않은 상태이다.
이 상태에서, 사용자가 해당 파일에 필요한 추가적인 변경 사항을 발견하여 파일을 변경하였다면, 해당 파일은 Modified
상태이며, 파일은 Staging Area에서 제거되고 Working Directory에만 남는다. 마지막으로, 사용자가 모든 변경을 마치고
파일을 저장한 후 스테이징과 커밋까지 완료하였다면, 해당 파일은 Committed 상태이며, Working Directory와
Repository에 존재한다. 여기서, 사용자가 이 파일이 추가되기 전 버전의 커밋을 체크아웃한다면, Working Tree가
이 파일이 추가되기 전의 상태로 되돌아가므로, 이 파일은 Repository에만 존재하고 Working Directory에서는 사라지게 된다.
Chapter 3. Commit과 Push#
init#
Git 저장소를 시작하는 방법에는 2가지가 있다.
- 이미 만들어진 저장소를 clone
- 아직 Git을 통해 버전이 관리되고 있지 않은 디렉토리에 Git을 적용
1번 방법에 대해서는 위 저장소 복제에서 설명하였으니 2번 방법에 대해서 살펴보자.
터미널 (리눅스라면 bash
, 윈도우라면 Powershell
혹은 Git Bash
) 에서 원하는 디렉토리로 이동 후 아래와 같이 입력한다.
git init
이 명령어를 입력하면 현재 디렉토리 안에 .git
이라는 숨겨진 폴더가 생성된다. 이 폴더에는 Git이 관리하는 모든 버전 기록과
관련된 데이터가 들어 있다. 만약 현재 작업 공간에서 Git 관련 데이터를 모두 지우고 일반적인 디렉토리로 되돌려 놓으려면
이 폴더를 삭제하면 된다.
git status
를 입력하면 현재 git 저장소의 상태를 볼 수 있다. 아직 어떤 커밋도 추가되지 않았으므로 그 출력 결과는
아래와 같을 것이다.
On branch main
No commits yet
nothing to commit (create/copy files and use "git add" to track)
add#
이제 파일을 추가해볼 것이다. 현재 디렉토리에 README.md
파일을 추가해 보자.
그 다음 git status
를 입력하면 아래와 같이 표시될 것이다.
On branch main
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
README.md
nothing added to commit but untracked files present (use "git add" to track)
Visual Studio Code를 사용하고 있다면, 파일 탐색기에 아래 그림과 같이 표시될 것이다.
U는 Untracked를 의미한다.
파일에 간단한 내용을 작성하고, 터미널에 아래 명령 중 하나를 선택해 입력하여 스테이징한다.
git add ./README.md # README.md 파일을 스테이징 영역에 추가
git add . # 모든 Untracked와 Modified 상태의 파일들을 스테이징 영역에 추가
git add *.md # 확장자가 '.md'인 모든 Untracked와 Modified 상태의 파일들을 스테이징 영역에 추가
git status
를 입력해보면 아래와 같이 표시된다. 참고로, 파일을 스테이징 영역에서 내리려면 표시된 것과 같이
git rm --cached README.md
를 입력하면 된다.
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: README.md
Visual Studio Code에서는 아래와 같이 표시된다.
A는 Index Added를 의미한다.
Tip
vscode를 사용 중이라면, Source Control 탭에서 쉽게 스테이징 영역에 파일을 올리고 내릴 수 있다.
변경사항 옆에 있는 +
를 눌러 로컬 변경사항을 스테이징하거나, 그 왼쪽에 있는 화살표 버튼을 눌러 로컬 변경사항을
원래대로 되돌려 놓을 수 있다. 단, 되돌려 놓은 변경사항은 복구할 수 없으니 주의해야 한다.
commit#
스테이징한 파일을 커밋하기 위해서는 아래와 같이 입력한다.
git commit
위 명령어를 입력하면, 현재 환경 설정에 따라 vim, nano, vscode 등의 텍스트 편집기 중 하나가 열릴 것이다. 해당 텍스트 편집기에 커밋 메시지를 입력하고, 저장한 후 편집기를 종료하면 커밋이 완료된다
#로 표시된 줄은 주석으로, 커밋 메시지로 기록되지 않고 무시된다.
커밋 메시지에 아무것도 입력하지 않으면 커밋이 취소된다. 그래서 커밋을 취소하고 싶을 때에는 커밋 메시지 공간을 비워 놓으면 된다.
커밋 메시지는 비워 놓지만 않으면 아무 내용이나 써도 되지만, 현재 커밋이 어떤 변경사항을 포함하는지 쉽게 알아볼 수 있게 작성하는 것이 좋다. 대형 프로젝트에서는 커밋 메시지의 규격을 정해서 해당 규격으로만 메시지를 작성하도록 규정한다. 이를 커밋 컨벤션이라고 한다.
이제 첫 번째 커밋이 만들어졌다. git log
를 입력하여 확인해보면 커밋의 고유 해시값, 브랜치, 작성자, 작성일시, 커밋 메시지가 표시된다. 해시값은 커밋의 고유한 ID와 같은 것으로, 해시 값을 통해서 커밋을 참조할 수 있다. 브랜치에 대해서는 잠시 뒤에 자세히 알아보겠다.
commit af73bb5fb4d1de260d67c3312a46bae2c3336446 (HEAD -> main)
Author: Sein Lee <lsin1227@gmail.com>
Date: Mon Jan 13 13:55:20 2025 +0900
add 'hello world!'
vscode의 git graph 확장 프로그램으로 확인하여도 비슷한 정보가 표시된다. main
은 브랜치 이름이다.
Git 커밋 그래프
Git 커밋 로그를 그래프로 시각화하여 본다면 커밋 히스토리의 이해가 훨씬 쉬워진다. gitk, Github Desktop 등 다양한 시각화 툴이 있지만 여기서는 Visual Studio Code의 확장 프로그램인 Git Graph를 이용하도록 하겠다.
push#
다른 개발자와의 협업을 위해서는 나의 로컬 저장소에 있는 내용들을 원격 저장소(Remote Repository)에 업로드해야 한다.
원격 저장소에 로컬 변경 사항들을 업로드하기 위해서는 당연히 해당 원격 저장소에 쓰기 권한을 가지고 있어야 한다.
Github 원격 저장소의 경우, 리포지토리 설정에서 다른 사용자에게 쓰기 권한을 부여할 수 있다. 원격 저장소에 현재까지의 커밋을 업로드하는 동작을 푸시(push)라고 하며, git push
명령어를 통해 수행할 수 있다.
만약 git clone
을 이용하여 원격 저장소를 복제해 왔다면, 자동으로 origin
이라는 이름의 원격 저장소가 추가되어 있을 것이다. git clone
을 수행할 때 인자로 원격 저장소의 URL을 넘겨줬으므로 이는 당연한 일이다.
반면 git init
을 통해 저장소를 처음부터 시작했다면, 원격 저장소가 등록되어 있지 않다. 따라서, 먼저 로컬 변경 사항들을 업로드할 원격 저장소를 등록해 주어야 한다.
git remote add <remote_name> <remote_url>
<remote_name>
은 원격 저장소의 이름이다. git clone
명령어가 자동으로 생성하는 원격 저장소의 이름이 origin
이기 때문에, 일반적으로 원격 저장소의 이름은 origin
으로 두는 경우가 많다.
<remote_url>
은 원격 저장소의 URL이다. 현재의 경우에는 Github에서 만든 비어 있는 저장소 주소를 입력하면 된다.
이제 원격 저장소를 등록하였으니 바로 git push
를 하면 오류가 발생한다. 이는 현재 로컬 브랜치 main
이 어떤 원격 저장소에 연결되어야 하는지를 찾지 못해서 발생하는 문제이다. 로컬 브랜치와 원격 브랜치에 대해서는 조금 뒤에 자세히 알아보고, 아래와 같이 입력하여 로컬 브랜치 main
과 원격 저장소 origin
을 연결하고 push를 진행한다.
git push --set-upstream origin main
이제 Git은 main
브랜치가 어느 원격 저장소로 가야 하는지 알고 있다. 따라서 다음부터는 그냥 git push
만 입력하면 된다.
push가 성공적으로 진행되었다면, 업데이트된 내용이 GitHub에 반영되어 있는 것을 확인할 수 있다.
참고로, 원격 저장소는 여러 개 등록할 수 있으며, 현재 등록되어 있는 원격 저장소를 확인하려면 아래 명령어를 입력한다.
git remote -v
자주 있는 일은 아니지만, 원격 저장소를 변경하거나 삭제하려면 아래와 같이 입력한다.
git remote set-url <name> <new_url> # 원격 저장소 URL 변경
git remote remove <name> # 원격 저장소 삭제