버전 관리다!
개발자 협업 기능
GitHub
작업물의 백업본을 만드는 것
1.
레포지토리
a.
프로젝트 관련 디렉토리
b.
프로젝트 디렉토리의 버전을 관리할 수 있다.
2.
커밋
a.
프로젝트 디렉토리의 모습을 남기는 하나의 동작으로 남기는 것
b.
고정된 프로젝트 버전 또한 커밋이라고 함
Git 써보기
레포지토리 만들기
git init: 깃을 init시키는 것 ⇒ 레포지토리를 만들고, 안에 숨어있는 파일을 만든다.
첫 commit에는 꼭 깃에게 commit한 사람 알려주기!
git commit -m "commit message"
git add .
git의 3가지 작업 영역
1.
working directory
작업을 하는 프로젝트 디렉토리
2.
staging area
git add를 한 파일들이 존재하는 영역,
커밋을 하게 되면 staging area에 있는 파일들만 커밋에 반영됨
3.
repository
working directory의 변경 이력들이 저장되어있는 영역
git add 더 자세히 알아보기
한 번에 모두 추가하는 방법
git add .
Git이 보는 파일의 4가지 상태
상태를 가진다
untracked 상태
tracked 상태
staged상태
unmodified 상태
modified 상태
Git add 취소하기
git reset 파일명
GitHub 계정과 Remote Repository만들기
레포지토리를 전송하는 것
원격 레포지토리를 만든다.
local repository에서 바뀐 내용을 Remote Repository에도 반영하기
새로운 커밋도 리모트 레포지토리에도 보내려면 따로 작업을 해야함
그 작업 ⇒ git push
Remote Repository에서 로컬 레포지토리로 들고오기
git pull
1.
안정성 ⇒ 로컬이 날라가면 그냥 리모트에서 받아오면 된다.
2.
협업 가능 ⇒ 받아와서 붙이고 받아와서 붙이고
git push는 본인만 할 수 있다.
근데 git pull은 누구든 할 수 있는데, 그래서 git push를 하려면 다른 사람들을 세팅에서 추가해줘야한다.
다른 프로젝트 가져오기
git clone url
README.md 를 좀더 예쁘게
•
이 프로젝트가 어떤 프로젝트인지 설명하거나
•
프로그램의 주요 사용법을 알려주거나
•
프로그램을 실행시키려면 어떤 사전 작업이 필요한지를 알려주는
Commit 다루기
커밋 히스토리 살펴보기
git log
위로 올라갈 수록 새 커밋
commit이라는 글자 뒤로 나오는 긴 문자열 → commit의 id이다.
예쁘게 하려면 git log —pretty=oneline
어떤 파일이 어떻게 변했는지 알고 싶다.,
git show commit id
— ⇒ 이전 상태
+++ 지금 상태
m옵션 없이도 커밋 메시지를 남길 수 있다.
git commit ⇒ 새로운 파일이 뜬다.
기본 설정된 text 에디터를 띄운다.
여기서 commit message를 입력하는 것
복잡하고 긴 커밋 메시지를 쉽게 남길 수 있다!
최신 커밋 수정하기
최신 커밋을 수정할 수 있는 기능이 있다.
git add .
git commit —amend
commit message를 수정할 수 있다.
aliasing
git config alias.history 'log —pretty=online'
두 커밋 간의 차이 보기
git diff 이전 id 더 최근 id
HEAD의 의미
HEAD : 가장 최근에 한 커밋을 가리킴
매번 더 새로운 커밋을 가리킨다.
HEAD가 있는 커밋에 따라서 working directory를 구성한다
git reset : HEAD가 이전 commit을 가리키도록 한다.
과거 커밋으로 아예 돌아가고 싶을 때 사용한다. 거기서부터 다시 시작
git reset의 3가지 옵션
—hard : head가 이전 커밋을 가리킴,
—mixed
—soft
몇 개의 영역까지 reset을 하느냐
hard : 3개 , mixed: 2개, soft: 1개
일단 git add . 를 해서 staging area에 올려놓은 거는 사라지지 않는다.
—hard 옵션은 커밋 이후를 다 날리는 거다.
하지만 이렇게 쓰지 않고 이렇게 써도 됩니다.
git reset --hard HEAD^
HEAD^는 현재 HEAD가 가리키고 있는 커밋의 바로 이전 커밋을 나타냅니다. 즉, 이 상황에서는 5번 커밋을 나타내죠.
실제로 실행해보면 아래와 같이 HEAD가 이제 5번 커밋을 가리키는 것을 알 수 있습니다.
만약 '바로 이전'보다 좀더 이전에 있는 커밋을 나타내고 싶다면 아래와 같이 쓰면 됩니다.
git reset --hard HEAD~2
여기서 HEAD~2는 현재 HEAD가 가리키는 커밋보다 2단계 전에 있는 커밋을 나타냅니다. 지금 HEAD가 5번 커밋을 가리키고 있죠? 이 상태에서 위 커맨드를 실행해보면
HEAD가 이제 3번 커밋을 가리킵니다.
방금 본 표기처럼 HEAD~x는 현재 HEAD가 가리키는 커밋보다 x단계 전에 있는 커밋을 말합니다.
앞으로 git reset을 할 때 커밋 아이디를 써주기 귀찮다면,
HEAD가 현재 가리키는 커밋을 기준으로 한 상대적인 표현법인
•
HEAD^
•
HEAD~2
같은 것들을 쓰는 게 더 편합니다. 참고하세요.
git 태그 달기
보통 프로젝트에서 주요 버전의 시작점이 되는 커밋에 이렇게 태그를 다는데요. 잠깐 아래 그림을 봅시다. 아래 그림에서 첫 번째 커밋에는 Version 1이라는 태그를 달고, 여섯 번째 커밋에는 Version 2라는 태그를 달고 싶다고 해봅시다.
아래와 같은 형식으로 태그를 달아줄 수 있는데요.
git tag [태그 이름] [커밋 아이디]
한번 해볼게요.
총 2개의 태그를 달았습니다.
그 다음에 이 프로젝트 디렉토리에 있는 모든 태그를 조회해볼게요.
git tag
라고 쓰면 됩니다. 실행해보면
제가 추가했던 Version1, Version2 태그들이 보이죠?
그 다음 각 태그와 연결된 커밋이 보고 싶으면,
git show [태그 이름]
의 형식으로 실행해주면 됩니다. 저는 Version_1 태그가 가리키는 커밋을 살펴볼게요.
Version_1 태그에 연결된 커밋의 정보가 잘 보이죠? 이렇게 새 버전의 시작점이 되는 커밋처럼, 특히 그 의미가 중요한 커밋들은 이렇게 태그를 달아주면 나중에 프로젝트의 이력을 파악할 때 도움이 됩니다.
브랜치 사용하기
브랜치 만들기
git branch premium(브랜치 이름)
브랜치로 이동하기
git checkout premium (브랜치 이름)
브랜치 다루기
브랜치 조회하기
git branch
브랜치 삭제하기
git branch -d 브랜치 이름
만들면서 이동하기
git checkout -b test
브랜치 merge하기
다른 브랜치에 있는 거를 그대로 들고와서 병합할 수 있다.
다른 브랜치에서 한 커밋을 들고와서 병합
merge시 conflict
컨플릭트가 발생한 파일을 열고
머지의 결과가 되었으면 하는 파일을 수정
머지를 취소할 수도 있다.
git merge —abort
conflict의 원인 → 둘다 modified된 상태이기 때문이다. 한 쪽이 확실하게 commit을 해놓고 merge하면 문제가 안 생김
HEAD와 브랜치의 관계
branch → 어떤 커밋을 가리키는 존재 : 포인터
master 브랜치가 새로운 커밋을 가리킨다.
Head : 커밋을 가리키는 포인터 → branch → commit → working directory
새로운 커밋을 하면 master 브랜치가 새로운 커밋을 가리킨다.
그래서 HEAD는 자동으로 바뀌는 거다.
새로운 브랜치를 파면 master 브랜치가 가리키던 커밋을 새로운 브랜치도 가리킨다.
checkout은 head를 브랜치에 따라 바뀌는 상황
머지를 하면 새로운 커밋이 나온다: 머지 커밋 : 헤드가 가리키던 커밋에 다른 브랜치가 가리키던 커밋을 합쳐서 새로운 커밋을 만드는 작업
git reset
git reset을 하면 HEAD가 가리키고 있는 브랜치가 가리키는 커밋이 바뀌는 거다.
1.
과거의 커밋으로 git reset을 한다고 그 이후의 커밋들이 삭제되는 게 절대 아닙니다. 계속 남아있습니다.
2.
git reset은 과거의 커밋뿐만 아니라 현재 HEAD가 가리키는 커밋 이후의 커밋으로도 할 수 있습니다.
git checkout
git checkout 을 하면 head가 움직인다.
git checkout id ⇒ master브랜치와 head가 분리된다.
이렇게 head가 직접 커밋을 가리키는 상황이 바로 Detached Head이다.
⇒ 이거는 과거의 특정 커밋에서 새로운 브랜치를 만들고 싶을 때이다.
이렇게 detached Head인 상태에서
git branch premium을 하면 premium 브랜치가 새로 생긴다.
새로운 커밋을 만들지 않는 merge
머지(merge)에 관한 좀더 깊은 이야기를 해볼게요. 머지를 하면 새로운 커밋이 생긴다고 했습니다.
그리고 머지를 통해서 생겨난 커밋을 머지 커밋(merge commit)이라고 부른다고 했는데요.
이 그림을 보면 지금 master 브랜치에서 premium 브랜치를 머지해서 검은색의 머지 커밋이 생긴 것을 알 수 있습니다.
하지만 머지를 한다고 항상 이렇게 새로운 커밋이 생기는 건 아닙니다.
아래 그림를 보세요.
지금 저는 master 브랜치에 있죠? HEAD가 master 브랜치를 가리키고 있으니까요. 이 상태에서
git merge premium
을 실행하면 어떻게 될까요?
그럼 이렇게 됩니다.
premium 브랜치가 가리키던 커밋을, master 커밋도 똑같이 가리키게 되는데요. 지금 총 커밋 수는 그대로죠?
이렇게 새로운 커밋이 생기는 게 아니라 단지 브랜치가 이동하게 되는 머지를 Fast-forward 머지라고 합니다. Fast-forward는 어떤 영상이나 소리를 빨리감기(앞으로 감기)한다는 뜻인데요. 지금 master 브랜치가 더 최신 커밋으로 이동하는 모습이 꼭 빨리감기같죠?
어떤 경우에 이렇게 되는 걸까요?
커밋 히스토리에서 같은 선(line) 상에 있는 브랜치를 머지할 때 Fast-forward 머지가 이루어집니다. 방금 전에는 master 브랜치와 premium 브랜치가 둘다 같은 선 상에 있었죠? 바로 이런 경우입니다.
하지만 노트 초반부에서 봤던
이 그림처럼 두 브랜치가, 커밋 히스토리 상에서 분리된 2개의 선에 각각 존재할 때 머지를 하면 머지 커밋이 새롭게 생기는 거구요. .
그리고 이런 머지는 3-way merge라고 합니다. 이름이 3-way인 이유는 지금 1, 2, 3 표시한 3가지 커밋을 고려해서 머지를 하기 때문입니다. 지금 각각
•
(1)번 : 두 갈래로 갈라지기 전 공통 조상이 되는커밋
•
(2)번 : 한 브랜치가 가리키는 커밋
•
(3)번 : 다른 브랜치가 가리키는 커밋
인데요. 3-way merge는 자신만의 방식을 갖고 이 3가지 커밋을 기준으로 머지 커밋을 자동으로 만들어냅니다.
그 방식에 대해서 간단하게 알려드릴게요. 아래 표에는 master 브랜치와 premium 브랜치를 머지했을 때 다양한 상황별로 그 결과가 정리되어 있는데요.
각 컬럼(column, 열)에 대해서 설명할게요. 지금 모든 커밋에 sample.txt 파일이 있다고 가정할게요.
1.
base : 두 브랜치의 공통 부모 커밋의 sample.txt 파일의 내용 중 일부 = 위 그림 (1)번
2.
master : 마스터 브랜치의 최신 커밋의 sample.txt 파일의 내용 중 일부 = 위 그림 (2)번
3.
premium : 프리미엄 브랜치의 sample.txt 파일의 내용 중 일부 = 위 그림 (3)번
4.
머지 결과 : master 브랜치에서 premium 브랜치를 머지했을 때의 최종 결과
자, 각각의 경우에 왜 표와 같은 머지 결과가 생기는 건지 설명해드릴게요.
case1
지금 base가 A이고, master는 A, premium은 B죠? 그럼 base를 기준으로 볼 때, master에서는 변화가 없었지만, premium에서는 A가 B로 변경된 상태입니다. 3-way merge는 base에서 변화가 발생한 것을 우선 채택합니다. 그래서 머지 결과는 'B'가 됩니다.
case2
지금 base가 1이고, master는 2, premium은 1이죠? 이 경우에도 base에서 변화가 발생한 2가 머지 결과가 됩니다.
case3
지금 base가 "hello"이고, master는 "hello"를 삭제한 공백 상태, premium은 "hello"입니다. "hello"를 삭제해서 공백 상태가 된 것이 변화가 더 발생한 것이기 때문에 머지 결과는 공백이 됩니다.
case4
지금 base가 "bye", master가 "fighting", premium이 "please" 인데요. 지금은 이전 경우들과 좀 다르네요. 둘 다 base 때와는 다른 변화가 일어났는데요. 이렇게 두 브랜치에서 다 변화가 있을 때 Git은 어떤 변화를 선택해야할까요? 정답은 바로 'Git도 모른다!' 입니다. 사실, 바로 이런 경우에 여러분이 배운 Conflict가 발생합니다. 이전에 Conflict가 발생했을 때 그것을 해결하고 머지를 마무리했던 거 기억나시죠? 바로 이런 경우였던 겁니다.
정리
•
git branch [새 브랜치 이름] : 새로운 브랜치를 생성
•
git checkout -b [새 브랜치 이름] : 새로운 브랜치를 생성하고 그 브랜치로 바로 이동
•
git branch -d [기존 브랜치 이름] : 브랜치 삭제
•
git checkout [기존 브랜치 이름] : 그 브랜치로 이동
•
git merge [기존 브랜치 이름] : 현재 브랜치에 다른 브랜치를 머지
•
git merge --abort : 머지를 하다가 conflict가 발생했을 때, 일단은 머지 작업을 취소하고 이전 상태로 돌아감
git 협업하기
git push 전에 git pull해야하는 경우가 많다.
push : 로컬 → 리모트
pull: 리모트 → 로컬
로컬과 remote에서 가리키는 최신 커밋이 다르다.
리모트 premium에 있는 최신 커밋들을 가져와서 merge하는 과정까지가 git pull이다
git pull 할 때까지 conflict가 발생할 수 있다.
A라는 개발자와 B라는 개발자가 같은 레포지토리를 들고와서 개발을 한다.
이때 B라는 개발자가 git push를 하면 바로 A가 push를 할 수가 없다.
A는 pull을 하고 push를 시킨다. ⇒ 이 과정은 A의 로컬에서 pull하고 merge를 하는거다.
git fetch도 있다.
git fetch → 가져오기만 한다.
가져오기만 하고, 머지는 하지 않는다.
리모트 레포지토리에 있는 브랜치의 내용을 일단 가져와서 살펴본 후에 머지하고 싶을 때 사용
fetch를 하면 로컬에 'FETCH_HEAD'의 이름으로 브랜치를 파게 되고, 그 이름으로 체크아웃 할 수 있다.
이 코드는 누가 작성했을까?
git blame 파일 이름
어떤 파일의 특정 코드를 누가 작성했는지 찾아내기 위한 커맨드
이미 remote repository에 올라간 커밋을 취소해야한다면?
지우고 다시 push해야함 → git revert
최근 커밋을 되돌리고 push하는 명령어
git reset을 하면 push가 안된다. remote 레포가 더 최신이기 때문에 pull해라고 뜬다
여러 커밋 취소하기
git revert id..id
정리
•
git fetch : 로컬 레포지토리에서 현재 HEAD가 가리키는 브랜치의 업스트림(upstream) 브랜치로부터 최신 커밋들을 가져옴(가져오기만 한다는 점에서, 가져와서 머지까지 하는 git pull과는 차이가 있음)
•
git blame : 특정 파일의 내용 한줄한줄이 어떤 커밋에 의해 생긴 것인지 출력
•
git revert : 특정 커밋에서 이루어진 작업을 되돌리는(취소하는) 커밋을 새로 생성