버전 관리란 파일 변화를 시간에 따라 기록했다가 나중에 특정 시점의 버전을 다시 꺼내올 수 있는 시스템이다.
로컬 환경에서 버전을 관리하기 위해 디렉토리로 파일을 복사하는 방법이다. 사용은 아주 간단하지만 작업하던 디렉토리를 지우거나 파일을 잘못 복사할 수도 있는 위험성이 크다.
프로젝트를 하다 보면 다른 개발자와 협업하는 경우가 많다. 이럴 때 생기는 문제를 해결하기 위해 CVCS가 개발되었고, 파일을 관리하는 중앙 서버가 존재하고 클라이언트는 중앙 서버에서 파일을 받아 사용(Checkout)하는 방법이다.
이 방법은 누가 무엇을 하고 있는지 명확하게 알 수 있고, 중앙 서버 하나를 관리하기 때문에 관리 측면에서도 장점이 있다. 다만, 중앙 서버가 마비된다면 마비된 시간만큼 아무도 다른 사람과 협업할 수 없고 백업할 수도 없다는 치명적인 단점이 있다.
Git, Mecurial, Bazaar, Darcs 같은 분산 버전 관리 시스템은 저장소를 히스토리와 더불어 전부 복제한다. 따라서 서버에 문제가 생기면 복제물로부터 다시 작업을 시작할 수 있다.
Git은 시간순으로 프로젝트의 스냅샷을 저장하는 분산 버전 관리 시스템이다. 만약 파일이 달라지지 않았다면 파일을 새로 저장하지 않고, 이전 상태의 파일에 대한 포인터만 저장하는 특징이 있어 성능이 빠르다.
모든 버전 관리 시스템은 브랜치를 지원한다. 개발을 하다 보면 코드를 여러 개로 나눠서 원래 코드와는 상관 없이 독립적으로 작업을 해야 하는 경우가 생긴다. 이럴 때 사용하는 것이 브랜치이고, 브랜치는 원래 코드와는 상관 없이 독립적으로 개발을 진행할 수 있도록 해주는 것이다.
새로운 브랜치를 만들어도 브랜치 사이를 이동하거나 생성하는 것은 하루에도 수십번씩 가능하므로 Git은 브랜치를 생성하여 독립적으로 작업하고, 작업 내용을 Merge하는 방법을 권장하고 있다.
Git은 데이터를 변경사항(Diff)으로 기록하지 않고 일련의 스냅샷으로 기록한다. Commit하면 Staging Area에 있는 데이터의 스냅샷에 대한 포인터, 커밋 메세지, 이전 커밋에 대한 포인터 등을 포함하는 커밋 개체를 저장한다. 이전 커밋에 대한 포인터가 있기에 현재 커밋이 무엇을 기준으로 바뀌었는지를 알 수 있다.
독립적으로 생성된 공간에서 작업한다고 해도 이를 Merge할 때 충돌이 발생할 수도 있다. 브랜치 공간과 원본 공간에서 동일한 부분을 수정했다면 Git은 Merge하지 못하고 충돌(Conflict) 메세지를 출력한다.
이 경우 Git은 자동으로 Merge하지 못해서 새로운 Commit이 생기지 않고, 충돌한 변경 사항에 대해선 개발자가 수동으로 해결한다. 만약 다음과 같은 충돌 메세지가 출력됐다고 가정해보자.
<<<<<<< HEAD:index.html
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
please contact us at support@github.com
</div>
>>>>>>> iss53:index.html
HEAD
라고 적힌 부분은 HEAD
버전(merge 명령을 실행할 때 작업하던 브랜치)의 내용이고, 아래 쪽은 iss53
브랜치의 내용이다.======
로 각 브랜치의 내용이 구분되어 있으므로 이를 잘 확인해 충돌한 부분을 새로 수정한 후 <<<<
====
>>>>
포함된 행을 삭제하여 충돌을 해결할 수 있다.git branch
명령어를 통해 브랜치의 목록을 확인할 수 있는데, git branch --merged
또는 git branch --no-merged
명령어를 사용하면 Merge된 브랜치인지 Merge되지 않은 브랜치인지 확인할 수 있다.
여기서 Merge된 브랜치는 다른 브랜치와 Merge했기 때문에 삭제해도 정보를 잃지 않으므로 삭제해도 되는 브랜치이다.
git에서 HEAD 포인터란 현재 작업중인 브랜치를 가리킨다.