[스터디] Git 기초

· 5min · clemado1

버전 관리 (VSC; Version Control System)

VSC는 파일 변화를 시간에 따라 기록했다가 나중에 특정 시점의 버전을 다시 가져올 수 있는 시스템이다. VSC를 사용하면 파일을 누가 어떻게 변경했는지 찾아 볼 수 있고 파일을 잘 못 삭제/수정했을 때 이전으로 되돌릴 수 있다.

로컬 버전 관리

로컬 VSC는 간단한 데이터베이스를 사용해서 로컬의 파일 변경 정보를 관리한다. 대표적인 로컬 VSC는 RCS(Revision Control System)이 있다.

중앙집중식 버전 관리 (CVSC; Centeralized Version Control System)

CVSC는 파일을 관리하는 서버가 별도로 존재하고 개발자들은 중앙 서버에서 파일을 받아서(Checkout) 사용한다. CVSC은 사용자들끼리 어떤 파일을 작업하고 있는지 파악할 수 있고 관리자가 그를 관리 감독하기 좋다는 장점이 있다. 그리고 당연히 사용자별로 로컬에서 버전을 관리하는 것 보다 중앙 서버 하나에서 관리하는 것이 훨씬 쉽다.
그러나 이러한 점은 다시 치명적인 단점이 될 수 있는데, 만약 중앙 서버에서 문제가 생긴다면 사용자들은 갱신하거나 백업할 방법이 없고 중앙 서버의 데이터가 날라가기라도 하면 모든 히스토리를 잃는다. 물론 로컬 VSC도 마찬가지로 로컬의 데이터가 손상된다면 모든 것을 잃는다.
대표적인 CVSC는 Subversion, Perforce가 있다.

분산 버전 관리 시스템 (DVCS; Distributed Version Control System)

DVCS의 사용자는 단순히 파일의 마지막 스냅샷을 Checkout하는 게 아니라 저장소를 전부 복제한다. 서버에서 문제가 발생하더라도 복제본으로 다시 작업할 수 있다. DVCS는 원격 저장소가 존재하는데 이 원격 저장소를 여러 개 가질 수도 있기 때문에 다양한 그룹과 다양한 방식으로 협업할 수 있다. Git이 바로 이 DVCS를 기반으로 한다.

Git

리눅스 커널의 버전 관리를 위해 사용하던 상용 DVCS, BitKeeper를 사용할 수 없게 된 리누스 토발즈는 자체 도구로 Git을 만들었다. 빠른 속도, 완벽한 분산, 비선형 구조 등을 목표로 약 2주만에 만든 Git은 몇 년 지나지 않아 점유율 1위를 차지했고 현재는 압도적인 점유율을 갖고 있다. Git은 대형 프로젝트도 끄떡 없을 정도로 빠르고, 동시다발적으로 수 많은 브랜치를 사용해도 전혀 문제 없다.

차이점

다른 VCS와 Git의 결정적인 차이점은, 다른 VCS는 파일의 목록을 관리하는 데 반해, Git은 시간순으로 프로젝트의 스냅샷을 정리한다는 점이다. Git은 상태를 저장할 때 마다 파일이 존재하는 그 순간을 중요하게 여긴다. 파일이 달라지지 않았다면 파일을 새로 저장하지 않고 이전 상태의 파일에 대한 링크만 저장한다. Git은 데이터를 스냅샷의 스트림처럼 취급한다. 또한 Git은 대부분 로컬 파일과 데이터만 사용하기 때문에 네트워크의 영향을 받지 않는다. 파일의 히스토리를 조회하거나 변경을 저장할 때에도 네트워크 연결 없이 로컬 저장소만 사용해서 작업할 수 있다.

특징

  • Git은 데이터를 저장하기 전에 SHA-1 해시를 사용해서 체크섬을 만들고 체크섬으로 데이터를 관리한다. 체크섬은 Git에서 사용하는 기본적인 데이터 단위이자 기본 철학이다.
  • Git은 파일을 세 가지 상태로 관리한다. Working Directory에서 파일을 수정한 뒤 Staging Area에 파일을 Stage하여 커밋할 스냅샷을 만들고, 커밋하여 Git Directory에 영구적인 스냅샷으로 저장한다.
    • Commited : 데이터가 로컬 데이터베이스에 저장되었음
    • Staged : 수정한 파일을 곧 커밋하기로 표시함
    • Modified : Working Directory에 있는 수정한 파일. 아직 로컬 데이터베이스에 저장하지 않은 상태

