Git

Jang Seok Woo·2021년 7월 1일
0

실무

목록 보기
19/136

Git에 대해 알아보도록 하겠다.

작년 모의외주 프로젝트때 한 번 사용해본 경험이 있었는데, Git을 이용해 코드를 클라우드?에 올려둔다고 생각하면 될 것 같다.

근데 이게 계속 수정이되고 업데이트가 된다고 해도 수정된 일부 부분만 올리는 것도 가능하고 전부 올리는 것도 가능하다. 또한 수정 전으로 되돌리는 것도 가능하다는 사실!

코딩 중에 뭔가 애매하고 자꾸 잘못된 방향인것 같지만 쭉 가고있을 때 결과적으로 어제로 돌아가고 싶을때 Git이 있으면 한방에 해결된다.

없이 그냥 코딩중이었다면 지못미

팀원과의 협업도 효율적으로 가능하도록 만들어주는 것이 이 Git이라는 사실.

개발자라면 이 Git을 일찍 부터 알고있는게 정신건강에 좋다.

출처 : https://evan-moon.github.io/2019/07/25/git-tutorial/#git%EC%9D%80-%EB%88%84%EA%B0%80-%EC%99%9C-%EB%A7%8C%EB%93%A4%EC%97%88%EB%82%98%EC%9A%94
공부하면서 정리해보겠다. 위 사이트의 내용으로 공부하였으며 이하내용은 그대로이다.

Git은 누가, 왜 만들었나요?

Git은 2005년 리누스 토르발즈가 자기가 쓰려고 만든 분산 버전 관리 시스템이다. 일단 이 리누스 토르발즈라는 핀란드 형부터가 개발자라면 대부분 알고 있을 정도로 유명하다.

Remote / Origin

우선 Remote는 말 그대로 리모트 서버 자체를 의미한다. 이 리모트 서버라는 개념이 잘 이해가 안되시는 분은 우리가 자주 사용하는 구글 드라이브나 N드라이브와 같은 클라우드 스토리지를 사용하는 것을 떠올리시면 된다. 전 세계 어딘가에 있는 서버에 우리의 소스를 저장하는 것이다.

이때 이 서버를 제공해주는 대표적인 업체가 Github, Bitbucket, GitLab과 같은 회사들이다. 이 회사들이 Git을 만든 게 아니라 Git이라는 시스템에 필요한 리모트 서버와 Git을 좀 더 편리하게 사용할 수 있는 기능들을 제공하는 것이다.

Git을 사용할 때는 내가 어떤 리모트 서버에 변경 사항을 업로드 할 것인지 정해야하는데, 반드시 하나의 리모트 서버만 사용할 수 있는 것이 아니기 때문에 내가 사용하는 리모트 서버의 이름을 정해줘야한다. 이때 주로 사용하는 관례적인 이름이 바로 Origin이다.

보통은 한 개의 리모트 서버만 운용하는 경우가 대다수이기 때문에 많은 사람들이 Remote와 Origin을 혼용해서 부르곤 한다.

Repository

레파지토리(Repository, Repo)는 저장소라는 뜻으로, 리모트 서버 내에서 구분되는 프로젝트 단위라고 생각하면 된다. 우리가 구글 드라이브를 사용할 때도 하나의 디렉토리에 모든 파일을 다 때려넣지않고 몇 개의 디렉토리를 만들고 용도에 따라 파일을 나눠서 구분하는 것과 동일하다.

일반적으로 한 개의 레파지토리는 하나의 프로젝트를 의미하지만 경우에 따라서 레파지토리 하나에 여러 개의 프로젝트를 구성하기도 한다.

https://github.com/user/repository.git
https://user@bitbucket.org/group-name/repository.git
레파지토리를 클론받을 때는 해당 레파지토리를 가리키는 URL이 필요한데, 레파지토리의 이름은 URL의 맨 마지막에 .git 확장자를 가지는 방식으로 표현된다.

Branch

