중요하다고 생각되는 내용을 정리하였다.
추가로 프로젝트 하면서 필요했던 부분들도 따로 추가하여 정리하였다.
Git 기초
Branch
Git Diff
Git Stash
시간 여행
Github 기초
Git 협업
Git Rebase
Git Tag
Git Reflog
Git Alias
Git 동작 원리
Git 은 가장 대표적인 VCS 이다.
Version Control System
파일의 변화를 추적하고 관리하는 프로그램
$ git config --global user.name "Hyun"
# 이름 설정
$ git config --global user.email "ghkdgus29@naver.com"
# 이메일 설정
$ git init
.git 디렉토리가 만들어진다.
$ git status
커밋은 git repository의 체크포인트 중 하나이다.
의미있는 작업단위를 하나의 커밋으로 묶는것이 좋다.
작업 내용을 Staging Area 에 올리고, Staging Area의 내용물들을 커밋하여 Git Repository 에 저장한다.
파일을 Staging Area 에 올리기
add
명령어를 사용한다.$ git add 파일이름
- 파일이 여러개인 경우, 띄어쓰기로 여러개를 추가할 수 있다.
- 모든 작업 파일들을 올리고 싶다면 파일이름 대신
.
사용
$ git commit
vim 에디터와 같은 편집기로 커밋메시지를 작성한다.
$ git log
$ git log --oneline
git hist
alias 설정하기$ git config --global alias.hist "log --graph --all --pretty=format:'%C(yellow)[%ad]%C(reset) %C(green)[%h]%C(reset) | %C(white)%s %C(bold red){{%an}}%C(reset) %C(blue)%d%C(reset)' --date=short"
다음과 같이 이쁘게 로그를 볼 수 있다.
커밋 메시지를 잘못 썼거나 특정 파일을 빼먹고 커밋한 경우와 같이 커밋을 수정해야 하는 경우
바로 직전의 커밋에 대해서만 --amend
명령어를 통해 수정이 가능하다.
까먹은 파일이 있는 경우
$ git add 까먹은 파일이름
까먹은 파일을 Staging Area 에 올린다.
$ git commit --amend
직전 커밋 메시지도 변경할 수 있다.
.gitignore 파일
- 깃에서 관리하고 싶지 않은 파일이나 디렉토리를 명시
- 관례상 프로젝트 최상단에 위치
$ git rm --cached 파일이름
- git repository 에서 해당 파일을 제거하나, 실제 working directory (로컬 컴퓨터 저장소) 에서는 제거하지 않는다.
- 다음과 같이 추적되던 파일 (
secrets.txt
) 을 deleted 함을 확인한 후 변경사항을 커밋한다.
- 이후의 작업부터는 깃이
secrets.txt
를 추적하지 않는다.
각각의 Branch는 독립된 문맥 (context) 을 의미한다.
기본적으로 Branch 에서의 작업은 다른 Branch에게 영향을 주지 않는다.
다른 Branch의 작업을 현재 Branch에 적용시키고 싶다면 병합 (merge) 한다.
Branch 포인터는 Branch 내의 가장 최근 커밋을 가리킨다.
HEAD 는 현재 내가 읽고 있는 Branch 포인터를 가리킨다.
HEAD -> master
$ git branch
$ git branch 생성할 브랜치 이름
- HEAD 가 가리키는 현재 위치에서 branch 를 만든다.
- 이때 HEAD는 생성한 브랜치로 이동하지 않고 가만히 있는다.
$ git switch 전환할 브랜치 이름
HEAD 가 가리키는 branch가 바뀐다.
$ git switch -c 생성 및 전환할 브랜치 이름
branch 를 만들고 해당 branch로 이동한다.
삭제
HEAD 가 삭제할 Branch를 가리키면 삭제할 수 없다.
따라서 다른 Branch로 이동해야 한다.
$ git branch -D 지울 브랜치 이름
branch 병합 여부와 상관없이 해당 branch 를 제거한다.
다른 브랜치에서의 변경 사항을 나의 브랜치에 반영하는 것
Fast-Forward Merge
특별하고 간단한 경우의 merge
나의 branch 포인터를 병합하는 브랜치의 끝까지 이동시킨다.
$ git merge 병합할 브랜치 이름
만약 fast-forward merge 가 일어나는 상황에서 이를 원하지 않는다면
$ git merge --no-ff 병합할 브랜치 이름
다음과 같이 현재 branch 포인터가 병합 branch 포인터까지 이동한다.
일반적인 merge
$ git merge 병합할 브랜치 이름
NEW COMMIT = 병합 커밋
merge conflict
현재 브랜치와 병합할 브랜치의 변경사항의 충돌이 발생한 경우
충돌을 해결한 뒤 commit 해야 한다.
충돌을 제거한 후 변경사항을 staging 에 올리고 커밋한다.
$ git diff
staging 된 파일
staging 된 파일 기준 working directory 의 변경사항을 확인할 수 있다.
만약 working directory 의 변경사항을 staging area 에 올리면
git diff
로 변경사항을 확인할 수 없다.
$ git diff HEAD
staging area 에 올린 파일이더라도 직전 커밋과 내용이 다르면 변경사항을 확인할 수 있다.
- 방금 생성한
numbers.txt
의 경우 staging area 에 올리면, 변경사항을 확인할 수 있다.
- untracked 파일의 경우엔 git 에서 아예 관리하지 않으므로
git diff HEAD
로도 변경사항을 확인할 수 없다.- 따라서 방금 생성한 파일을 staging area 에 올려야 한다.
/dev/null
은 해당 파일이 방금 생성되었음을 의미한다.
$ git diff 파일이름
HEAD 옵션도 사용가능하다.
$ git diff 브랜치1..브랜치2
브랜치1이
-
, 브랜치2가+
부호가 된다.
만약 현재 작업중인 브랜치에서
커밋하기엔 애매한 미완성의 작업물들이 있는 상황에
다른 브랜치로 이동하고 싶은 경우 stash에 미완성 작업물들을 임시 저장하고 이동할 수 있다.
$ git stash
git stash save
를 축약한 명령어- 모든 작업물들이 stash에 저장된다.
$ git stash pop
가장 최근에 stash에 저장한 작업물들을 꺼내 working directory 에 적용한다.
$ git stash apply
pop
은 stash 에서 저장한 작업물을 꺼내며 working directory 에 작업물을 적용한다.apply
는 stash 에서 저장한 작업물을 꺼내지 않고 working directory 에 작업물을 적용한다.$ git stash list
- stash 에 임시저장된 작업물들을 보여준다.
- 여기서 삭제하고 싶은 작업물의 이름을 확인한다.
$ git stash drop stash@{n}
stash의 n번째에 위치한 작업물을 제거한다.
$ git stash clear
stash의 모든 작업물들을 제거한다.
특정 커밋으로 이동하기 위해 사용한다.
Branch 포인터를 가리키는 HEAD가 특정 커밋을 가리키게 된다.
detached HEAD 상태
switch
명령어를 사용해 HEAD가 다시 Branch 포인터를 가리키도록 할 수 있다.
$ git checkout 커밋해시
$ git checkout HEAD~n
HEAD~1
은 HEAD의 바로 직전 커밋을 의미한다.HEAD~n
은 HEAD 이전 n 번째 커밋을 의미한다.
$ git checkout HEAD 파일이름
git checkout -- 파일이름
으로도 같은 작업을 수행할 수 있다.
- 만약 특정 파일을 이전 커밋의 내용으로 바꾸고 싶은 경우엔 다음처럼 이동하고 싶은 커밋을 지정한다.
$ git checkout HEAD~n 파일이름
mixed reset (default)
이동하는 커밋 이후의 커밋 기록들은 모두 제거된다.
이동하는 커밋 이후의 커밋에서의 작업들은 제거되지 않고 staging 된다.
$ git reset 커밋해시
hard reset
이동하는 커밋 이후의 커밋 기록들은 모두 제거된다.
이동하는 커밋 이후의 커밋에서의 작업들도 모두 제거된다.
$ git reset --hard 커밋해시
특정 커밋의 변경사항을 취소하는것은 reset
과 유사하나 커밋기록을 제거하지 않는다.
다른 사람과의 협업을 위해 사용한다.
reset
하면 문제가 발생한다.Git Reset
- 커밋 기록이 제거된다.
Git Revert
- 커밋기록이 제거되지 않는다.
- 이전 커밋의 작업들을 무효화하는 커밋이 만들어진다.
git revert HEAD
직전 커밋을 무효화하는 Revert 커밋을 만든다.
git revert 돌아가고 싶은 커밋해시..HEAD
돌아가고 싶은 커밋의 이후 커밋들을 모두 무효화하는 Revert 커밋들을 만든다.
로컬에 있는 깃 저장소를 클라우드에 호스팅할 수 있는 플랫폼
이를 통해 다른 사람들과 쉽게 협업할 수 있다.
클라우드에 호스팅 된 원격 깃 저장소를 복제하여 내 로컬 저장소에 가져온다.
아무런 권한이 없어도 어떤 public 원격 저장소든지 clone
할 수 있다.
$ git clone 원격저장소 URL
$ git remote -v
로컬 깃 저장소와 연결된 원격 깃 저장소의 목록을 보여준다.
$ git remote add origin 원격저장소 URL
- 원격 깃 저장소와 로컬 깃 저장소를 연결한다
- 원격 깃 저장소의 이름을 origin 대신 다른 이름을 사용해도 되지만, 관용적으로 origin 을 사용한다.
$ git remote rename 과거이름 변경할이름
$ git remote remove 원격저장소 이름
$ git push origin 브랜치이름
master 브랜치를 Github 원격 저장소에
push
하는것보단 관용적으로 main 브랜치로 이름을 바꿔push
하는것이 낫다.$ git branch -M main
- 브랜치 이름 변경 명령어
$ git push -u origin 브랜치이름
- 특정 브랜치에 대한 upstream 을 설정하며
push
한다.
- 원격 깃 저장소의 브랜치와 로컬 깃 저장소의 브랜치가 연결된다.
- 이후의
push
는git push
만 입력해도 된다.
$ git push origin -d 삭제할 원격 브랜치이름
원격 브랜치를 삭제하면 로컬과 Github에서 브랜치를 볼 수 없게 된다.
$ git branch -r
origin/원격 브랜치 이름
들을 확인할 수 있다.
원격 저장소를 로컬로 가져오는 clone
을 하였더라도 main 브랜치를 제외한 다른 브랜치 정보는 로컬로 가져오지 않는다.
따라서 원격 저장소의 다른 브랜치를 가져오고 싶다면 git switch 원격 브랜치 이름
을 사용한다.
origin/
은 붙이지 않는다.
이 경우 로컬에 새로 생성된 로컬 브랜치와 원격 브랜치는 자동으로 연결된다.
원격 저장소의 변경사항들을 로컬 저장소로 가져오려면 fetch
를 사용한다.
원격 저장소의 변경사항들을 working directory 에 적용시키는 것은 아니다.
원격 저장소의 변경사항들을 로컬 저장소로 가져오면 origin/브랜치이름
의 커밋들에 checkout
할 수 있다.
git checkout origin/master
를 사용해 원격의 작업물을 볼 수 있다.- 이는 로컬에 존재하지 않는 브랜치로 이동하는 것이므로 detached HEAD 상태가 된다.
$ git fetch
git fetch origin
을 줄인 명령어- 만약 다른 이름의 원격 저장소에서 가져오고 싶다면 origin 대신 다른 이름을 사용한다.
원격 저장소의 변경사항들을 working directory 로 가져오려면 pull
을 사용한다.
fetch
+ merge
$ git pull origin 가져올 브랜치 이름
- 로컬의 현재 브랜치로 원격 저장소의 브랜치 커밋들을 가져온다.
- 만약 로컬 브랜치와 원격 브랜치가 연결된 상태라면
git pull
만 사용해도 된다.
Git 작업 요약
feature 브랜치를 main 브랜치에 merge
하기 위한 방법 중 하나이다.
PR을 보고 팀원들은 코드리뷰를 통해 merge
여부를 결정할 수 있다.
PR은 Github 에서 만들 수 있다.
PR merge 시 충돌이 발생할 수 있다.
- Github 에서 충돌 해결하기
- 충돌파일이 적은 경우 편하다.
로컬에서 충돌 해결하기
$ git switch main $ git pull $ git switch foo $ git merge main
- IDE 를 열어 충돌을 해결한다.
$ git add . $ git commit $ git push
push
권한이 없는 원격 리포지토리 (upstream) 에 PR을 날리고 싶을때 사용한다.
push
권한이 없는 리포지토리를 fork
하여 내가 작업할 수 있는 리포지토리 (origin) 를 내 계정에 만든다.
내 계정에 생긴 원격 리포지토리를 로컬로 clone
하여 작업한다.
내 계정에 생긴 원격 리포지토리 (origin) 의 작업들을 push
권한이 없는 원격 리포지토리 (upstream) 에 PR로 날릴 수 있다.
오픈 소스 프로젝트와 같이 많은 사람들이 협력할 때 필요하다.
로컬의 작업을 origin 에
push
origin 의 변경사항을 upstream 에
PR
upstream 의 변경사항을 local 로
pull
local 에 적용된 변경사항을 origin 으로
push
깃의 커밋기록들을 재작성하는 명령어
같은 작업내용과 커밋메시지를 가지는 새로운 커밋들이 새로 만들어진다.
커밋해시가 다르다.
크게 2가지 용도로 사용된다.
merge
대신 사용
커밋 기록들을 깔끔하게 정리하기 위해 사용
- 상단의 그림처럼 Feature 브랜치가 Master 브랜치의 변경사항을 적용하기 위해서
merge
를 여러번하게 되면
- Feature 브랜치와는 상관없는 merge 커밋들이 지저분하게 섞인다.
rebase
를 사용하면 하단의 그림처럼 Master 브랜치의 끝 부분을 베이스로하는 Feature 브랜치를 새로 만든다.
- Feature 브랜치와는 상관없는
merge
커밋들이 사라진다.
$ git switch feature
$ git rebase master
rebase
역시 충돌이 발생할 수 있다.
충돌이 발생한 커밋에서 rebase
가 일시정지된다.
충돌을 해결한 뒤 파일을 Staging Area 에 올리고 git rebase --continue
로 rebase
를 재개한다.
$ git rebase -i HEAD~n
- HEAD로부터 n번째 이전의 커밋을 기준으로, 그 이후의 커밋들을 모두 편집한다.
- 즉 n 번째 이전의 커밋이 base 커밋이 되고, 해당 커밋은 새로 만들어지지 않고 그대로 사용한다.
git rebase -i HEAD~9
명령어 입력시 열리는 에디터
- 9 번째 전의 커밋인
initial commit
은 포함되지 않는다.- 아래에 있을수록 최신 커밋이다.
git log
와 반대순서
pick
: 해당 커밋을 그대로 사용한다.reword
: 해당 커밋의 내용은 유지하되 커밋 메시지만 변경한다.- 편집 내용을 저장하고 닫는다.
커밋 메시지를 변경하기 위한 에디터
- 편집 내용을 저장하고 닫는다.
결과
- 커밋 메시지가 변경되었음을 볼 수 있다.
- 커밋해시가 이전과 전부 다른걸 확인할 수 있다.
특정 커밋들을 하나로 합치기
하나의 작업을 완료하고 커밋을 해버렸는데 빼먹은 작업이 있어 추가적인 커밋을 한 경우
하나의 작업단위가 지저분하게 여러개의 커밋들로 표현된다.
이 경우 추가적인 커밋들을 원래 작업 커밋 안으로 넣을 수 있다.
fixup
: 해당 커밋 기록을 제거하고 커밋의 작업 내용을 과거 커밋과 합친다.
결과
fixup
한 커밋들은 기록에서 제거된다.
특정 커밋 제거하기
실수로 올린 잘못된 커밋을 제거할 수 있다.
커밋 기록, 작업 내용 모두 제거한다.
drop
: 해당 커밋을 제거한다.
결과
drop
한 커밋이 기록에서 제거된다.
rebase
를 하는 순간 기존에 작성된 커밋들은 같은 내용의 다른 커밋해시를 가진 새로운 커밋들로 대체된다.
따라서 다른 사람들이 공유하고 있는 커밋 기록, 브랜치는 절대로 rebase
해서는 안된다.
나의 로컬에만 있는 feature 브랜치를 rebase
하는 것은 괜찮다.
다른 사람들의 로컬에도 있는 main 브랜치를 rebase
하는 것은 위험하다.
특정 커밋에 붙이는 일종의 라벨
브랜치 포인터는 커밋이 추가되면 추가된 끝부분을 가리키도록 이동한다.
태그는 커밋의 추가여부와 상관없이 특정 커밋만을 가리킨다.
주로 프로젝트 릴리즈 버전을 나타내는 용도로 사용된다.
lightweight tag와 annotated tag로 나뉜다.
소프트웨어의 버전 번호를 매기는 규칙
태그에 많이 사용된다.
의미있는 세가지의 숫자로 구성된다.
- Patch Release
- 작은 업데이트들을 의미하며 단순한 버그 수정이나 미미한 기능 추가시 숫자가 올라간다.
- Minor Release
- 새로운 기능이 추가되는 경우 숫자가 올라간다.
- Minor Release 배포 시 Patch Release 번호는 항상 0으로 초기화해야 한다.
- Major Release
- 기능이 완전히 삭제되거나 대규모 기능 개편이 있는 경우 숫자가 올라간다.
- Major Release 번호가 다른 경우 제대로 동작하지 않을 수 있다.
- 하위 호환성이 보장되지 않는다.
- Patch Release 번호나 Minor Release 번호는 다르더라도 잘 동작해야 한다.
- Major Release 배포 시 Patch Release 번호와 Minor Release 번호는 항상 0으로 초기화해야 한다.
$ git tag
git tag -l "정규식"
을 사용해 검색조건을 걸 수 있다.
$ git checkout 태그이름
detached HEAD 상태가 된다.
$ git diff 태그1 태그2
lightweight tag 생성하기
$ git tag 태그이름
현재 HEAD 가 가리키는 커밋에 태그를 만들어 붙인다.
annotated tag 생성하기
$ git tag -a 태그이름
현재 HEAD가 가리키는 커밋에 태그를 만들어 붙인다.
$ git show 태그이름
annotated tag 의 자세한 정보를 확인할 수 있다.
태그의 위치 변경하기
일반적으로 태그는 재사용할 수 없다.
따라서 이미 사용한 태그명을 다시 사용할 수 없다.
이를 이용하여 태그의 위치를 변경할 수 있다.
$ git tag -f 이동시킬 태그이름
이미 존재하는 태그를 옮기고 싶은 위치에서 강제로 다시 붙이면 이전 위치의 태그는 삭제된다.
$ git tag -d 태그이름
태그 push
하기
태그가 붙은 커밋이 원격으로 push
되어도 태그는 push
되지 않는다.
따라서 태그는 따로 push
해야 한다.
$ git push --tags
모든 태그를 원격으로
push
$ git push origin 태그이름
특정 태그만 원격으로
push
Git에서 관리하는 HEAD의 참조 로그이다.
HEAD 가 가리키는 곳이 바뀔때마다 reflog에 기록된다.
어떤 명령어를 사용해서, 어떤 브랜치나 커밋에서 어떤 브랜치나 커밋으로 이동하였는지 기록한다.
지역적인 기록으로 나의 로컬에서의 참조 기록들만 저장된다.
오랜시간 참조되지 않은 기록들은 제거된다.
$ git reflog
git reflog show HEAD
를 줄인 명령어- 만약 특정 브랜치의 reflog 만 보고싶다면
git reflog show 브랜치이름
을 사용한다.
시간 기반 한정자 사용하기
$ git reflog show HEAD@{2.days.ago}
2일전까지의 모든 reflog 확인
$ git reflog show HEAD@{yesterday}
어제까지의 모든 reflog 확인
$ git reflog show HEAD@{one.minute.ago}
1분전까지의 모든 reflog 확인
reset
이나 rebase
등으로 커밋 기록을 변경하는 작업은 실제로 커밋을 제거하지 않는다.
따라서 커밋 기록을 복구하고 싶을 때 reflog 를 뒤져 커밋해시를 찾아 복구하고 싶은 시점의 커밋에 checkout 할 수 있다. (detached HEAD 상태)
실수로 feat 브랜치를 삭제하여 feat 브랜치 관련 커밋기록들을 잃어버렸다.
reflog 를 뒤져 feat 브랜치 포인터가 가리키던 커밋을 찾아내
checkout
한다.
브랜치를 생성한 뒤 feat 브랜치로
switch
하여 feat 브랜치를 복구한다.
rebase
를 잘못하여 되돌리고 싶은 상황이라면
feat 브랜치의 reflog 를 뒤져 돌아가고 싶은 커밋해시값을 찾는다.
feat 브랜치에서
git reset --hard 돌아가고 싶은 커밋해시
명령어를 사용하여rebase
이전으로 복구할 수 있다.
각각의 깃 저장소에 적용되는 지역 설정 파일은 .git/config
이다.
로컬의 모든 깃 저장소에 적용되는 전역 설정파일은 ~/.gitconfig
이다.
git config --global A.aa
는 전역 설정 파일의A
항목에aa
필드의 값을 반환한다.git config --global A.aa 넣고싶은값
은 전역 설정 파일의A
항목에aa
필드의 값을넣고싶은값
으로 변경한다.
Git Alias 는 사용자설정 깃 단축키이다.
전역 설정파일의 alias 항목에 단축키와 수행할 깃 명령어들을 적는다.
내가 설정한 git hist
alias 설정 방법은 다음과 같다.
$ git config --global alias.hist "log --graph --all --pretty=format:'%C(yellow)[%ad]%C(reset) %C(green)[%h]%C(reset) | %C(white)%s %C(bold red){{%an}}%C(reset) %C(blue)%d%C(reset)' --date=short"
이를 통해 로그를 이쁘게 볼 수 있다.
git init
을 하면 만들어지는 디렉토리로 깃이 저장하고 조작하는 대부분의 것들이 존재한다.
config
파일
- 해당 깃 저장소에만 적용되는 지역 설정 파일
refs
디렉토리
- 브랜치 포인터나 태그와 같은 참조 (포인터) 값이 저장된다.
- 포인터 파일은 커밋 해시값을 갖는다.
- HEAD 파일
- 현재 HEAD가 가리키는 브랜치나 특정 커밋해시값 (detached HEAD 상태의 경우) 을 갖는다.
objects
디렉토리
- Git 객체 4가지를 저장한다.
commit
blob
tree
annotated tag
깃은 해시값을 key로 사용한다.
key 에 대응되는 value는 깃 객체들이다.
objects
디렉토리 내부에 해시값의 앞 두글자를 따 디렉토리를 만들고 그 안에 나머지 해시값을 이름으로 하는 깃 객체를 만든다.
git cat-file -p 해시값
을 통해 해당 객체가 커밋 객체임을 확인할 수 있다.
blob
은 파일 내용을 저장하는 객체이다.
파일 이름은 갖고있지 않다.
tree
가 관리한다.파일의 변경사항이 있는 경우에만 새로운 blob
객체가 만들어진다.
tree
는 파일들의 관계, 디렉토리 내용을 저장하는 객체이다.
blob
과 tree
를 가리키는 포인터들을 가지고 있다.
blob
해쉬값은commit
해쉬값과는 다르다.
git cat-file -p 해시값
결과
commit
은 tree
, 부모 commit
해시, 작성자와 커밋 메시지를 저장하는 객체이다.
commit
객체가 가리키는 tree
객체를 바탕으로 working directory 를 재구성한다.출처
Udemy 【한글자막】 Git & Github 실무 활용 완벽 가이드 강의 (Colt Steele 강사님)
https://www.udemy.com/course/best-git-github/?kw=git+github&src=sac