설모의 기록

[우아한테크캠프] 9일차 본문

일상/우아한테크캠프

[우아한테크캠프] 9일차

HA_Kwon 2018. 7. 12. 16:01


Git : Version Control System



동료와 협업을 하다보면 동료의 코드와 제 코드를 합쳐야 할 경우가 생깁니다. VCS 가 없었을 때는 파일이나 프로젝트를 압축해 동료와 코드를 직접 비교해가며 합쳤습니다. 이것은 굉장히 비효율적인 일이었고, 실수로 오타를 내거나 코드를 합치지 못할 경우가 생길 수도 있습니다. 

그래서 나타난 것이 VCS인데요. Git 이전에는 SVN 이라는 버전 관리 시스템이 있었습니다. SVN은 현재에도 많이 쓰이는 버전 관리 시스템으로 장단점이 있습니다.

  • Git
    - Git 은 DVCS (Distributed Version Control System: 분산 버전 관리 시스템) 입니다.
    서버의 원격 저장소를 내 컴퓨터의 로컬 저장소로 복제해와서 나만의 commit history 를 관리할 수 있습니다. 분산 관리 방식은 서버가 다운되어도 내 로컬 저장소가 있기 때문에 계속 commit 하면서 개발을 진행할 수 있습니다.

    - 로컬 저장소가 있기 때문에 네트워크 연결이 되어 있지 않아도 commit 이 가능합니다.

    - 파일을 수정한 후 commit 하면 이전 파일에 덮어쓰는 것이 아니라 새로운 파일이 저장됩니다. 

  • SVN
    - SVN 은 CVCS (Centralized Version Control System: 중앙 집중식 버전 관리 시스템) 입니다.
    프로젝트 관리를 중앙인 서버가 하고 있는 구조입니다. 따라서 Git 과는 달리 나만의 로컬 저장소가 없기 때문에 commit 을 하면 서버 저장소가 바로 바뀝니다. 그래서 서버가 다운되면 commit history 를 관리할 수 없어 불편합니다.

    - 서버에 버전을 올려야하기 때문에 네트워크 연결이 필요합니다.

    - 파일을 수정한 후 commit 하면 수정한 부분만 commit 에 관리됩니다. 따라서 update 과정이 Git 보다 빠릅니다.


이외에도 다양한 차이점이 있겠지만, 결국 SVN 의 가장 주된 단점은 나만의 history 를 관리하지 못한다는 것입니다. 이 불편사항을 해소하기 위해 리누스 토발즈는 Git을 개발했습니다. 아래에서 Git 의 명령어 사용법을 설명하겠습니다.




저장소 구조

Git 은 원격 저장소 (remote repository)로컬 저장소 (local repository) 가 있습니다. 로컬 저장소는 내 컴퓨터에 생성된 저장소이며, 원격 저장소는 Github 저장소를 말합니다. 아래의 그림으로 설명하겠습니다.



아래의 명령어 순서로 설명드리겠습니다.



git init


git init 명령어는 로컬 저장소를 생성하는 명령어입니다. 명령어를 실행하면 .git 이 생성되며 이것을 지우면 로컬 히스토리가 모두 날라가게 됩니다. .git 이 존재하는 디렉토리를 Working directory라 합니다.




git clone <저장소> [디렉토리명]


git clone 은 이미 존재하는 원격 저장소에 연결된 로컬 저장소를 생성하는 명령어입니다. 디렉토리명을 입력하지 않으면 저장소 이름으로 Working directory 가 생성되며, Working directory 안에 .git 이 생성됩니다.

저장소는 저장소_URL 뿐만 아니라 ssh, 컴퓨터 내의 로컬 저장소 디렉토리 경로를 입력할 수도 있습니다.




git add <스테이지에_올릴_파일>


git add 명령어는 Working directory 의 변경사항을 stage 에 올리는 작업입니다. 스테이지는 메모리가 아니라 파일이므로 컴퓨터를 꺼도 그 내용이 없어지지 않습니다. 스테이지에 올린 파일을 다시 내리고 싶다면 git reset 명령어를 이용하시면 됩니다.




git commit -m "commit_message"