브랜치는 일종의 독립된 작업을 진행하기 위한 작업 공간의 개념이다. 맨 처음 Git을 초기화했을 때 기본적으로 master라는 이름의 브랜치가 하나 생성된다. 그 후 개발하는 기능 또는 버그 픽스에 따라서 브랜치를 새로 생성하고 거기서 작업한 후에 나중에 다시 master로 합치는 것이다.

이 브랜치 개념은 Git에 익숙하지 않은 분들에게 잠깐 설명하고 넘어가기에는 직관적으로 이해가 잘 안될 수 있는 개념이기 때문에 추후 다른 포스팅에서 다시 설명하겠다. 일단 지금은 이 3가지 정도만 기억해두자.

Git을 초기화하면 기본적으로 master 브랜치가 생긴다. 이 친구가 메인 브랜치 역할을 한다.
브랜치는 어떤 브랜치에서 분리시키는 것이고, 분리된 브랜치는 분리될 당시의 부모 브랜치 상태를 그대로 가지고 있다.
개발자는 각각의 브랜치에서 개발을 진행한 뒤 나중에 다시 master 브랜치로 변경 사항을 합친다.

필수 명령어들을 알아보자

만약 여러분이 혼자서만 프로젝트의 버전 관리를 한다면 단순히 리모트 서버의 레파지토리에서 소스를 받아와서 변경한 후 다시 리모트 서버로 업로드하는 과정만으로도 프로젝트를 진행하는데는 사실 아무 문제가 없다.

하지만 Git은 애초에 혼자서 개발하는 상황보다는 여럿이서 함께 소스를 수정하며 개발하는 협업 상황을 상정하고 만들었기 때문에 협업에서 발생할 수 있는 여러가지 곤란한 상황들을 타파하기 위한 많은 기능을 가지고 있다.

Git은 기본적으로 CLI(Command Line Tools)을 통해 사용하고 commit, fetch, branch와 같은 여러가지 명령어를 사용하여 이 기능들을 사용할 수 있게 해준다.

그럼 이번에는 Git을 사용하여 버전을 관리하기 위해 기본적으로 알아야 하는 몇 가지 명령어를 한번 살펴보자.

clone

clone은 말 그대로 리모트 서버의 레파지토리에서 클라이언트로 파일을 복붙하는 행위를 말한다. 이때 클론을 수행하기 위해서는 어떤 레파지토리에서 파일을 가져올 것인지에 대한 정보가 필요한데, 이 정보는 위에서 설명했듯이 URL로 표현한다. HTTPS 프로토콜이나 SSH 프로토콜을 사용하여 소스를 클론할 수 있는데, 보통 HTTPS를 많이 사용한다.

pull

pull 명령어는 리모트 서버의 최신 소스를 가져와서 로컬 소스에 병합(Merge)해주는 명령어이다. 만약 우리가 처음 소스를 클론한 후에 다른 사람이 리모트 서버를 상태를 갱신했더라도 리모트 서버가 우리에게 그 변경된 사항을 알려주지는 않기 때문에 우리가 직접 서버에 문의를 날려야 하는 것이다.

또한 pull은 단순히 리모트 서버에서 로컬로 소스를 가져온다의 개념보다는 가져와서 합친다의 개념이기 때문에 브랜치끼리도 pull을 통해 소스를 합칠 수 있다.

오픈소스에 관심이 많은 분이라면 Pull Request라는 단어를 들어보았을 것이다. 이 Pull Request는 “내가 작업한 브랜치를 가져가서 합쳐줘~“라는 의미이다.

fetch

fetch는 리모트 서버의 최신 이력을 내 클라이언트로 가져오되 병합은 하지 않는 명령어이다.
그럼 이 명령어가 pull의 하위 호환이 아닌가? 라는 생각이 들 수도 있는데, pull과 fetch는 조금 용도가 다르긴 하다. pull 같은 경우는 일단 묻지도 따지지도 않고 바로 리모트 서버의 최신 소스를 가져와서 내 로컬 소스에 합쳐버리기 때문에 조금 위험하긴 하다. 뭐 예를 들면 지금 리모트 서버의 최신 소스가 버그가 있는 상태일 수도 있지 않은가?

