$ git init
현재 디렉토리에서 git 버전 관리함을 선언합니다. 이때 git 디렉토리가 생성됩니다.
$ git config --global user.name 'testGlobal'
$ git config --gloval user.email 'test@gmail.com'
$ git config --local user.name 'testLocal'
git 유저명 및 이메일을 설정합니다. git commit시 로그인이 안되어 있으면 에러가 발생합니다.
$ git status
각 파일이 어떤 상태를 가지는지 파악합니다. 즉, commit할 파일들이 알맞게 설정됨을 파악합니다. modified, track, untrack etc
$ git log
$ git log --graph
현 브랜치의 커밋 내역을 확인합니다. 이를 시각적으로 파악하고 싶다면 '--graph'도 이용하는데 깃 크라켄, 소스트리의 사용을 권장합니다.
$ git add test.java
$ git add .
add 명령어는 해당 파일을 추적하겠다는 의미인데, 추적된 파일들은 staging area에 올라갑니다. 여기서 staging area는 commit될 파일들이 모여있는 임시 저장소라고 생각하면 됩니다. 즉, commit을하면 staging area 위에 올라가있는 파일들이 새로운 스냅샷 및 델타를만들고, 이 staging area 위에 파일을 올리는 행위가 git add 명령어입니다.
$ git reset HEAD [file] # staging area에 올라온 특정 파일을 unstaged 상태로 바꿉니다.
$ git reset . # staging area에 올라온 파일들을 모두 modified 상태로 바꿉니다.
$ git commit #이후 vim editor에서 commit 메시지 작성
$ git commit -m "message"
$ git commit am "message" #add명령어 없이 바로 커밋 가능
$ git commit --amend # 최근 커밋한 메시지를 수정하고 싶을 때 사용
staging area에 있는 파일들을 스냅샷&델타로 변환하여 local repository에 추가하는 명령어입니다. commit을 실행하면 .git/objects 디렉토리에 git object가 생성됩니다. 또한 commit id로 커밋들을 구분하며 추후 이력을 편하게 보기 위해 '버그 수정', '기능 추가' 등 작업 별로 구분하여 커밋함을 권장합니다.
HEAD로부터 몇번 째 혹은 해당 commitId로 이동할 수 있습니다. 이때 임시 브랜치(?)로 해당 커밋이 나타나고, 해당 커밋의 소스파일을 새롭게 커밋하고 싶다면 머지 작업을 거치면됩니다. 보통 checkout 하기 전 staging area에 올라와 있는 파일들을 modified 상태로 바꿔줘야하는데 '$ git reset .' 명령어를 사용하면 됩니다.
$ git checkout HEAD~3 # 현 브랜치의 HEAD에서 세 단계 과거 커밋으로 이동합니다.
$ git checkout eb92kdn # 해당 commitId로 checkout합니다.
Example.
$ git checkout eb93kd
$ git checkout -b testBranch
~~~ 작업
$ git checkout master
$ git merge testBranch
특정 커밋으로 되돌아 갈 수 있는데, 되돌린 버전 이후의 커밋들은 히스토리에서 삭제됩니다. 즉 현재가 없었던것처럼 이력을 남기지 않습니다.
$ git reset --soft commmitId # commit 이후 파일들을 staged 상태의 working 디렉토리에 모두 보존합니다.
$ git reset --mixed commitId # commit 이후 파일들을 unstaged 상태의 working 디렉토리에 모두 보존합니다.
$ git reset --hard commitId # commit 이후 파일들을 모두 삭제합니다.
# default는 mixed 명령어입니다.
혼자서 진행하는 프로젝트라면 로컬과 리코트 커밋 히스토리가 불일치해도 git push --force를 통해 원격 저장소에 연결할 수 있습니다.
$ git push --force origin dev
특정 버전으로 되돌아갈 수 있지만 되돌린 버전 이후 이력들이 남아있습니다. 즉, 삭제할 커밋 이전 내역을 남겨두고 새로운 커밋을 생성하면서 과거로 돌아갑니다.
$ git revert commitId
reset을 사용하면 커밋 히스토리를 깔끔하게 유지할 수 있고, 혼자 작업할 때 편하게 되돌아갈 수 있다는 장점이 있습니다. 하지만 팀원과 같은 브랜치에서 함께 작업할 때 커밋이 뒤섞여 버릴 수 있다는 위험한 단점이 있어 팀 프로젝트에서는 가급적 revert 사용을 권장합니다.
revert를 사용하면 중간에 무슨 문제가 있었는지, 왜 돌아갔는지 등의 기록이 가능합니다. 또한, 다른 사람과 같은 브랜치에서 함께 작업할 때 코드 충돌을 최소화 할수 있습니다.
$ git rebase --interative
1) $ git rebase -i HEAD~3
2) # 터미널에서 HEAD ~ 3개까지의 커밋을 보여줌
3) [명령어] [커밋 해시] [커밋 메시지] 순서로 구성되고, 터미널에서 원하는 명령어 수행
4) 이후 rebase --continue 실행
다른 브랜치에 있는 커밋을 내 브랜치에 선택적으로 적용시킬 수 있습니다. rebase, reset 기능으로도 가능하지만 아래의 상황에서 cherry-pick의 장점이 극대화됩니다.
Example
아래 그림에서 마스터 브랜치에 f만 선택적으로 가져오고 싶을 때 cherry-pick없이와 cherry-pick을 이용할 경우를 비교합니다.
$ git checkout Feature
$ git rebase Master
$ git checkout Master
$ git merge Feature
$ git reset --hard f # master g commit 삭제
$ git reset --soft e # master f commit 해제 및 코드 변화 가져오기. f는 stash 상태
$ git stash # f 코드 변화 stash 저장
$ git reset --hard d # master e commit 삭제
$ git stash pop # master f 코드 변화 불러오기
$ git commit f
$ git checkout master
$ git cherry-pick f-CommitId
$ git branch # 작업중인 working 디렉토리의 브랜치들을 보여줍니다.
$ git branch branchName # 새로운 브랜치를 생성합니다.
$ git branch -D branchName # 해당 브랜치를 삭제합니다. (d)와 구별
$ git checkout branchName # 작업할 브랜치로 이동합니다. 이때 staging area와 working 디렉토리의 파일들이 변경된 브랜치를 기준으로 변경됩니다.
마스터 기둥으로부터 필요에 따라 브랜치 가지들로 나눠 작업할 수 있습니다. 메인 작업을 하는 도중 브랜치를 따서 작업을 진행하면, 마스터 브랜치에 머지하기 전까지 아무런 영향을 끼치지 않습니다. 만약 맡은 기능을 모두 완성하면 마스터 브랜치로 머지합니다. 그러면 마스터 브랜치에 변경사항이 적용됩니다.
서로 다른 두 브랜치를 통합하는 방식에는 'merge', 'rebase' 두가지가 존재합니다.
사용 예시
서로 다른 두개의 브랜치를 합치는 작업입니다. 두 갈래가 있어 복잡하지만 시간 순으의 커밋을 파악할 수 있습니다. 반면 rebase는 한 갈래로 있어 플로우가 간단하지만 시간순 커밋이 아니기 때문에 히스토리 기록용으로는 적절치 않습니다. merge에는 'fast forward merge'와 '3 way merge'가 존재합니다.
$ git merge targetBranch
$ git merge targetBranch --no--ff # fast-forward임에도 merge commit을 남길 수 있
습니다.
$ git merge --squash myBranch
주의 할 점
어떤 브랜치에 머지 할 것인지를 확실히 정하고 해당 브랜치로 checkout 해야합니다. 즉, 코드가 반영될 branch(주로 master, dev)로 checkout하고 merge를 수행해야합니다.
다른 브랜치로 checkout 할 때는 branch의 변경된 사항을 모두 add하고 commit 한 뒤에 checkout하는것이 좋습니다. 이는 working 디렉토리의 파일이 날라갈 수도 있습니다.
현재 브랜치의 헤드가 (아래 그림에서 master) 변경이 일어난 브랜치의 (아래 그림에서 function) base commit과 동일한 경우 현재 브랜치(아래 그림에서 master)의 head를 변경이 일어난 브랜치의 head로 올립니다. 즉, function 브랜치는 master 브랜치의 모든 이력을 갖고 있기 때문에 단순히 master branch의 head를 올리기만 하면 됩니다.
아래 그림처럼 새로운 브랜치가 기존 브랜치의 모든 이력을 포함하지 않는 경우(red commit 때문) fast forward merge가 안됩니다. 즉 간단하게 마스터 헤드만 단순히 움직여서 해결이 힘듭니다. 따라서 새로운 머지 커밋을 만들어(이를 merge commit이라고 부릅니다.) 문제를 해결합니다. 하지만 fast forward merge보다 그래프가 복잡하여 git rebase를 사용하는 경우도 있습니다.
squash and merge
서브 브랜치의 이력을 기록하지 않ㅇ며 머지 커밋은 한개의 부모 커밋을 가집니다.
현재 브랜치의 base commit을 새롭게 설정한다는 의미입니다. 즉, 현재 브랜치의 base commit을 baseBranch의 head로 이동합니다.
사실 3 way merge와 실행결과는 동일하지만 아래의 차이점이 존재합니다.
rebase는 커밋 히스토리를 깔끔하게 관리 할 수 있습니다.
로그 히스토리가 바뀔 수 있어 시간순의 이력을 파악하는데 어렵습니다.
잘못 사용할 경우 커밋들이 꼬일 수 있습니다.
$ git checkout user
$ git rebase dev
$ git checkout dev
$ git merge user # fast forward merge 진행
※ Automatic merge failed; fix conflicts and then commit the result.
서로 다른 브랜치에서 같은 파일을 수정할때 나오는 로그입니다. 이는 수동으로 수정하여 문제를 해결합니다.
서브 브랜치의 이력 모두를 기록하며, 머지 커밋은 두개의 부모 커밋을 가집니다. 모든 히스토리를 파악할 수 있지만 복잡한 플로우를 야기합니다.
$ git merge user
서브 브랜치의 이력을 기록하지 않으며 머지 커밋은 한 개의 부모 커밋을 가집니다. 일부 히스토리를 파악할 수 있지만 깔끔한 플로우를 가집니다.
$ git merge --squash user
서브 브랜치의 이력 모두를 기록하며, 각 커밋은 한 개의 부모 커밋을 가집니다. 하지만 각 커밋은 시간순의 히스토리를 파악하는데 어려움이 있습니다.
Reference)
https://velog.io/@kwonh/Git-Rebase%EB%9E%80
https://devye.tistory.com/27
https://www.git-scm.com/