프로젝트를 하다 보면 버전 관리는 필수적인 덕목이다.
개인적으로 프로젝트를 진행하며 버전 관리에 대한 중요성을 뒤늦게 나마 깨달아 git에 대해 공부를 하기 위해 코드잇 강의를 보며 강의노트를 만들었다.
git란?
> 버전관리 프로그램이다. - 파일의 변화를 시간에 따라 정리하고, 특정 시간대의 파일을 가져오는 시스템이다.
> 여러 개발자와 협업을 할수 있게 해준다.
> 다른 컴퓨터에 작업물을 보낼수 있다. => 백업기능
-> 외부컴퓨터를 제공해주는데, 거기에다 버전관리를 할 프로젝트를 올려두는 데 이것이 GitHub이다.
장점
1) 지난 과정의 확인 가능
2) 이전 버전으로 돌아갈수 있음
repository & commit
> 프로젝트를 만들 때 필요한 프로그램들을 담을 디렉토리가 필요하다. 이를 깃으로 프로젝트 디렉토리의 버전을 관리 하여 , 변화과정을 확인 할수있다. 이런 변화과정에 대한 정보를 저장하는곳이 repository이다.
> 프로젝트 디렉토리 안에 .git라는 파일이 만들어지는데, 이것이 repository의 정체이다.
> 프로젝트를 하다보면 여러 변경사항이 생기는 데, 그중 하나의 특정 모습을 하나의 버전으로 만들고 싶다하면, commit이다.
-> 스냅샷 사진처럼 그 버전의 특정모습을 찍어서 넣는다 생각하면 편하다.
> commit 이 저장되는곳 -> repository
repository는 어떻게 만드는가?
> git init - 비어있는 repository를 생성한다.
-> .git라는 파일이 생기고 .git 안에 버전관리를 위한 파일들이 생성된다.
본격적으로 버전 디렉토리 commit하기
> git commit - 프로젝트 디렉토리안에 폴더를 생성하고, commit 해주기
-> 커밋전에, 커밋하는 사람이 누구인가 알려주기
1) git config user.name "~"
2) git config user.email "~"
-> 커밋을 할 파일을 지정해주기 (Untracked Files Present)
- git add ~(파일이름)
-> 이후 다시 커밋하면, 에러 x
> 커밋에 메세지 남기기 git commit -m "~"
-> 커밋을 할때 마다 변경사항에 대한 설명을 꼭 적어주자. 작은 습관처럼 보이지만, 미래의 나에겐 큰도움이 될것이다.
이제 Git의 작업영역들에 대해서 알아보려고 한다.
Git에는 3가지 작업 영역이 존재하는데 > (working directory, staging area, repository)
왜 이렇게 나뉘어져 있는 것일까? 이에 대한 대답은 차근차근 영역별로 나누어 공부하며 알아가 보도록 하자.
큰그림으로 보자면
working directory -> 쉽게 말해, 작업을 하고 있는 폴더
staging area -> git add를 한 파일들이 존재하는 영역. 즉, 깃이 인식하고 있는 프로젝트 디렉토리이다. 커밋을 할때 이 영역에 있는 파일들만 커밋에 반영이 된다. +) git status로 이영역을 확인 할수 있다
repository -> working directory의 변경이력들이 저장되는 영역. 즉, 커밋이 저장 되는 영역
요약하자면, working directory에서 뭔가 작업을 한다 -> git add를 해준다(staging area) -> commit을 해주면 repository로 작업내용이 들어가게 된다. ( 사실, working directory에 있는 .git파일이 repository이다. )\
+) git add는 어떻게 지울까? -> git reset ~
+) Git로 관리되는 파일은 2가지 상태를 갖는다. - tracked, untracked
untracked: 파일을 생성하고 add 하지 않으면 이상태이다.
tracked: 변동사항이 추적되고 있는 상태
1) staged: 파일의 내용이 수정되고, staging area에 올라와 있는 상태. 즉, add가 된 상태
2) modified: 최신 커밋과 변경사항이 있는 상태
3) unmodified: 최신 커밋과 다를게 없는 상태. 커밋을 하고 난 직후 working directory가 이상태이다.
github이란?
이전글에서 설명했다싶이, 따로 프로젝트 저장용 컴퓨터를 제공해주어서 업로드를 해줄수 있게 해주는 Git의 서비스라고 생각하면 쉽다.
- 작업하던 내용을 전송한다. 'repository에 전송한다'는 뜻
- 작업도 할수 있고, 버전관리도 가능하다.
How to use
- 작업하던 내용을 Github에 넣으려면 repository를 생성해야한다.
-> Github에 있는 repository를 원격 repository 혹은 remote repository라고 하고, 컴퓨터에 있는 repository는 local repository라고 한다.
*remote repository
- 안전성이 높다( local repository가 사라져도 프로젝트는 건재하다.)
- 협업이 가능하다
-> 단, git push(local -> remote 밑에 글 참조)는 아무나 가능한 것이 아닌데, setting에서 Manage Acess를 들어가 collaborator를 지정해주면 된다.
- repository를 처음 만들면,
1) local repository를 만드록, 커밋을 한후 올리는법
2) 이미 만든 repository를 업로드 하는 법이 있다.
-commit 탭을 누르면 history 확인을 할수있다.
-local repository 변경내용을 remote repository에도 반영하기
-> working directory에서 변경사항을 git add . 해준다. 그다음, git commit -m "~"
매우 중요!!
=> staging area에 올려준 다음에, local repository에 변경사항을 commit 하는 과정이다.
-> git push를 해줌으로서, local -> remote로 전송 +) git push -u origin master 로컬에서 리모트로 처음 올릴때 사용
+ README파일은 그 파일의 내용을 설명해주는데, 프로젝트에 대한 개요를 잘 설명 해주는 게 중요하다.
- remote repository 변경내을 local repository에도 반영하기
-> 예를 들어, gitgub에서 코드내용이 수정될 때, 이때에도 커밋을 해준다
-> git pull을 해줌으로서, remote -> local로 커밋
- 커밋 히스토리 확인하기
git log -> 모든 히스토리 확인가능
git show [commit id일부] -> 이전 커밋과 이후 커밋을 보여줌
git commit -> -m 없이 입력하면 수정 창이 나오는 데 i키로 수정하고, esc 누른후, ;wg입력하여 나온다.
- 최신 커밋 수정하기
워킹 디렉토리에서 수정한 내용을 저장하고, git add . 후에 git commit --amend하고 다시 remote에 git push 해주면 된다.
-commit 작성요령
-> commit 에는 3가지 정보가 들어가야 한다.
1) 커밋을 한 사용자 아이디
2) 커밋한 날짜, 시간
3) 커밋 메세지
-> 제목과 설명 사이에 한줄 띄워준다.
-> 왜 커밋을 했는지, 무슨 문제가 있었고, 해결책이 어떤 효과를 갖는 지
-> 하나의 이슈에 대한 해결내용만
-> 에러가 없는 경우에만 커밋한다
- git log 꾸미기
-> git log --pretty=oneline 이쁘게 보여준다
-> aliasing 하려면, git config alias.history 'log --pretty=oneline'
-> 특정 디렉토리에만, 적용이 되는 데 그 디렉토리로 가서 cat.git/config으로 설정 확인 가능
- 두 커밋간의 차이점 보기
-> git diff [commit id 1] [commit id 2]
- HEAD
-> git log를 보면 HEAD가 나오는데, 어떤 커밋 하나를 가리킨다.( aliasing을 하면, 예로 위처럼 history로 한다면 git history로 보기 편하게 확인 할 수 있다.
-> 만약에. HEAD가 최근이 아닌 이전 커밋을 가리킨다면, working directory의 내부 구성도 바뀐다.
- 이전 커밋으로 돌아가기
-> git reset [옵션] [commit id]
-> 옵션에는 세가지 있다
working directory | staging area | repository | |
soft | 안바뀜 | 안바뀜 | Head가 해당 커밋을 가리킴 |
mixed | 안바뀜 | 그커밋 처럼 바뀜 | Head가 해당 커밋을 가리킴 |
hard | 그커밋 처럼 바뀜 | 그커밋 처럼 바뀜 | Head가 해당 커밋을 가리킴 |
* git reset --soft후, git status로 가장 최근과 이전커밋의 차이가 있는 파일도 보여진다 -> staging area는 그대로 이기 때문
- reset이후 복구
-> git pull - github에서 이전 버전을 끌어오기 때문이다.
- 커밋아이디에 태그 달아주기
-> git tag [태그이름] [커밋아이디] => 프로젝트 이력확인 할때 도움된다.
branch란?
하나의 소스관리 흐름이다. 사실, 이친구도 어떤 포인터를 가리키는 존재인데, 여기서 Head와 브랜치의 차이란 무엇인가? 라는 의문이 든다.
- 마스터 브랜치 - repository를 만들고 커밋을 하면 생기는 기본 브랜치
- 브랜치를 만드는 법 - git branch [브랜치 이름]
- 해당 브랜치로 이동하는 법 - git checkout [브랜치 이름]
- 브랜치 이동후 working directory에서 파일을 수정하고 add, commit을 하면 내용이 바뀐다. 하지만 다른 브랜치에선 내용이 바뀌지 않는다.
- 브랜치 확인 하는 법 - git branch
- 브랜치 지우는 법 - git branch -d [브랜치 이름]: 브랜치 지우기
- 브랜치 생성후 바로 이동 - git checkout -b [브랜치 이름]
- 현재위치의 브랜치에 다른 브랜치 합병 - git merge [합치고자 하는 브랜치 이름]
- merge중 conflict가 발생했을 때 - 수정후 다시 git add -> git commit (머지의 결과가 되었으면 하는 모습으로 수정후에 커밋해야한다)
- conflict 해결안하고 이전 커밋으로 돌아가기 - git merge --abort
- 여러파일에 conflict - 하나씩 수정후 git add [파일이름] 후 커밋 (중간중간 git status로 확인)
- master브랜치와 또다른 branch를 push 하기 - master 브랜치는 처음 생성될때 remote repository에 tracking을 설정하기위한 set-upstream 옵션이 사용됐기 때문에 git push만 해줘도 되지만, 다른 브랜치를 push 하려고 할때, 처음 푸쉬를 한다면 git push --set-upstream origin [브랜치이름]하면 된다.
- 매번 커밋이 생길때마다, 마스터 브랜치는 그새로운 브랜치를 가리키게 된다.
HEAD는 보통 branch를 가리키게 되는데, 따라서, 브랜치가 움직이면 HEAD도 같이 움직이게 된다.
-> 만약에 어떤 커밋 상태에서 새로운 브랜치를 만들고 Head를 그곳으로 움직이고 다시 커밋하면, 그 HEAD와 branch는 새로운 커밋으로 이동하게 된다. 전브랜치는 움직이지 않게 된다. 그러고 다시 HEAD를 전 브랜치로 옮기고 또 commit 을 하게 되면 분기하게 된다.
-> merge란 결국 Head가 가리키던 commit에 다른 브랜치가 가리키던 커밋을 합쳐서 새로운 커밋을 만드는 작업이다
HEAD +(merge) Branch = new Commit
-> HEAD가 브랜치를 통해서가 아닌 직접 특정 커밋을 가리킬수 있는데, 과거 특정 커밋에서 새로운 브랜치를 만들고 싶을때 사용된다. (Detached Head) - git checkout로 HEAD가 직접 커밋을 가리키게 할수 있으며, 직접 브랜치를 가리키게도 할수있다.
reset | checkout |
HEAD가 가리키던 브랜치가 다른곳을 가리키게 한다 | HEAD 자체가 움직임 |
HEAD도 결국 간접적으로 다른 커밋을 가리킴 |
merge의 종류 - 3way, fast-forward
- fast-forward - 커밋이 같은 선상에 있을때, 이전 커밋에 브랜치가 있으면 현재브래치를 그 브래치까지로 빨리 감기
- 3way - 커밋히스토리 상에서 분리된 2개의 선에 각각 브랜치가 존재하여 merge할떄 -> 분기점 (1개), merge되는 브랜치(2개)가 가리키는 두 커밋 (이래서 3way) -> 분기점을 기준으로 차이점이 있는 것을 update. 둘다 차이가 있으면 conflict 발생
local repository -> remote repository로 프로젝트를 넘길때, push
remote repository -> local repository로 프로젝트를 넘길때, pull
local repository 수정중에, remote repository에 수정 사항이 생겼다면, git push를 할수 없다.(다른 사람의 업데이트가 날아 갈수 있기 때문이다.) 따라서, git pull을 먼저 해줘야 한다.
-> conflict가 발생한다면, merge 상황에서 했던 conflict 수정하듯이 하면 된다. 필요없는 부분 지우고, 파일 저장후 git add -> commit 하고, 다시 push
git pull이란, remote repository에 있는 브랜치를 local repository에 가져온다. (브랜치가 가리키고 있는 이전의 모든 커밋들을 가져오는 것- 검토없이 바로 합치고 싶을 때) 이때, git fetch를 하면 merge를 안하고 코드만 가져올수 있는데, 이경우 브랜치내용을 일단 가져온후, 살펴본후에 merge할때 유용하다.(검토)
두 커밋간의 차이를 확인할 때 - git diff
remote repository에 문제가 생겼을 때, 잘못된 부분 수정후 다시 git push함. merge 하고 다시 커밋(?)
어떤 부분의 코딩을 누가 썼는지 알고 싶을 때 - git blame [파일이름] (git show [commit id] 더 자세히 나옴)
이미 remote repository에 올라온 커밋을 취소하기 - git revert [commit id] (해당 커밋을 취소하지만 revert에 대한 커밋이 생겨서 history상에서 볼수있다. 그걸 다시 commit을 하면 HEAD가 local, remote와 같은 선상에 존재한다)
git reset vs git revert - reset -> remote 와 로컬이 있을때, remote는 그대로 local은 이전 커밋으로
revert -> remote는 그대로, local에서 한단계 높은(그 다음으로의) 커밋으로 간다.
한번에 여러 커밋 취소하기 - git revert [commit id]..[commit id + n] 커밋아이디 +1 부터 n번째 까지 취소한다.
git reset을 하고 나면 HEAD가 가리키던 브랜치를 옮긴다. 그렇다면, 브랜치가 옮겨진후 상위 커밋들은 어떻게 되나?
-> 삭제 되지는 않는다. 가장 최신의 commit으로 돌아가려면? git reset [옵션] [commit id] or git reflog(HEAD가 여태까지 가리켜온 커밋들의 정보를 알려준다. HEAD가 가리켰던 commit id를 확인할 때 유용하다.)
git rebase [merge하고 싶은 branch 이름] -> conflict 수정후 -> git add . -> git rebase --continue =>리베이스 계속 진행
rebase vs merge (결과물은 같다) - rebase는 새로운 커밋을 만들지 않는다.
rebase로 만들면 history가 훨씬 깔끔하다.
두 브랜치가 병합했다는 정보가 필요한 경우 -> merge
깔끔한 히스토리 -> rebase
작업한 내용 임시 저장하는 법
- 작업하던 중 다른 브랜치로 이동하면 작업하던 내용이 사라질수도 있기 때문에, git stash(stack에 작업 내용을 저장)로 저장할수 있다.
git stash -list로 작업내용 리스트 확인
git stash apply - 스택에 있는 내용을 다시 working directory로 가져와 적용
git stash를 사용하는 다른 경우 - 잘못된 브랜치에서 작업을 하고 있었을 때
git stash로 스택에 내용을 저장하고, 올바른 브래치로 이동해서 git stash apply를 해준다.
필요없는 작업 내용은 git stash drop [stash list id]
git stash pop - apply해줌과 동시에 drop해준다.\
필요한 커밋만 가져오는 법 - git cherry-pick - 자신이 원하는 커밋들만 가져와 현재 브랜치에 추가 하는 법