그래서 필자같은 경우 보통 로컬 소스와 리모트 소스의 변경 사항을 미리 비교해보고 싶을 때 fetch를 사용한다. 그리고 fetch를 잘 이용하면 이런 얌생이도 가능하다.

#!/bin/bash

git fetch --all -p; git branch -vv | grep ": gone]" | awk '{ print $1 }' | xargs -n 1 git branch -d

이 쉘스크립트는 필자가 예전에 만들어 놨던 친구이다. fetch를 통해 리모트 서버의 최신 내용을 받아온 뒤, branch 명령어를 사용하여 리모트 서버에서는 삭제되었지만 로컬에는 남아있는 브랜치를 찾아서 싹 다 지워주는 스크립트이다. 참고로 로컬에는 있지만 리모트에서 삭제된 브랜치는 브랜치 이름 뒤에 : gone이라는 문구가 붙어있기 때문에 구분이 가능하다.

add

원하는 변경사항만 골라 담는 add 명령어

$ git add . # 현재 디렉토리의 모든 변경사항을 스테이지에 올린다
$ git add ./src/components # components 디렉토리의 모든 변경사항을 스테이지에 올린다
$ git add ./src/components/Test.vue # 특정 파일의 변경사항만 스테이지에 올린다
$ git add -p # 변경된 사항을 하나하나 살펴보면서 스테이지에 올린다

이때 선택된 변경 사항들은 스테이지(Stage)라고 불리는 공간으로 이동하게 된다. 이때 git add <경로> 명령어는 해당 경로 안에 있는 변경 사항을 전부 스테이지에 올리게 되는데, 이게 영 불안하다 싶은 사람은 -p 옵션을 줌으로써 변경 사항을 하나하나 확인하면서 스테이지에 올릴 수도 있다.

이렇게 스테이지에 담긴 변경 사항들은 git status 명령어를 사용하여 확인해볼 수 있고, status 명령어에 추가적으로 -v 옵션을 사용하면 어떤 파일의 어떤 부분이 변경되었는지도 함께 볼 수 있다.

commit

add를 사용하여 원하는 변경사항을 스테이지에 올렸다면 이제 스테이지에 있는 변경 사항들을 포장할 차례이다. 이때 이 포장하는 행위를 commit이라고 한다. 커밋은 Git에서 상당히 중요한 부분을 차지하는 행위인데, 바로 Git이 하나의 커밋을 하나의 버전으로 정의하기 때문이다. 그렇기 때문에 특정 버전으로 어플리케이션을 변경이라는 기준도 당연히 바로 이 커밋이 된다.

push

커밋을 통해 포장된 변경 사항들은 push 명령어를 사용하여 리모트 서버로 업로드 된다. 이때는 커밋된 변경 사항들을 실제 리모트 서버에 전송하는 것이기 때문에 반드시 네트워크에 연결이 되어있어야 한다. 그리고 푸쉬할 때 반드시 A 로컬 브랜치는 A 리모트 브랜치에만 푸쉬해야 한다는 룰 따윈 없기 때문에 커밋들을 리모트 서버로 푸쉬할 때는 Git에게 “어떤 리모트 서버의 어떤 브랜치로 푸쉬할 것인지”도 함께 알려줘야 한다.

두 개의 브랜치를 합쳐보자

각자의 브랜치에서 작업을 계속 진행하다보면 언젠가 두 브랜치를 합쳐야 하는 날이 다가온다. 이때 두 개의 브랜치를 합치는 행위를 브랜치 병합(Branch Merge)이라고 한다. Git은 Merge, Merge and Squash, Rebase 각각 특색있는 3개의 브랜치 병합 기능을 제공한다. 결국 이 3개의 명령어 모두 두 개의 브랜치를 합친다는 행위는 같지만, 합치는 방법도 다르고 버전 히스토리도 다르게 남기 때문에 적재적소에 이 기능들을 잘 이용한다면 팀원들에게 이쁨받는 깃쟁이가 될 수 있을 것이다.