명령어

  • init : init 명령어는 프로젝트에 Git 저장소를 생성한다. 이 저장소는 .git 이름을 가진 하위 디렉토리로 되어있다. 내부 구조는 아래와 같다.
$ ls -F1
config
description
HEAD # 현재 Checkout한 브랜치를 가리킨다.
hooks/
info/ # 버전 관리 제외 대상
objects/ # 데이터베이스
refs/ # 브랜치, 태그, 리모트 등을 저장
  • clone : 원격 저장소에 있는 프로젝트를 로컬로 가져올 때 사용한다. 명령어를 실행하면 원격 저장소의 프로젝트를 로컬에 복제하는데, 먼저 디렉토리를 만들고 git init으로 Git 저장소를 생성한다음 입력한 원격 저장소를 origin이라는 이름으로 추가하고 fetchcheckout 한다.

    $ git clone {remote}
    
  • add : Working Directory에 있는 수정한 파일(Modified 상태)을 Staging Area에 추가하는 명령어. add를 통해 Stage한 파일만 commit이 가능하다. 주의할 점은 add 또한 파일을 단위로 하는 것이 아니라 그 순간을 기록하는 것이므로 add 후에 파일을 또 수정한다면 다시 add를 사용해주어야 한다.

    # 파일의 상태 확인
    $ git status
    $ git add {filepath}
    # 전체 추가
    $ git add .
    
  • commit : 현재 Staging Area에 있는 파일을 모두 커밋한다. 커밋을 하면 데이터베이스에 스냅샷으로 기록하고 현재 브랜치가 새로운 커밋을 가리키도록 한다.

    # 기본 편집기를 통해 커밋 메시지 작성
    $ git commit
    # 인라인으로 커밋 메시지 작성
    $ git commit -m "commit message"
    # add 생략 (Tracked 상태 모두 추가)
    $ git commit -a -m "commit message"
    
  • push : 로컬에서 새로 변경한 내용을 원격 저장소에 반영하기 위해 사용한다. push 명령어를 사용하면 자동으로 로컬에만 있는 커밋 뭉치만 Push 한다.

    $ git push origin master # origin의 master 브랜치를 로컬의 master 브랜치로 업데이트
    
  • pull : 지정한 리모트에서 Fetch한 다음 브랜치에 Merge를 시도한다. push와 마찬가지로 로컬에만 없는 커밋 뭉치만 받아와 저장한다. pullfetchmerge를 순서대로 실행하기 때문에 좀 더 섬세한 작업을 위해서는 명령어를 분리하여 사용하는 것이 좋다.

    $ git pull origin master
    
  • fetch : fetch 명령은 원격 저장소에서 로컬 데이터베이스에 있는 것을 제외하고 모두 가져온다. pull과는 다르게 자동으로 merge는 하지 않는다. 혼자 사용하는 저장소이거나 중요도가 높지 않다면 pull 명령어를 사용해도 문제 없지만, 진행 상황을 매 번 확인해야 하는 경우 사용한다. fetch를 한 다음 diff 명령어를 사용하면 병합을 하기 전에 변경될 내용을 미리 확인할 수 있다.

    $ git fetch origin
    $ git diff HEAD origin/master
    
  • merge : 다른 브랜치를 현재 Checkout 된 브랜치에 병합한다. A 브랜치에서 B 브랜치를 병합한다고 할 때 만약 B 브랜치가 단순히 A 브랜치의 미래라면 fast-forward(빨리감기)로 이동하고, 서로 다른 커밋을 가지고 있다면 별도의 커밋을 통해 3-way Merge를 한다. 하지만 동일한 파일에서 수정이 일어났을 경우에는 두 방법 다 실패할 수 있다. 이 때 병합을 시도하면 충돌 에러 메시지가 출력되고 파일이 Unmerged 상태가 되는데, 파일을 확인해보면 충돌난 내용을 확인할 수 있다. 내용을 확인하여 수정한 다음 add & commit 을 통해 수동으로 해결할 수 있다.

    $ git merge origin/master
    

참고

  • 프로 Git 2판