git commit 명령어는 스테이지에 있는 내용을 로컬 저장소에 저장하는 명령어입니다. commit 이란 내 컴퓨터의 스냅샷과도 같습니다. commit 을 하는 이유는 commit history 를 관리해 원하는 커밋 상태로 돌아갈 수 있도록 하기 위함입니다. 따라서 commit history 가 .git 에 저장되며 $ git status 명령어를 입력하면 clean 하다는 메세지를 볼 수 있을 것입니다.

commit 객체가 생성되는 과정은 다음과 같습니다. 

  • 새로운 커밋 객체를 만듭니다.
  • 커밋 객체를 만들기 직전의 커밋이 새로운 커밋 객체의 부모입니다. 따라서 새로운 커밋은 부모 커밋의 참조를 기억합니다.
    -> 특정 커밋을 부모로 만들고 싶다면 '$ git commit -p 부모_커밋_아이디' 를 실행하시면 됩니다.
  • HEAD 를 새로운 커밋을 가리키게 합니다.


commit 은 보통 하나의 로직을 구현하면 수행하기 때문에 에러가 발생하거나 논리적으로 오류가 있는 상태에서는 하지 않습니다. commit message 에 대한 컨벤션은 링크 를 참고하시면 됩니다.

commit 관리는 자식 커밋이 부모 커밋의 참조를 가지도록 하여 이전 커밋으로 돌아갈 수 있도록 합니다. 즉, 최신 커밋이 자신이 유래된 커밋의 참조를 가지고 있는 것입니다.




git push


git push 명령어는 로컬 저장소에 있는 커밋 내용을 원격 저장소로 보냅니다. 이 때, 착각하지 말아야 할 내용은 다음과 같습니다.

  • 모든 브렌치의 커밋 내용을 보내는 것이 아니라 현재 나의 브렌치의 커밋 내용만 원격 저장소로 보냅니다.
  • 이미 원격 저장소에 있는 커밋은 보내지 않고 존재하지 않은 커밋만 보냅니다.

따라서 git push 명령어를 수행하면 해당 브렌치의 로컬 저장소 상태와 원격 저장소의 상태가 같아지게 됩니다.




git pull


git pull 명령어는 fetch 명령어와 merge 명령어를 한 번에 수행하는 것과 같습니다. 

먼저 원격 저장소에 있는 커밋 내용을 로컬 저장소로 가져옵니다. 이 과정이 fetch 과정이며 이 때는 모든 브렌치의 커밋 내용을 가져오게 됩니다. 특정 브렌치의 커밋 내용만 가져오고 싶다면 $ git fetch [저장소이름] [브렌치이름] 를 수행해야 합니다.

이 후 로컬 저장소의 내용과 워킹 디렉토리의 내용을 merge 합니다. 이 때, 충돌이 발생할 수 있으므로 해결한 후에 다음 작업을 수행해야 합니다.




git status


git status 명령어는 스테이지와 워킹 디렉토리, 그리고 로컬 저장소의 차이점을 알려주는 명령어입니다. clean 하다는 것은 세 개의 상태가 똑같다는 것을 의미합니다.




git branch


branch 는 커밋 객체에 대한 참조입니다. HEAD 는 현재 작업중인 브렌치를 가리킵니다. 

git branch 명령어를 실행하면 현재 저장소에 존재하는 브렌치를 모두 보여줍니다. 'git branch 브렌치이름' 을 실행하면 '브렌치이름'으로 브렌치가 생성됩니다. 해당 브렌치는 현재 작업중이 브렌치의 복제본이므로 커밋 히스토리 또한 복제됩니다.




git reset


 

   

git reset 명령어는 말 그대로 초기화하는 것입니다. 내가 원하는 커밋으로 되돌리는 명령어입니다. '$ git reset C1' 명령어를 수행하면 위의 왼쪽 이미지에서 오른쪽 이미지의 상태로 변경이 됩니다.

 git reset 의 옵션으로 3가지가 있는데요, 가장 많이 사용하는 옵션은 --hard 입니다.

  • --soft : checkout 과 같은 것으로 스테이지와 워킹 트리는 바꾸지 않고 HEAD 만 바꾸는 것입니다.
  • --mixed : 스테이지만 바꾸고 워킹 트리는 바꾸지 않습니다.
  • --hard : 스테이지와 워킹트리 모두 바꿉니다.





