올해 동아리 대표로 활동하게 되면서 어디서부터 일반 부원들을 가르쳐야 할까 고민하다가, 생각보다 많은 학생들이 모르던 Git에 대해 다루게 되었다. (전공자라도 별도로 프로젝트나 웹에 대해 공부를 해보지 않은 분들은 모르는 경우가 많았다.)
어차피 git을 검색해볼 정도라면 대부분 프로젝트를 이제 막 처음 시작해보는 분들이 많을 것 같으니, 최대한 쉽고 간단하게 정리하려고 노력했다.
git branch를 나누고 팀플을 진행하는 방식과 commit 내역을 관리하는 내용까지 쓰려니 너무 길어져서 다음 포스트에 이어적을 계획이다.
목차
1. What is Git & Github?
2. Git command & flow
3. git init, git status : git 영역을 생성하고 확인하자.
4. git add : commit할 내역을 추적하자
5. git commit : 변경사항을 저장소에 저장하자
6. git push : 로컬 저장소의 커밋 내역을 클라우드 서버 저장소에 올리자
7. git clone vs git fork : clone과 fork의 차이는 뭔가요?
8. git fetch & git pull : 클라우드 서버 저장소의 커밋 내역을 받아오자
1. What is Git & Github?
📌 Git의 역할
정의를 그대로 말하면 Git은 "오픈 소스 버전 관리 프로그램"이고
쉽게 풀어 보자면 "모든 버전마다 파일을 기록하고 체계적으로 관리하는 프로그램" 이다.
git의 역할을 이해하는 가장 좋은 방법은 git이 없는 환경을 가정해보면 된다.
코드를 열심히 작성하다가 의미있는 작업이 끝났을 때마다 작업하고 있는 소스 코드 혹은 폴더 전체를 저장해두고 싶을 수 있다.
가장 간단한 방법은 Working directory 전체를 압축시켜 이름을 잘 붙여준 후, 백업 폴더에 저장해두는 방법이다.
하지만 이 방법은 몇 가지 문제가 있다.
첫 째, 변경 사항을 파악하기가 굉장히 힘들다.
둘 째, 백업을 할 수록 차지하는 용량이 점점 커질 것이고, 순서를 파악하기가 힘들어진다.
셋 째, 위의 두 가지 사항과 다른 여러 측면에서 살펴봐도 팀플을 이런 식으로 진행하기에는 무리가 있다.
이런 문제점들을 해결할 수 있는 것이 바로 Git이라는 프로그램이라고 생각하면 된다.
커밋을 한 파일들은 변경한 코드의 변화 전후 차이를 비교하여 스냅샷을 찍고, 이를 또 엄청 작게 압축시켜 저장한다.
리눅스 터미널에서 diff라는 명령어를 사용해본 사람을 알겠지만, git의 변화 전후 차이를 비교하는 방식은 매우 효율적으로 작동한다.
📌 Git과 Github
참고로 Git과 Github의 차이가 처음에 다소 어려울 수 있을 텐데, 밑에서 다룰 자세한 내용들을 이해한다면 대충 이해할 수 있을테니 여기선 간단하게 설명하고 넘어갈 것이다.
Git은 개인 컴퓨터(Local 환경)의 로컬 저장소(Repository)에 버전을 커밋하여 관리하고 기본적으로 서버를 구축하지 않는 이상 다른 사람이 접근하지 못 한다.
하지만 개인적인 git 서버를 구축한다는 건 초심자가 시도하기엔 상당히 많은 양의 CS지식을 요구하기 때문에 어렵다.
그래서 Github, Gitlab과 같은 곳에서 "야, 너네가 각자 PC에 저장하고 있는 커밋 내역들 우리 서버에 업로드해. 그럼 우리가 여러 기능들 지원해줄게!"하면서 유도하는 것이다.
요약하자면 Git은 일종의 프로그램이고, Github은 Git을 사용하는 유저들을 위한 클라우드 기반의 호스팅 서비스(로컬 서버 밖에 있는 데이터 베이스)다.
💡 Github은 버전 관리, 소스코드 공유, 분산 버전 제어 등의 기능을 제공하는 원격 저장소이다.
2. Git command & flow
우선 git 명령어를 알아보기 전에 코드를 작성하는 환경(Working directory), 커밋할 내역을 추적하는 Staging Area, 커밋한 내역을 저장하는 Local Repository, 서버에 커밋한 내역을 올렸을 때의 Github Repository (보통 협업에서 Repository를 언급하면 깃헙과 같은 서버를 지칭하는 것.)로 구분하여 각 명령어가 어떤 기능을 하는지 설명할 것이다.
git 명령어는 주로 사용하는 것들 위주로 골랐다.
이번 포스팅에서 언급하지 않은 내용은 다음 포스팅에 나올 듯.
- git init : 현재 파일 경로에 git 저장소를 초기화한다. (프로젝트 시작 시)
- git status : 현재 working dir과 staging 영역의 상태를 표시한다.
- git add : commit 하고자 하는 파일들을 선택하여 staging 영역에 등록한다.
- git commit : local repository에 변경 사항들을 저장한다.
- git push : 원격 저장소에 커밋 내역을 반영한다.
- git clone : github의 오픈 소스나, 도중에 프로젝트 일원으로 참여할 때 원격 저장소를 로컬에 복제
- git fork : 보통 프로젝트 일원은 아닌 경우(외주), 내 원격 저장소에 repo를 복사하여 원본에 반영시킬 때는 pull request를 보내 허락을 받아야 한다.
- git fetch: 원격 저장소의 변경사항을 들고오지만 merge는 따로 하지 않음. (변경내용 확인 용이)
- git pull : 원격 저장소의 변경사항을 확인하고 로컬 저장소로 들고 와 자동으로 merge해준다.
- git merge : git fetch된 변경사항이나 브랜치를 합칠 때 사용한다.
- git rebase : branch의 base를 옮긴다.(여기서부턴 branch 개념을 알아야 이해가 가능)
- git log : git commit 내역(snapshot)을 확인한다.
- git branch : git branch 확인 및 생성이 가능하다.
- git checkout : Header를 옮긴다.
참고로 지금 당장 전부 외울 필요 없다. 어차피 팀 프로젝트를 해본다면 외우기 싫어도 손이 먼저 움직이고 있을 텐데, 벌써부터 암기해봐야 다 잊어먹는다.
보통 git을 처음 공부하는 단계에서는 세 가지 명령어만 기억해도 된다.
git add, git commit, git push 앞으로 질리도록 사용할 커맨드와의 첫만남이다.
💡 명령어들의 대략적인 Flow
3. git init, git status : git 영역을 생성하고 확인하자
git init은 말 그대로 초기화(initialize) 작업이다.
보통 프로젝트를 시작하는 사람이 사용하고, 나머지 팀원은 github에 올라온 url을 이용해서 git clone을 하면 된다.
당연한 거지만 git init은 현재 위치의 디렉토리부터 시작해서 하위 디렉토리를 모두 현재 git의 영역 내에 있다고 간주하기 때문에 "현재 위치"를 이동하는 것을 잊지 말자.
📌 매우매우 자주 사용하는 git status
사실 git status를 4번으로 했어야 했는데, 이 커맨드를 추가하는 걸 잊어먹었다가 제목 수정하는 게 너무 귀찮아서 init에 끼워파는 중. ㅎ
working 디렉토리와 staging area 상태를 간단하게 볼 때 가장 좋은 방법이다.
현재 dir에서 git status를 쳐봤더니 Untracked files에 'COG_ICT/' 디렉토리라는 것이 있다고 알려준다.
특정 코드를 수정하고 git status를 쳐보면 modified, 즉 수정된 파일이 무엇인지 보여준다.
git add를 하면 초록불이 들어오는데 이 상태가 되면 "staging area"에 들어갔다고 한다.
git add/commit을 하기 전에 확인하는 습관을 들이는 것을 권장한다.
4. git add : commit할 내역을 추적하자
혹시 밑에서 설명할 내용이 이해가 안 간다면 지금은 이렇게 이해하고 다음으로 스킵하면 된다.
"git add는 git이 주시해야 할 파일을 타겟팅 해주는 명령어다." (작년의 이맘때 내가 이해한 방식..😄)
'git add .'은 현재 위치에서 staging 영역에 올라가지 않은 모든 파일을 stage한다.
만약 그 중에서 특정 파일만 선택하고 싶다면 'git add (파일or디렉토리명)'을 써주면 된다.
🤔 Staging Area? 왜 필요하죠?
이 밑에 내용은 어느정도 git 경험이 있는 사람이 아니고서야 이해하기가 어려우니, git을 처음 배우기 위한 목적으로 해당 글을 읽고 있다면 넘기는 것이 좋다.
Git은 commit을 하여 local repository에 반영하기 전에 "commit을 할 내역을 추적"하는 특이한 과정이 존재한다.
팀플이 아니라면 보통 개발자는 방구석 공장의 기계마냥 git add-commit-push를 반복하고 있는데, 대체 staging이라는 영역은 왜 존재해서 commit-push만 하면 될 것을 일거리를 추가해서 귀찮게 하는 걸까?
git을 처음 설명할 때 그랬듯이 staging 영역이 없을 경우를 생각해보면 된다.
git add는 전체가 아니라 특정 파일 혹은 디렉토리를 타겟팅하여 git이 주시할 대상을 정해줄 수 있다.
만약, 이런 과정이 없다면 매번 전체 파일을 커밋하는 것은 비합리적이므로 커밋할 파일을 하나하나 골라내야 한다.
일련의 과정들이 메모리 상에서 이루어져야 한다는 의미가 되는데, 이는 다루기가 굉장히 까다롭고 귀찮은 문제로 변질된다.
예를 들어, branch를 merge를 하는 과정에서 일부의 파일만이 충돌한 상황을 가정하자.
그러면 충돌한 파일을 직접 수정하는 과정(resolve)이 필요한데, 그동안 충돌하지 않은 파일은 메모리에 두기보단 디스크에 저장해두는 것이 낫다.
즉, 여기서 말한 디스크가 Staging Area이고, 이곳에 충돌하지 않은 파일들을 담아두고 그 동안 resolve를 해주면 된다.
처음 공부할 때는 'add가 대체 정확히 뭔데..???'라는 생각이 들 수 있지만, 어느정도 git에 대한 이해도가 올라가면 충분히 그 존재 이유를 납득하게 될 수 있을 것이다.
5. git commit : 변경사항을 저장소에 저장하자
git commit을 하면 local 환경의 repository에 변경 사항 전/후의 snapshot을 찍어 남긴다.
코드 한줄한줄을 모두 저장하므로 용량이 제법 커질 수도 있지만, 소스 코드를 극한으로 압축하여 조그만 청크로 만들어버리고는 diff 결과만을 보관하는 방식으로 용량을 관리한다.
하지만 그렇더라도 무분별한 commit을 하다보면 상당히 용량이 커질텐데 이 경우엔 어떻게 하냐는 의문이 들 수 있지만 이건 git log 파트에서 다루도록 하자.
중요한 것은 commit을 하면 Local 영역(개인 공간)의 repository에 소스 코드가 저장되었다고 생각하면 된다.
6. git push : 로컬 저장소의 커밋 내역을 클라우드 서버 저장소에 올리자
Git Repository Server는 개인적으로 구축하려면 할 수도 있다.
하지만 굳이 로컬단의 commit 내역을 서버에 올리는 행위는 주로 프로젝트 목적일 텐데, 팀원들의 네트워크 접속을 모두 계산하고 견딜 수 있는 클라우드 시스템을 만들어낼 수 있다면 내 블로그 따위를 보고 있을리가 없다. (오히려 내가 그 사람의 블로그를 참고하고 있을 것이다..)
현재 디렉토리의 local git repo와 github의 원격 리모트를 잘 연결해주었다면 push한 소스 코드가 쭉쭉 올라가게 되고
유료 버전이 아닌 이상 모두 오픈 소스로 공개되어 누구나 코드를 참조할 수 있게 된다.
💡 git push 강제 모드 옵션
git을 쓰다보면 push를 했는데 안 되는 경우가 존재한다.
한 번은 팀원이 merge 작업을 제대로 끝마치지 않은 상태로 push를 하는데 잘 안 되어서 강제 옵션을 걸었다가 branch가 제대로 꼬인적이 있었다. (헤더가 엉뚱한 곳을 가리키고, 서로 이상한 곳을 origin으로 둔 채 push를 하고 난리도 아니었다.)
결론만 말하자면, 모르면 쓰지 마라. git 작업에서 강제로 뭔가를 해서 좋은 결과를 볼 확률이 몇이나 될진 모르겠지만 적어도 본인이 작성한 명령어가 어떤 의미를 가지지도 못 하는 채로 push했을 때 그 확률은 0에 수렴할 것이다.
git 명령어를 다루다가 잘 모르겠으면 솔직하게 팀원들에게 도움을 구하자.
참고로 그 때 해당 branch를 날려버리고 이전 헤더의 데이터를 불러와 전부 덮어씌웠다.
7. git clone vs git fork : clone과 fork의 차이는 뭔가요?
(1) git clone
git clone은 repository를 내 local machine에 복사하여 새로운 저장소를 생성한다.
원본 repository의 remote를 origin으로 가지고 있기 때문에, 권한이 없다면 push가 불가능하다.
보통 팀 프로젝트의 일원이라면 clone을 하여 작업을 시작하면 된다.
(2) git fork
한 번은 프로젝트 진행을 위해서 git repo를 생성하고 각 팀원들에게 기능을 구현해오라 했었다.
이후 팀원들이 분명히 push를 했다는데, 정작 git pull로 업데이트를 해봐도 아무런 변경 사항이 없다고 뜬 적이 있었다.
알고보니 팀원들이 전부 git fork로 가져갔던 건데, 지난 팀에서는 다들 이렇게 했다고...🤦♂️
fork라는 단어만 봐도 알 수 있듯이 이건 찍먹용이다.
얼핏 보면 clone과 똑같이 내 저장소로 복사해오는 거 아니야? 라고 느낄 수 있지만 clone은 내 git repo로 복사를 해오지 않는다는 점을 생각해보라.
clone은 원본의 remote까지 몽땅 내 local로 옮겨오지만 fork는 내 git 저장소로 일단 복사하고, 이 복사본을 clone함으로써 remote가 복사본을 향해있다.
권한이 없다면 원본을 수정할 수 없는 것은 똑같지만 pull request를 보내서 내가 작성한 소스 코드와 merge 작업을 해달라고 요청을 할 수는 있다.
그러면 해당 저장소의 관리자가 코드 리뷰를 하고 병합 여부를 결정하면 된다.
즉, git fork는 프로젝트와 관련이 없거나 외주를 받고 몇 가지 기능을 구현해주는 사람들이 하는 방법이지 팀원이 git fork로 repo를 가져간다? 제발 그러지 마세요..
기깔나게 설명이 잘 되어있으니 한 번 참고해보면 좋을 영상
8. git fetch & git pull : 클라우드 서버 저장소의 커밋 내역을 받아오자
간단하게 fetch + merge를 한 것을 pull과 동일하게 보면 된다.
(1) git pull
원격 저장소로부터 필요한 파일을 다운받고 자동으로 merge시킨다.
참고로 이 자동으로 merge하는 것이 완벽하질 않아서 충돌이 발생하면 개발자가 직접 수정해주어야 한다.
충돌난 부분은 diff 명령어를 실행했을 때처럼 보여주는데, 처음 이걸 접해보는 코린이는 상당히 난감한 상황에 빠질 수도 있다.
(2) git fetch
원격 저장소로 부터 필요한 파일을 받기는 하는데 merge를 하기 전 단계에서 멈춘다.
즉, 개발자가 직접 변경 사항을 직접 확인해야 하는 경우에 사용한다. git diff HEAD origin/main
혹은 commit이 얼마나 됐는가에 대한 정보도 얻을 수 있다. git log --decorate --all --oneline
git pull을 하면 아무래도 알아서 merge 작업이 수행되다 보니 정확히 변경사항이 어딘지 개발자가 파악할 수 없다.
물론 이걸 매번 파악하는 것도 이상한 일이고 대부분은 pull을 사용하겠지만, 그럼에도 신중에 신중을 더해야 하는 작업이 있다면 fetch로 받아오는 것 또한 하나의 전략이 될 수 있다.