Merge

머지(Merge)는 제일 기본적인 브랜치 병합 기능으로, 합치려고 하는 대상 브랜치의 변경 사항을 타겟 브랜치에 모두 반영하면서 머지 커밋(Merge commit)을 남긴다.

Merge squash

일반 머지는 머지가 되는 대상 브랜치의 모든 커밋이 남아있는 상태에서 타겟 브랜치로 합쳐지지만 머지 스쿼시는 대상 브랜치의 모든 커밋을 모아서 하나의 커밋으로 합치고 타겟 브랜치에 머지하는 방식이다. 사실 이 기능의 정확한 이름은 Merge "and" Squash이다. 즉, 스쿼시도 머지와 같이 독립된 하나의 개념이라는 것이다. 스쿼시는 커밋을 여러 개 합친다는 개념이기 때문에 하단에 후술할 rebase 명령어와 함께 사용하여 현재 브랜치의 커밋을 합칠 때도 사용한다.

Rebase

리베이스(Rebase)도 머지(Merge)와 마찬가지로 브랜치를 다른 브랜치로 합칠 수 있는 기능이다. 단 머지와 차이가 있다면 바로 합치는 방식이다. 머지는 말 그대로 두 개의 브랜치를 하나로 합치는 기능이기 때문에 A 브랜치의 변경 사항 전부를 B 브랜치에 푸쉬하는 것과 동일하다. 그렇기 때문에 머지를 사용하여 브랜치를 합치게 되면 반드시 머지 커밋(Merge commit)이 남게 된다.

반면 리베이스는 단순히 합치는 것이 아니라 말 그대로 브랜치의 베이스를 변경하는 것이다. 방금 전 예시의 feature 브랜치를 master로 리베이스하게 되면 마치 feature 브랜치의 변경 사항들이 master의 변경 사항이었던 것처럼 히스토리가 기록된다.

Cherry Pick

체리픽(Cherry Pick)은 다른 브랜치에서 어떤 하나의 커밋만 내 브랜치로 가져오는 기능이다. 체리픽이 하는 일을 보면 대상 브랜치의 커밋 하나를 가져와서 현재 브랜치에 병합하는 행위라고 느껴지지만 히스토리를 보면 병합되는 그림이 아니라 그냥 해당 커밋을 그대로 복사해와서 내 브랜치에 커밋되는 형태로 기록된다.

Stash

스태쉬(Stash)는 현재 작업 중인 변경 사항들을 잠시 스택에 저장할 수 있는 명령어이다. 이 명령어는 아직 마무리되지 않은 작업이 있는데 다른 브랜치로 체크아웃 해야하는 경우에 유용하게 사용할 수 있다.

Reset

리셋(Reset)은 지정한 커밋 당시로 돌아가는 것이다. 아예 시간을 되돌린다고 생각하면 된다. 즉, 리셋을 사용하게되면 지정한 커밋 이후의 히스토리는 모두 사라지게 된다. - 삭제됨

Revert

리벗(revert) 또한 리셋처럼 히스토리를 다시 되돌리고 싶을 때 사용하는 명령어이다. 리셋이 지정한 커밋 이후의 모든 히스토리를 없애버렸다면 리벗은 특정 커밋의 변경 사항을 되돌리는 기능이다. 이때 해당 커밋을 되돌린다고 해서 히스토리에서 그 커밋을 삭제하는 것이 아니라, 되돌리고자 하는 커밋의 내용을 반전시키는 것이다. - 삭제는 안됨 되돌리는 히스토리가 생기는거임

profile
https://github.com/jsw4215

0개의 댓글