git merge


git merge 는 말 그대로 내용을 합쳐주는 명령어입니다. git rebase 보다 안전한 명령어지만 history 가 깔끔하지는 않습니다. 아래의 예를 보면 이해가 쉬울 것입니다.

merge 를 하기 전 상태입니다.


첫 번째 이미지에서 ' $ git merge branch1 ' 을 하면 master 와 branch1 이 합쳐져 두 번째 이미지 상태가 됩니다. 

그 후 ' $ git merge branch2 ' 를 하면 master 와 branch2 의 상태를 합친 새로운 커밋 객체 C7이 생성되고 master 의 최신 커밋이 됩니다. 이 상태는 아래의 이미지와 같습니다.


merge 를 사용하면 합쳐진 새로운 커밋 객체가 생성되어 사이클이 형성되므로 히스토리가 rebase에 비해 깨끗하지 않습니다. 

C7 은 C4, C6 두 개의 부모 커밋을 가지게 됩니다. 원래 가리키고 있었던 C4 가 첫 번째 부모 커밋이 되며, 합쳐진 C6 커밋이 두 번째 부모 객체가 됩니다. 




git rebase


git rebase 명령어는 merge 와 비슷하지만 새로운 커밋 객체를 만드는 것이 아니라 rebase 할 대상 브렌치가 가지고 있지 않은 커밋 버전들을 대상 브렌치의 아래로 붙여주는 작업입니다. 말로는 어려우니 아래의 이미지로 설명하겠습니다.


먼저 rebase 하기 전 상태입니다.

master 가 ' $ git merge branch1 ' 명령어로 branch1 과 합친 모습입니다. branch2 와 master 는 C1 커밋 이후부터 다른 커밋 객체를 참조하고 있습니다. rebase 는 branch2 가 C1 커밋 이후로 참조하는 커밋들을 master 의 최신 커밋, 즉 C4 커밋의 자식으로 복제하는 것입니다. 다음 이미지들을 보겠습니다.


   

첫 번째 이미지는 ' $ git checkout branch2 ' 현재 작업 브렌치를 branch2 로 바꾼 모습입니다. 

이 때, ' $ git rebase master ' 를 하면 C5, C6 커밋을 C4 커밋의 자식으로 붙여진 모습이 되는 것을 확인할 수 있습니다. 

주의해야할 것은 그대로 복제하는 것이 아니라 C5`, C6` 커밋이 생성된다는 것입니다. 다시 말해서 C4 와 C5, C4 와 C6 가 다른 코드를 가지고 있을 수 있기 때문에 충돌이 발생할 수 있다는 것입니다. 따라서 히스토리는 사이클이 형성되지 않고 깔끔하게 한 줄이 되지만 충돌이 발생해 해결하기가 까다로울 수도 있다는 것입니다. 

예를 들어 branch2 가 master 가 가지고 있지 않은 커밋을 100개 가지고 있다고 하면, 충돌이 일어난 커밋 객체가 100개가 될 수도 있다는 것입니다. 따라서 rebase 는 조심히 사용해야 할 명령어입니다.


rebase 를 한다고 해서 master 의 최신 커밋이 branch2 가 가리키고 있는 커밋이 되는 것이 아닙니다. 따라서 master 로 브렌치를 바꾼 후 ' $ git merge branch2 ' 명령어를 수행해야 비로소 위의 이미지와 같은 상태가 됩니다. 이후로 branch2 는 계속 작업을 수행하면 됩니다.



이미지 출처 : https://learngitbranching.js.org/

'일상 > 우아한테크캠프' 카테고리의 다른 글

[우아한테크캠프] 12일차  (1) 2018.07.18
[우아한테크캠프] 10일차  (0) 2018.07.14
[우아한테크캠프] 8일차  (0) 2018.07.12
[우아한테크캠프] 7일차  (0) 2018.07.11
[우아한테크캠프] 6일차  (0) 2018.07.09
Comments