9주차: 협업을 위한 git (구진선) - GooJinSun/Shooting-star-of-my-music-chart GitHub Wiki
오늘 세미나의 목표는 git을 완벽하게 다루는 것도, git을 엄밀하게 이해하는 것도 아닙니다
git은 이런 느낌의 툴, 이럴 때 유용하다!! 를 이해하면 됩니다
세미나, 코딩데이에서 접했던 git에 대해 빠르고 간략하게 복습한 뒤에
git의 원리와 중요한 개념에 대해 짚어보고, 협업을 위한 간단한 git, github 실습을 함께 해봅시다
1주차 웹의 기초 세미나에서 git과 github의 필요성에 대해 다룬 영상을 보았습니다
git의 필요성은 크게 버전 관리와 협업, 둘이 명확하게 나뉘진 않지만 두 가지로 요약할 수 있습니다
-
버전 관리
- 이전 버전의 기능을 복원해야 할 때
- 바뀐 코드를 확인해야 할 때
- 누군가가
잘못 합쳐놓은 코드(ㅠㅠ)를 복원해야 할 때 등등...
-
협업
- 코드 공유
- 코드의 병합(merge)
- 프로젝트에 영향을 주지 않고, 새로운 기능 개발 / 버그 수정
- 코드 병합 후 발생한 에러 추적 등등...
사실 오늘 git 세미나를 한다고 했지만, 여러분은 이미 git을 사용해 프로젝트의 버전 관리를 하고 있습니다
git add .
git commit -m "2-CRUD-homework"
git push
어차피 commit할 건데, 왜 add가 필요한가요?
git을 사용한다는 것은 프로젝트를 의미있는 버전으로 나누어서 관리하겠다는 것입니다
따라서 혹시나 생길 상황을 위해, commit할 내용을 자유롭게 조정할 수 있어야겠죠?
예를 들어, 이전 세미나에서의 내용을 커밋하는 것을 까먹었을 때
한 번에 두 세미나의 내용을 commit하게 된다면 버전 관리의 의미가 퇴색될 수 있습니다
commit은 작업 하나의 의미(하나의 세미나는 하나의 commit!)를 가져야 하는데 말이죠
따라서, git add [file name] 라는 명령어로 원하는 파일만을 커밋할 수 있도록 add가 도와주는 것입니다!
[지옥에서 온 Git] Staging Area - 파일을 선택적으로 commit 버전에 포함시키기]
이전 커밋 이후의 변경 사항을 모두 하나의 커밋에 준비시킬 때 사용하는 git add .는 사용하기에 편해 보이지만,
어떤 내용이 커밋에 포함되는지를 확인할 수 없기 때문에 이 명령어를 습관적으로 사용하는 것은 좋지 않습니다!
git의 다른 명령어들을 공부해보고, 보다 의미있는 단위로 프로젝트를 관리해 보도록 합시다:)
지금 개념을 확실히 알 필요는 없지만, 우리는 0주차에 저장소(pangpangmandu/snulion7th), 레포(repository)를 fork하기도 했습니다
이렇게 저장소를 fork하면 원래 프로젝트(pangpangmandu/snulion7th)에 영향을 미치지 않고 자유롭게 코드를 짤 수 있고, 한 저장소를 함께 fork한 다른 저장소들도 쉽게 열람해 볼 수 있습니다
~~이는 모두 운영진들이 여러분이 얼마나 열심히 하고 있는지 쉽게 감시하기 위함……이랍니다~!~!^^~~
저장소를 fork한다는 것은 무엇을 의미하나요?
다른 사람의 프로젝트에 변경 사항을 제안하거나, 다른 사람의 프로젝트를 출발점으로 자신의 아이디어를 발전시키는 데에도 사용됩니다
[참고] fork a repo
그리고 우리는 포크된 저장소([github-username/snulion7th)를 git clone <repository URL>
명령어를 통해 각자의 컴퓨터(local)에 이 저장소를 저장했고,
계속해서 각자의 컴퓨터에 있는 저장소, 즉 로컬 저장소와 Github에 올려진 이 저장소, 즉 원격 저장소를 지속적으로 동기화시키고 있습니다
여기서 잠깐 로컬 저장소와 원격 저장소의 개념에 대해 짚고 넘어가려고 합니다
로컬(loacl) 저장소와 원격(remote) 저장소
-
로컬(loacl) 저장소
- 각자의 컴퓨터
- 핸드폰의 사진 앱
-
원격(remote) 저장소
- github과 같은 원격 서비스
- 유튜브나 구글 포토
이러한 개념을 바탕으로 본다면, 레포를 clone한다는 것은 원격 저장소를 clone해서 로컬 저장소에서 작업을 하고,
계속해서 git push
명령어를 통해 로컬 저장소의 변경 사항들을 원격 저장소에 동기화시킨다고 이해할 수 있겠죠?
이제 본격적으로 git의 원리와 중요한 개념에 대해 배워봅시다!
git이 어떻게 작동하는지 흔히 오해하는 부분 중에 하나가, git이 파일 간의 ‘차이만’ 저장한다고 알고 있는 것입니다
바로 이렇게 말이죠
프로젝트는 진행 상황에 따라 각각의 파일에 변경 사항이 있을 때도, 그렇지 않을 때도 있습니다
만약 차이만 을 저장한다면, 이렇게 각 버전마다 ‘차이’를 확인하고 차이가 있다면 그 차이만을 저장하는 방식이겠죠?
물론 git이 아닌 다른 버전관리시스템(VCS-Version Control System)들 중 이렇게 차이만을 저장하는 시스템도 있지만, git은 그렇지 않습니다
이러한 방식의 버전관리시스템을 '델타기반시스템'이라고 합니다
그럼 git은 어떻게 버전 관리를 하는 것일까요?
git은 파일을 스냅샷의 연속으로 취급합니다
이 때 ‘스냅샷’이란 말그대로 사진을 찰칵 찍어내는 것을 의미합니다
버전이 만들어질 때마다, 개발자들이 커밋 을 사용해 의미있는 단위로 나눌 때마다 git은 커밋 당시의 모든 파일들을 스냅샷으로 찰칵찰칵 찍어냅니다
이 때 변경 사항이 없는 파일들은 성능을 위해 해당 파일을 가리키는 정보만 남기고 변경 사항이 있는 파일들만 스냅샷으로 저장하게 되는 것입니다
이로 인해 최신 버전을 확인할 때, 처음 커밋부터 해당 커밋까지의 차이-델타 데이터를 모두 정리해야하는 차이를 저장하는 위의 델타기반시스템의 방식과 달리,
git은 변경 사항이 생긴 전체 스냅샷을 가지고 있기 때문에 엄청나게 빠른 속도를 자랑합니다
그렇다고 git이 commit간의 차이를 비교할 수 없는 것은 아닙니다!
git diff
와 git diff
의 다양한 옵션들로 차이를 확인해 볼 수 있고,
git blame 파일명
으로 해당 파일의 수정 이력을 볼 수도 있습니다
다음으로는 git이 어떻게 우리가 프로젝트의 버전 관리를 하도록 돕는지, 파일들을 어떻게 나누어 관리하는지를 알아볼 것입니다
오늘 세미나에서 가장 중요한 내용이니, 꼭 집중해서 들어주세요!!
git은 파일을 Modified, Staged, Committed 이렇게 세 가지 상태로 관리합니다
- Modified: 수정한 파일을 아직 로컬 데이터베이스에 커밋하지 않은 상태
- Staged: 현재 수정한 파일을 곧 커밋할 것이라고 표시한 상태
- Committed: 데이터가 로컬 데이터베이스에 안전하게 저장된 상태
또한, git이 관리하는 파일의 세 가지 상태는 git 프로젝트의 세 단계와 연결되어 있습니다
-
Working Directory: 프로젝트의 특정 버전,
git add
를 하기 전 modified 파일을 가지고 있는 디렉토리 -
Staging Area: 곧 커밋할 파일에 대한 정보를 저장,
git add
한 파일(staged 파일)들 ("index"라 부르기도 함) -
Git Repository:
git commit
한 파일들을 가지고 있고, 프로젝트의 메타데이터와 객체 데이터베이스를 저장하는 곳(git init을 하거나 저장소를 Clone 할 때 git repository가 만들어짐)
즉, git이 하는 일은 아래와 같이 요약할 수 있습니다
- Working Directory에서 파일을 수정한다 (Modified)
- 파일을 Stage해서 커밋할 스냅샷을 Staging Area에 추가한다. 모든 파일을 추가할 수도 있고 선택하여 추가할 수도 있다 (Staged - git add)
- Staging Area에 있는 파일들을 커밋해서 git repository에 영구적인 스냅샷으로 저장한다 (Committed - git commit)
git이 파일을 관리하는 형태는
git status
명령어를 통해서 확인할 수 있습니다!
- 멋사 도전 과제를 하기로 한 A, 하지만 세미나 전에 끝낼 수 있을지 모르겠다…
- 협업을 하고 있는 팀
브랜치(branch)는 코드를 통째로 복사해서 원래 코드와는 독립적으로 개발을 진행할 수 있도록 돕는 기능입니다
branch의 의미대로 메인 프로젝트에서 나뭇 가지를 하나 쳐서, 원래 프로젝트에 영향을 미치지 않고 독립적으로 개발을 하도록 돕는 것이죠!
그렇다면 브랜치를 어떻게 만들고, 브랜치를 옮겨다닐 수 있을까요?
git은 우리가 branch를 만들지 않았는데도, 기본적으로 하나의 branch를 만듭니다
바로 master 브랜치인데요, 지금 프로젝트에서 아무런 브랜치도 만들지 않았다면 master 브랜치 하나만 있다는 것을
git branch
라는 명령어를 통해 확인할 수 있습니다
위의 명령어는 현재 프로젝트의 로컬 저장소에 어떤 브랜치들이 있는지(브랜치의 목록) 와, 현재 어떤 브랜치에 있는지를 확인하는 명령어입니다!
새로운 브랜치 를 만들기 위해서는,
git branch jinsun(branch_name)
다른 브랜치로 이동(=checkout) 하기 위해서는,
git checkout jinsun(branch_name)
위의 두 명령어 (새로운 브랜치를 만들고, 그 브랜치로 이동하기) 를 하나의 명령어로 줄여서 사용할 수도 있습니다
git checkout -b jinsun(branch_name)
그리고 브랜치를 삭제 하기 위해서는, 다음과 같은 명령어를 사용하면 됩니다
git git branch -d jinsun(branch_name)
지금까지 살펴본 것은 로컬 저장소에서 브랜치를 어떻게 생성하고, 이동하고, 관리하는지였습니다
로컬 저장소와 리모트 저장소가 있는 것처럼, 브랜치도 로컬 브랜치와 리모트 브랜치가 있는데요,
안타깝게도, 로컬 브랜치는 자동으로 리모트 저장소로 전송되지 않습니다!!!!!
따라서 명시적으로 로컬 브랜치와 리모트 브랜치를 연결해 주어야 합니다!!
이러한 이유로 다음과 같은 것들이 가능합니다
- 리모트 저장소에 전송하지 않고 로컬 브랜치에만 두는 비공개 브랜치
- 다른 사람과 협업하기 위해 로컬 저장소에 전송한 협업용 브랜치
- 로컬 브랜치와 연결된 리모트 브랜치의 이름을 다르게 설정할 수도…!
실습을 해볼 예정이니, 지금은 너무 걱정하지 말고 가볍게 들어주세요!
로컬 브랜치와 리모트 브랜치 사이에 연결 고리가 있을 때, 이들을 부른는 또 다른 용어가 있는데요
리모트 브랜치와 직접적인 연결 고리가 있는 로컬 브랜치를 트래킹 브랜치(tracking branch) 라고 부르며,
트래킹하는 대상 브랜치, 즉 로컬 브랜치가 트래킹하는 리모트 브랜치를 업스트림 브랜치(upstream branch) 라고 부르기도 합니다
로컬 저장소에 만든 로컬 브랜치 'jinsun'을 같은 이름으로 리모트 저장소에도 만들기 위해서는,
git push origin jinsun
을 하면 됩니다!
이 때 origin은 원격 저장소를 의미합니다
저는 원격 저장소의 이름을 origin이라고 설정한 적이 없습니다! origin은 무엇을 의미하나요?
git 서버의 저장소를 하나 clone 하면 git은 자동으로 origin이라는 이름을 붙입니다.git init 명령이 자동으로 “master” 라는 이름을 가진 브랜치를 만드는 것과 마찬가지로 “origin” 도 git clone 명령이 자동으로 만들어주는 리모트 이름 입니다
origin 대신 booyah라는 이름을 붙여주고 싶을 때,
git clone -o booyah라고 옵션을 주고 명령을 실행하면 booyah/master라고 사용자가 설정한대로 리모트 이름을 생성할 수 있습니다
브랜치는 이처럼 매우 편리한 기능을 쉽게 제공하지만, 나뭇 가지가 지저분하게 관리된다면 전체 나무를 보기는 어렵겠죠?
따라서 실제 프로젝트에서는 나름의 규칙을 가지고 운영됩니다
가장 간단한 한 가지 예로, 보통 배포를 위한 안정된 코드만 master branch에 유지합니다
commit에도 의미가 있는 것처럼, 브랜치도 의미를 담아 사용하고 관리해봅시다!:)
이제 협업을 위한 간단한 git, github 실습을 해볼까요?
협업 프로젝트를 위한 레포를 만들고, 각자 브랜치를 만들어 수정 사항을 만든 뒤에 브랜치를 하나 합쳐볼 거에요!
실제 개발 협업 과정을 간략하게 실습해 보도록 하겠습니다
저를 포함한 여러분 모두가 하나의 팀으로 개발한다는 가정 하에 진행해보도록 할게요!
실습 시작 전 (add, commit, push) branch, checkout, merge, pull 만 알아두기!!!
먼저 제가 처음 레포를 만들기로 한 팀원이라고 가정하고, 개발 프로젝트를 함께 진행할 저장소를 Github에 만들어보겠습니다
- Github main 화면의 왼쪽 상단에 보이는 녹색의 New 버튼을 눌러주세요
- 레포 이름(Repository Name)과 설명(Description), public/private, Readme file 초기 설정등을 포함한 선택 정보들을 입력하고, Create Repository 버튼을 눌러주세요.
.gitignore
파일도 바로 생성할 수 있습니다! - 함께 협업할 Github 레포가 완성되었습니다!
구글 드라이브를 사용해보셨다면, 처음 공유 파일이나 폴더를 만들고 작성 권한을 주기 위해 공유할 사람들을 추가해 보셨을텐데요, Github도 마찬가지입니다
public으로 공개된 레포의 코드들을 모두 열람할 수 있지만,
코드를 함께 작성하고 수정하기 위해서는 레포를 만든 사람 이외의 사람들에게 작성 권한 이 필요합니다
여러분들을 레포에 초대해보도록 하겠습니다
- 새로 만든 레포 페이지의 맨 오른쪽 탭 settings 를 누릅니다
- 좌측 탭의 collaborator 를 누릅니다
- 유저 네임이나 이름, 이메일로 검색해서 추가합니다
- collaborator로 추가된 사람에게는 이메일이 도착합니다! 초대장을 받아 승낙해주세요!!
여러분 모두가 초대장에 승낙하는 동안, 저는 프로젝트 초기 설정을 해보도록 할게요
- 생성한 레포를 데스크탑에 clone합니다(clone방법은 다음 [Gihub 레포를 데스크탑에 clone하기 참고])
- clone한 폴더로 이동합니다
cd GitSeminar
- Django 프로젝트 시작하기
django-admin startproject GitSeminar .
** . **을 하는 것을 잊지 마세요!!
- 프로젝트의 언어와 시간대를 한국으로 맞춥니다
-
README
파일과.gitignore
파일을 작성합니다 - 변경 사항을 Github에 push하겠습니다
- 해당 레포 페이지에 들어가서 페이지 상단의 녹색 clone or download 버튼을 눌러주세요
- 저장소 링크를 복사해주세요
- 로컬에 저장소를 clone 해주세요
git clone <repository URL>
- 이제 협업 준비가 끝났습니다!!(와아-!)
팀 회의에서 각자 구현할 기능들이 정해졌습니다
편의상 여기서는 각자 자신의 이름을 가진 앱만 하나씩 만들어 보도록 하겠습니다
앞의 세미나에서 설명한 것처럼 master 브랜치의 로그를 깔끔하게 관리하고, 안정된 버전만을 master branch에 두기 위해
각자의 이름을 가진 branch를 직접 만들어서 그 브랜치에서 앱을 만들거에요!
- 먼저, 현재 브랜치를 확인합니다
$ git branch
- 자신의 이름을 가진 브랜치를 만듭니다
$ git branch jinsun
- branch가 만들어졌는지 확인해봅니다
$ git branch
- 이제 만들어진 각자의 branch로 checkout 해봅시다
$ git checkout jinsun
branch가 옮겨졌는지 확인도 해봐요!
이 때 유의할 점은 여기서 만들어진 branch는 자기 자신의 로컬에만 만들어졌다는 것을 유의해야합니다!
예를 들면, 저의 컴퓨터 안에서는 jinsun이라는 이름을 가진 브랜치와 git이 처음에 만드는 master branch만 존재하고,
collaborator인 여러분의 로컬 저장소에는 jinsun branch가 존재하는지는 알 수 없습니다
git branch
명령어를 통해 직접 확인해 보세요! 말그대로 여기까지는 ‘로컬’ 브랜치를 만든 것입니다!
- 각자의 branch에서 각자의 이름을 가진 장고 앱을 만들어주고, settings.py에 INSTALLED_APPS에 추가해주세요!
$ (django) > django-admin startapp jinsunPage
# settings.py
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
+ 'jinsunPage.apps.JinsunPageConfig',
]
...
와 앱 하나씩을 뚝딱 만들어냈네요!
이제 각자의 변경 사항들을 Github의 원격 저장소에 push 해보도록 하겠습니다
$ git add .
$ git commit -m "new jinsun app"
$ git push
fatal: The current branch jinsun has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin jinsun
push를 하려고 하자 이러한 메시지가 뜹니다
현재의 브랜치가 upstream branch가 없으니, 현재 브랜치를 push하려면 upstream branch를 설정하라고 하네요
이는 앞에서 설명한 것처럼 새로 만든 branch가 로컬에만 존재하기 때문입니다
로컬의 branch에서 작업한 내용을 push하기 위해서는 remote 저장소에 새로운 branch를 트래킹할 upstream branch를 만들어야 하는데요
여기 이 명령어 git push --set-upstream origin jinsun
를 통해서 “origin”이라는 이름을 가진 원격 저장소에 jinsun이라는 remote branch를 만들고 로컬의 branch와 이 remote branch를 연결시킵니다
git push --set-upstream origin jinsun
을 git push -u origin jinsun
으로 줄여서도 쓸 수 있습니다!
그리고 변경 사항까지 origin이라는 원격 저장소의 jinsun branch에 push합니다
$ git push --set-upstream origin jinsun
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 4 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 347 bytes | 347.00 KiB/s, done.
Total 4 (delta 3), reused 0 (delta 0)
remote: Resolving deltas: 100% (3/3), completed with 3 local objects.
remote:
remote: Create a pull request for 'jinsun' on GitHub by visiting:
remote: https://github.com/GooJinSun/snulion7th/pull/new/jinsun
remote:
To https://github.com/GooJinSun/snulion7th.git
* [new branch] jinsun -> jinsun
Branch 'jinsun' set up to track remote branch 'jinsun' from 'origin'.
push를 하는데 성공했습니다!
리모트 브랜치와 관련된 git 명령어들
리모트 브랜치 만들기
git push origin feature-01
리모트 브랜치와 로컬 브랜치 연결하기
git branch —set-upstream-to origin feature-01
리모트 브랜치를 만들고 로컬 브랜치 연결하기
git push -u origin feature-01
또는
git push —set-upstream-to feature-01
원격 저장소의 브랜치 목록 확인
git branch -r
모든 브랜치의 리스트를 확인
git branch -a
로컬의 모든 branch의 upstream branch 체크하기
git branch -vv
를 통해 로컬의 브랜치와 리모트 저장소의 브랜치가 어떻게 연결되어있는지를 살펴볼 수 있습니다
각자의 브랜치에서 맡은 개발을 하고, github 레포에 코드를 올릴 수 있게 되었습니다
이제 각자 개발했던 코드 중 안정적인 버전의 코드를 master
branch에 합쳐 보아야겠죠?
이 부분은 따라하지 마시고 그냥 보면 됩니다!!
- github 레포의 메인(code tab)에서 new pull request 버튼을 눌러줍니다
- 병합을 원하는 브랜치(base, compare)를 선택합니다
base 브랜치 에 compare 브랜치를 병합하는 것입니다
- 내용을 확인하고 create a pull request 버튼을 누릅니다
- Title / comment 을 필요시에 작성하고 pull request를 날립니다
- 팀의 규칙에 따라 팀원 몇명이 확인 후에, 충돌(conflict)이 있다면 해결하고 병합을 합니다!
이제 master
브랜치에 하나의 앱이 병합되었습니다
하지만 github에서, 리모트 저장소에서 병합이 된 것이 아니기 때문에 로컬 저장소에 받아와서 작업을 시작해야겠죠?
그리고 안정적 버전의 master
브랜치 코드를 작업하던 각자의 브랜치에 합치고 나서 개발을 시작해 보도록 하겠습니다
master
브랜치에 바뀐 코드가 앞으로의 작업에 영향을 줄 수도 있으니까요!
git status
로 커밋하지 않은 수정 사항, 현재 브랜치를 확인한 뒤에,
리모트 저장소의 master
브랜치에 있는 코드를, 현재 작업하고 있는 로컬 브랜치에 가져오고 병합합니다
git pull origin master
git log --pretty=format:"%h %s" --graph
로 병합 로그를 살펴봅시다!
`git pull` 과 `git fetch`
리모트 저장소의 소스를 로컬 저장소로 가져오는 방법에는 `git pull` 말고도 `git fetch`도 있는데요,
그 차이를 간단히 살펴보고, 적절한 상황에서 사용해 보아요!
- pull : 리모트 저장소의 소스를 로컬 저장소로 가져온다! 또한 현재 작업중인 소스들의 merge 작업까지 통합하여 수행한다
- fetch : 중앙 저장소의 소스를 로컬 저장소로 가져온다! 그러나 현재 작업중인 소스들을 변경하는 merge 작업을 하지는 않는다
- [github] issue
- [github] insights
- [github] project dashboard
- [trello] 업무 분배 / 업무 진행 상황 공유
Git - Book
git 간편 안내서
A Visual Git Reference
초심자를 위한 Github 협업 튜토리얼