본문 바로가기

CS

[Git] Git 기초

What is Git(https://github.com/git)?

Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.

위 문장에서 알 수 있듯 깃은 분산형 버전 관리 시스템이다. 이렇게만 들으면 꽤나 그래서 그게 뭔데?라는 생각이 들기 마련이다. 그래서 우리가 한번쯤은 해봤을 팀플로 예를 들어보자면, 하나의 파일을 여러명이서 같이 수정을 해야한다고 했을 때, 같은 부분을 수정해서 하나로 합치는데 다시 한번 수정을 해야할 수도 있고 예전의 파일에서 있던 내용을 가져와서 다시 반영해야하는 경우도 생길 것이다. ('과제_최종', '과제_최종의_최종') 이러한 불편함이 개발을 할 때도 똑같이 발생하는데 이를 해결하기 위한 방법으로 사용하고 있는 것이 깃, 깃허브인 것이다. 깃을 통해 내 소스코드를 저장하고 버전을 관리하며, 다른 사람에게 공유할 수 있으며 협업 시에 발생할 수 있는 여러 충돌들을 해결할 수 있다.

 

기본 용어

  • Repository(저장소): 파일이나 폴더를 저장하는 공간으로 깃에서 repository는 히스토리, 태그, branch 등을 모두 기록해둔다.
    • Remote repository: 파일이 원격 저장소 전용 서버에서 저장이 되며 여러사람과 같이 공유되는 저장소이다. 주로 깃허브를 일컫는다.
    • Local repository: 파일이 저장되는 개인 저장소이며 보통 본인 pc(노트북이나 데스크탑) 안에 있는 저장소라고 생각하면 된다.
  • Working Directory: 흔히 말하는 폴더를 생각하면 된다. 작업하고 있는 프로젝트의 디렉토리 이다.
  • Staging Area: 저장소에 commit하기 전에 commit을 준비하는 위치이다.(뒤에서도 나오겠지만 git add가 된 파일들이 모여있는 곳이라고 생각하면 된다.) 즉, working directory와 repository의 징검다리라고 보면 된다.

 

git add

git add는 working directory 상의 내용을 staging area에 추가하는 역할을 수행한다. add는 뒤에서 나오는 다른 명령어와는 다르게 이력을 남기지 않는다.

  • git add (파일 디렉토리 경로) - 작업 디렉토리의 일부 내용만을 staging area로 넘기고 싶은 경우에 이 명령어를 사용한다.
  • git add . - 현재 디렉토리 이하에 속한 모든 내용을 staging area로 넘긴다. 보편적으로 add를 할 때 많이 사용한다.
  • git add -A - 작업 디렉토리 상에서 어디에 위치하든 상위 디렉토리를 포함해 모든 내용을 staging area로 넘긴다.
  • git add -p - 이 옵션을 사용하면 각 변경사항을 하나하나 터미널에서 확인하는 과정을 거쳐 staging area로 넘길 수 있다.

git commit

git commit은 add와 달리 명시적 기록을 남긴다. 보통 git add를 사용한 이후 사용하며, staging area에 있는 내용을 repository로 옮길 때 사용한다.

  • git commit - 커밋 메시지를 남기지 않고 이력만을 남길 때 사용한다.
  • git commit -m "(커밋 메시지)" - 커밋 메시지를 남기면서 커밋한다.
  • git commit -am "(커밋 메시지)" - 특이하게도 staging area를 건너뛰게 해준다. 즉, add .와 commit -m가 결합된 형태라고 생각하면 되며, 커밋 메시지를 남기면서 커밋한다.
  • git commit --amend - 가장 최근의 커밋을 new commit으로 대체한다.

git push

commit은 변경사항에 대한 이력을 local repository에 남길 뿐, 원격 저장소인 remote repository는 이것을 확인할 수 없다. 따라서 필요한 것이 git push이다. git push는 로컬 레포지토리에 있던 내용을 깃허브의 remote 레포지토리로 이동시킨다. 디폴트로 git push는 remote에 있는 상응하는 브랜치의 내용을 업데이트 한다.

  • git push origin (remote 브랜치 이름) - remote에 있는 브랜치를 지정해서 push할 수 있다.

remote

git remote는 로컬 레포지토리가 트랙킹하고 있는 remote 레포지토리들을 관리한다. remote와 소통할 수 있는 방법으로 pull, push, clone, fetch 등이 있다.

  • git remote -v - 로컬 레포지토리와 관련있는 현재 레포지토리를 리스트업 해준다.
  • git remote add (이름) (url) - add remote
  • git remote remove (name) - remove remote

 

지금까지 살펴봤을 때, 깃은 Working Directory -(add)-> Staging Area -(commit)-> Local Repository -(push)-> Remote Repository라는 플로우를 따르게 된다는 것을 알 수 있다. 여기까진 가장 기본적으로 사용하는 Git의 기능들로 필수로 알아야 하는 것들이었다면, 알면 Git을 좀 더 편리하게 사용할 수 있는 기능 몇개를 더 소개하고자 한다.

Git Hook

훅(Hook)은 이미 작성되어 있는 코드의 특정 시점을 동작 방식의 변화를 주는 기술을 뜻하는데, Git 또한 다른 버전 관리 시스템과 같이 이를 지원한다. git hook은 특정 상황에 특정 스크립트를 실행하는 방식으로 이루어져 있으며 아래에 .sample 확장자로 된 것들이 모두 Git에서 지원하는 훅들이다. 레포지토리 내에서 .git/hooks/ 디렉토리에서 찾을 수 있으며 확장자 없이 hooks 디렉토리에 있으면 자동으로 훅 스크립트가 켜진다.

훅은 클리이언트 훅과 서버 훅으로 분류된다. 클라이언트 훅은 커밋 워크플로, 이메일 워크플로, 기타 워크 플로로 구성되어 있으며 clone하는 것만으로는 복사되지 않기 때문에 주의가 필요하다. 서버 훅은 어떤 정책도 강요할 수 없었던 클라이언트 훅과 다르게 push 전후에 반드시 실행된다. 따라서 git repository 서버를 관리할 수 있는 권한이 있다면 서버 훅을 사용하는 것이 더 유용할 수 있다. (아래에서 설명할 두 개의 훅은 pre-commit과 pre-push 두개로 모두 클리이언트 훅에 속한다.)

pre-commit

  • 이름 그대로 커밋 직전에 실행되는 훅으로 커밋 메시지를 작성하기 전에 호출
  • 커밋하는 snapshot을 확인(빠트린 것이 있는 지, 테스트를 했는 지, lint 같은 프로그램에 돌린다던지 주석 확인 등)
  • exit code가 0이 아니라면 커밋 취소
  • git commit --no-verify을 통해 훅을 일시적으로 생략 가능
  • 다음 url(https://pre-commit.com/hooks.html)을 통해 커뮤니티에서 제공하는 pre-commit hook 확인 가능

pre-push

  • git push 명령을 실행하면 동작하며 remote 정보를 업데이트하고 push 하기 전에 실행
  • push 작업이 확인 없이 진행되는 것을 방지(특정 브랜치로의 의도하지 않은 수동 push 방지, 문법 테스트 등 실패시 push 방지)
  • exit code가 0이 아니라면 커밋 취소

 

.git

git init 명령 실행시 생성되는 디렉토리로, 아래처럼 subdirectory를 가진다.

가볍게 살펴보자면,

  • .git/config - local한 범위의 repository의 세부 설정
  • .git/hooks - 위에서 살펴본 hook에 관한 내용들을 담고 있음
  • .git/refs - heads, remotes, stash, tags에 대한 정보를 담고 있음 (ref: indirect way of referring to a commit)
  • .git 디렉토리 내에 존재하는 특별한 상위 refs
    • HEAD - 현재 가리키고 있는(checkout한) commit/branch
    • FETCH_HEAD - 가장 최근에 remote repo에서 fetched된 branch
    • ORIG_HEAD - HEAD에 급격한 변화가 생기기 전에 HEAD에 대한 백업 reference
    • MERGE_HEAD - git merge를 통해 현재 branch에 merge하고 있는 commit
  • .git/objects - object 데이터베이스 (Git은 content-addressable filesystem으로 모든 content들은 key를 가지고 있다.)

.gitignore

프로젝트에서 버전 관리를 할 때 .gitignore 파일 안에 명시해두면 Git에서 제외시켜준다.

  • build 과정에서 생기는 부산물, 따로 설치해서 사용할 수 있는 용량이 부담스럽게 큰 npm 모듈 같은 파일들, 보안상 민감한 데이터를 포함한 파일 등을 .gitignore에 넣는다.
  • Git에서 한번 추적한 파일은 이후에 .gitignore에 포함시켜도 제외되지 않으므로 삭제 후 커밋한 이후 다시 변경해주어야 한다.

 

참고자료:

https://github.com/git-guides
https://iseunghan.tistory.com/322
https://git-scm.com/book/ko/v2/Git%EB%A7%9E%EC%B6%A4-Git-Hooks
https://git-scm.com/docs/git-init
https://www.atlassian.com/git/tutorials/refs-and-the-reflog
https://git-scm.com/book/en/v2/Git-Internals-Git-Objects
https://www.daleseo.com/gitignore/