📂 목차
📚 본문
Git 초기 설정
깃을 깔고 가장 먼저 하는 것은 깃 커밋에 들어갈 사용자 정보를 설정해주는 것이다. Git 은 커밋 기록을 남길 때 작성자 정보를 저장해야 하는데, 그 때 필요한 값이 바로 user.name, user.email 이 되겠다.
git config --global user.name "username"
git config --global user.email "useremail@email.com"
#### 확인 명령
git config user.name
git config user.emailGit OS 마다 줄바꿈 문자 설정
운여에제마다 줄바꿈 방식이 달라서 협업할 때 파일이 의도치 않게 전부 변경된 것처럼 보이는 문제가 생긴다. 이를 방지하기 위해 다음을 입력해준다.
# Windows 사용자(체크아웃 할 때 LF -> CRLF 로 자동 변환, 커밋 할 때 CRLF -> LF)
git config --global core.autocrlf true
# Mac/Linux 사용자(커밋할 때만 CRLF -> LF 로 변환)
git config --global core.autocrlf input- Windows → CRLF (\r\n)
- Mac / Linux → LF (\n)
각각을 운영체제에 맞게 넣어주자.
Git Pager 변경
대부분의 git 의 log 를 보거나 diff 를 보거나 할 때 굉장히 많은 문자열들이 출력되게 된다. 이를 방지하기 위해 git pager 방식(기본은 less)을 바꿔줄 수 있으며, cat 으로 다음 설정을 해주면 된다.
git config --global core.pager cat특정 명령에 대해서만 페이저를 비활성화 할 수도 있다.
git --no-pager logGit Editor 변경
Git 이 기본적으로 사용하는 에디터도 변경할 수 있다.
git config --global core.editor nanoGit init
Git init 명령어는 해당 폴더를 Git 의 버전 관리 범위에 등록하겠다는 의미이다. 이때 현재 디렉토리에 .git 이 생성이 되게 되며, 해당 폴더는 이제 버전관리가 되게 된다.
❯ git init
힌트: Using 'master' as the name for the initial branch. This default branch name
힌트: is subject to change. To configure the initial branch name to use in all
힌트: of your new repositories, which will suppress this warning, call:
힌트:
힌트: git config --global init.defaultBranch <name>
힌트:
힌트: Names commonly chosen instead of 'master' are 'main', 'trunk' and
힌트: 'development'. The just-created branch can be renamed via this command:
힌트:
힌트: git branch -m <name>이제 .git 파일을 들여다보자.
.git 파일
PSH ~/Desktop/.git tree
.
├── config
├── description
├── HEAD
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── fsmonitor-watchman.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── pre-merge-commit.sample
│ ├── pre-push.sample
│ ├── pre-rebase.sample
│ ├── pre-receive.sample
│ ├── prepare-commit-msg.sample
│ ├── push-to-checkout.sample
│ └── update.sample
├── info
│ └── exclude
├── objects
│ ├── info
│ └── pack
└── refs
├── heads
└── tags
9 directories, 17 filesconfig: 깃 저장소 전용 설정 파일, 보통 원격 저장소 주소, 브랜치 기본 설정, 사용자 정보 등이 있다.description: GitWeb 같은 웹 UI 에서 표시되는 저장소 설명이며, 보통 Git 로컬에서는 사용하지 않는다.HEAD: 현재 체크아웃된 브랜치를가르키는 포인터이며, Git 이 어떤 브랜치를 보고 있는지 결정해준다.hooks: Git 의 각종 이벤트 발생 시 실행되는 스크립트가 있는 곳이다.info: 해당 저장소만을 위한 Git 설정들exclude: .gitignore 과 동일 역할이지만, 로컬 저장소에만 적용되는 ignore 규칙이다.
objects: Git 의 모든 데이터가 저장되는 핵심 영역이며, 실제 파일을 저장하는게 아닌 스냅샷 형태로 압축하여 저장하게 된다.info: 내부적으로 Git 객체 정보 저장pack: 여러 개의 Git 객체들을 묶어 효율적으로 저장하는 압축파일이다. 히스토리가 많아지만 자동으로 pack 파일이 생성되고, Git 저장 공간을 크게 줄여준다.
refs: 브랜치와 태그를 어디에 저장할지를 정의하는 곳이다heads: 브랜치 이름 -> 커밋 해시를 매핑하느 파일tags: 커밋 해시 저장
위처럼 알다시피 파일 저장을 특정 해시 값으로 저장하는 것을 볼 수 있다.
.gitignore 파일
내부적으로 버전 관리를 하지 않을 파일들이나 디렉토리를 문자열 패턴으로 지정할 수 있다.
- 단순 파일 이름:
secret.txt - 특정 폴더 무시:
logs/ - 확장자 무시:
*.tmp - 하위 폴더 포함 무시:
build/ - 특정 파일은 제외:
*.log \n !important.log<-important.log는 추적 - 와일드 카드: *(0개 이상 문자), ?(정확히 1개 문자), abc, **(0개 이상의 디렉토리 포함)
필수 추가: .DS_Store, build
Staging Area
스테이징 영역은 준비 단계 이전에 변경 사항들이 들어갈 영역을 말한다. 이는 보통 인덱스라고 부르기도 한다.
이런 스테이징 영역, 어떤 포장 박스 안에 담는 작업이라고 생각하면 쉬우며, 담는 명령어는 git add 를 통해 담을 수 있다.
또한 담은 후에는 git status 를 통해 어떤 파일들이 올라가 있는지 확인할 수도 있다. 스테이징 영역과 작업 영역 사이의 차이를 알고 싶다면 git diff --cached 명령어를 통해 확인할 수 있다.
이제 패키징이 다 되었다면 git commit 을 통해 커밋 메시지와 함께 스테이징 영역을 저장소 영역으로 스냅샷 하나를 생성해준다. 커밋 이후에는 .git/objects/ 에 다수의 값들이 생성됨을 볼 수 있을 것이다.
Git 객체와 해시
깃에서는 모든 데이터는 객체 형태로 저장되는데, 파일을 그대로 저장하지 않고 압축된 스냅샷(snapshot) 과 해시로 관리하게 된다.
Git 객체
Blob: 파일 데이터 자체를 저장하며, 내용만 저장하게 된다. 파일 이름은 저장하지 않는다.Tree: 디렉토리 구조를 저장하며 파일 이름 + blob 해시, 하위 디렉토리 tree 를 해시 값을 포함한다.Commit: 특정 시점의 스냅샷을 기록하며 부모 커밋, tree 해시, 작성자, 커밋 메시지등을 포함한다.Tag: 특정 커밋에 레이블을 지정할 수 있으며, 가벼운 태그(lightweight) 와 주석 태그가 있다.
Git은 모든 객체를 SHA-1 해시 40자로 관리하며, 이 해시는 객체를 고유하게 식별하고, 내용이 같으면 해시도 같다는 특징이 있다. 이때 파일을 objects 폴더에 저장할 때, 파일의 맨 앞 두자리는 서브디렉터리의 이름으로 사용하며, 나머지 38자리는 객체 파일 이름으로 사용하게 된다.
echo "Hello Git" > hello.txt
git add hello.txt
git commit -m "Add hello.txt"
# git log --pretty=raw -> 커밋 해시 확인 가능
# Git 객체 보기
git cat-file -t <해시> # 객체 타입 확인
git cat-file -p <해시> # 객체 내용 확인
# Git 파일 해시 값 보기
sha1sum Hello.java깃 파일 의 해시값과 sha1sum 명령어로 실행된 출력 값이 맞지 않는 이유:
.git/objectsGit이 내부적으로 파일을 저장할 때 단순히 파일의 내용을 그대로 저장하는 것이 아니라, 파일의 유형(예: blob), 파일의 크기 등의 메타데이터를 포함한 문자열을 생성하고 이 문자열 전체에 대해 SHA-1 해시를 계산하기 때문
파일의 해시값을 보기 위해 현재 헤더가 가르키는 스냅샷을 따라 git ls-tree HEAD 를 사용하여 폴더가 어떻게 저장되어 있는지, 파일이 어떻게 저장되어 있는지 볼 수 있다. 또한 git log 로 커밋 이력을 보고, 커밋의 해시를 가져와서 git cat-file -p (커밋 해시) 명령어를 통해 확인 할 수도 있다. -p 옵션으로 해시값을 넣어 해당 객체에 대한 정보들을 볼 수 있다.
Git 저수준 명령어
깃은 사용자 친화적 명령어와 저수준 명령어로 나눌 수 있는데,
- Porcelain 명령어: git commit, git push, git status, 등등
- Plumbing 명령어: git 내부 데이터 구조를 직접 다루는 명령어로 스크립트나 고급 Git 사용자에게 유용하다.
git cat-file
Git 객체의 내용을 확인할 수 있으며, 객체 타입도 확인이 가능하다.
git cat-file -t HEAD # HEAD가 가리키는 커밋 객체 타입 확인
git cat-file -p HEAD # 커밋 객체 내용을 보기git hash-object
파일의 내용을 기반으로 SHA-1 해시를 계산하여 Git 객체를 생성할 수 있다.
git hash-object myfile.txt # 파일 해시 출력
git hash-object -w myfile.txt # Git DB에 Blob 객체로 저장해시 생성(hash-object)
git update-index
인덱스(스테이징 영역)에 직접 파일을 추가하거나 제거
git update-index --add myfile.txt # 파일을 Stage에 추가
git update-index --remove oldfile.txt # Stage에서 제거해시 파일 인덱스에 추가(update-index)
git rev-parse
브랜치, 태그, HEAD 등의 Git 이름을 실제 SHA-1 해시로 변환
git rev-parse HEAD # 현재 커밋 해시 출력
git rev-parse master~2 # master 브랜치에서 두 번째 이전 커밋 해시 출력reverse parsing 으로 생각하기
git reflog
브랜치나 HEAD 의 참조가 업데이트된 기록 확인 가능
git reflog # HEAD 변경 로그 전체 확인
git reflog show master # master 브랜치의 변경 로그 확인삭제된 커밋을 복구할 때 유용하다.
깃에서 참조는 브랜치나 태그처럼 특정 커밋을 가르키는 이름인데, main 이 최신 커밋 abc123 을 가르킨다고할 때, 브랜치 이름 자체는 커밋 해시를 가르키는 포인터라고 생각하면 된다. 이 포인터가 참조이며, 참조가 업데이트 된다는 것은 포인터가 다른 커밋을 가르키도록 이동했다는 의미이다.
이때 git reflog 는 이 포인터가 이동한 모든 기록을 보여주게 된다.
- 브랜치가 새 커밋으로 이동한 기록
git reset이나git checkout으로 HEAD 가 이동한 기록
따라서 삭제된 커밋도 여기 기록 덕분에 복구가 가능하게 된다.
> git reflog
abc123 HEAD@{0}: commit: Add feature X
def456 HEAD@{1}: commit: Fix bug Y
ghi789 HEAD@{2}: checkout: moving from master to featuregit ls-tree
트리 객체의 구조와 파일 목록 확인가능
git ls-tree HEAD
# 예시 출력:
# 100644 blob e69de29bb2... README.md
# 100644 blob 2c1a1f3... main.pygit pack-objects
git pack-objects 로 여러 Git 객체들을 packfile 로 압축한다.
# 특정 객체를 압축
git pack-objects mypack < list-of-objects.txt
# 팩 파일 풀기
git unpack-objects < mypack.packgit reset
이는 저수준 명령은 아니며, 자주 사용하게 될 수도 있는 명령이다.
reset 은 일단 이전 상태로 되돌리는 명령이다. 그러면 HEAD 도 이동하게 된다. 이때 되돌릴때 변경 사항을 어떻게 가져갈지를 옵션으로 지정해줄 수 있다.
--soft: HEAD 를 지정된 커밋으로 이동하지만, 스테이징 영역과 작업디렉토리는 변경되지 않는 상태로 이동한다.--mixed: 지정된 커밋으로 이동하고, 스테이징 영역은 리셋되며, 작업디렉토리는 변경되지 않는다.--hard: 모든 변경 사항과 커밋을 완전히 없애버리고 싶을때 사용하며, 사전에 데이터를 백업해놓는 것이 좋다.
Commit Message 작성
- 명령문 사용: 커밋 메시지는 무엇을 했는지가 아니라 무엇을 할 것인가를 설명
- 첫 줄은 요약, 본문은 상세 설명
- 이슈 트래커 ID 포함: 관련된 이슈 번호나 ID 를 커밋메시지에 포함시킨다
- 예:
Fixes #123
- 예:
- 변경 내용 범위를 명확히, 여러 변경이 포함된 경우는 상세 메시지로 변경 범위를 명확히 해준다.
- 일관성 유지: 프로젝트 내에서 커밋 메시지 형식을 일관되게 유지한다.
- 한글 사용: 한국인 팀의 경우 그렇게 하며, 국제적인 프로젝트라면 영어가 낫다.
Git Template
Git 커밋 메시지를 매번 형식에 맞춰서 전부 쓰기는 힘들다. 따라서 특정위치에 폼을 저장하여 이를 config 에서 설정해주면 커밋 메시지 형태를 일관되게 가져갈 수 있다.
# 예: 홈 디렉터리에 템플릿 파일 생성, 다른 경로도 무방
touch ~/.gitmessage.txt아래와 같이 vim 을 통해 입력해주자(다른 형식도 무방).
# 제목 (50자 이내, 한 줄)
# 예: [feat] 로그인 기능 구현
#
# 상세 내용 (필요시 여러 줄)
# - 변경한 기능/버그 수정 내용
# - 주의사항/설명
#
# 관련 이슈 번호
# 예: #123, #456
#
# 체크리스트 (필요시)
# [ ] 테스트 코드 작성
# [ ] 코드 리뷰 완료
# [ ] 문서 업데이트# 으로 시작하는 것은 주석으로 처리되며, 실제 커밋 메시지에는 포함되지 않는다.
feat(새로운 기능),fix(버그 수정),docs(문서 관련 변경),style(코드 스타일 변경), refactor(코드 리팩토링),test(테스트 코드 관련),chore(빌드, 환경 설정 등 기타 작업)
이제 아래와 같이 설정해주면, gitmessage.txt 의 형식에 맞게 작성하도록 권장할 수 있다.
git config --global commit.template ~/.gitmessage.txt이는 개인용 템플릿이며, 프로젝트 / 팀용 템플릿은 보통 .git 폴더에 두게 된다.
git config commit.template .gitmessage.txt위와 같이 입력하면 해당 레포지토리를 사용하는 모든 사람들이 이 템플릿을 공유하고 커밋 메시지를 일관되게 가져갈 수 있을 것이다.
특정 커밋이나 브랜치에서의 파일명 들고오기
git restore --source=<커밋 해시 또는 브랜치> <파일명>
Git Branch
깃 브랜치는 말 그대로 커밋 분기를 나누는 것이다. 여러 전략이 있지만, 다음을 소개한다
GitHub Flow
주요 브랜치는 main 만 존재하며, 기능 브랜치(feature branch)로 새로운 기능, 버그 수정 등 작업할 때 별도의 브랜치를 생성한다.
이때 PR 로 기능 브랜치를 main 에 병합하기 전에 코드 리뷰 및 테스트를 수행하게 된다. 배포는 main 으로하며, 항상 배포 가능한 상태로 유지하기 때문에 CD 를 잦게 하는 경우 유용하다.
- 브랜치 생성
git checkout -b feature/login새로운 기능이나 버그 수정을 위해 브랜치 생성
- 커밋
git add .
git commit -m "[feat] 로그인 기능 구현"중요한 점은 작은 단위로 자주 커밋하는 것이다.
- 원격 브랜치 푸시
git push origin feature/login- PR 생성
Github 에서 feature/login 브랜치를 main 에 병합하도록 PR 생성 후 코드 리뷰, CI/CD 테스트를 진행한다.
- Merge
- 리뷰, 테스트 통과
- squash merge 를 통해 깔끔하게 기록 관리
- 배포
- main 브랜치를 항상 배포 가능한 상태를 유지
- 필요시 자동 배포를 사용 가능
- 브랜치 삭제
- git branch -d feature/login
- git push origin –delete feature/login
작업 완료 후에는 브랜치 정리가 필수
main ────●────●────●────> 배포
\ \ \
feature/login feature/signup ...이제 merge 하는 두 가지 방법(merge, rebase) 를 본다.
Git Merge
Fast-forward Merge
병합에는 두 종류가 있다. Fast-forward Merge 로 브랜치가 한쪽으로만 직선으로 이어진 경우 발생, Git 이 커밋 기록을 그대로 앞으로 이동시켜 브랜치를 병합, 새로운 커밋을 만들지 않고 단순히 포인터만 이동한다.
main A---B---C
\
feature D---E예를 들어 위의 경우에는 C 이후의 커밋 기록이 없기 때문에 그대로 이동시켜 브랜치를 병합한다.
git checkout main
git merge featuremain A---B---C---D---E3-way Merge
두 브랜치가 각자 커밋이 있는 경우, Git 이 공통 조상을 기준으로 세 방향을 비교하며 자동으로 새로운 Merge Commit 이 생성된다.
main A---B---C---F---G
\
feature D---E위와 같이 분기에서 3개의 방향이 나타난다. 이때 머지되는 구조가 3-way Merge 이며, 이때 충돌이 발생할 수가 있다.
Git Rebase
브랜치의 base 를 새로 옮기는 강력한 명령이며, 간단히 브랜치의 시작점을 다른 커밋 위로 옮기는 것이라고 생각하면 된다. 주로 병합 기록을 깔끔하게 만들고 싶을때 사용하게 된다.
main: A---B---C
feature: D---E위와 같이 되어 있을때,
git checkout feature
git rebase main위를 입력하면, D-E 커밋이 C 이후의 커밋으로 다시 적용되며 새로운 D’-E’ 가 생성된다
main: A---B---C
feature: D'---E'이때도 커밋마다 충돌이 발생할 수 있기 때문에 충돌이 발생하면 개별적으로 해결해줘야 하는 단점이 있지만, 깔끔한 커밋 히스토리를 유지할 수 있기 때문에 가시적인 장점을 가져갈 수 있다. 이를 다음과 같이 쓸 수도 있다.
git checkout feature
git rebase mainfeature 브랜치를 main 의 상태로 옮겨서 머지를 준비하고
git rebase -i HEAD~5최근 5개의 커밋을 편집, 합치기, 순서 변경을 하여, 깔끔한 직선 히스토리를 유지하도록 할 수 있다. rebase 는 개인 브랜치에서만 안전하게 사용하는 것을 권장한다.
PR 제출 전 많이 사용하며, 나의 브랜치에서 오로지 개발을 진행 후에 여러 커밋들을 합치면 된다.
-
git rebase <upstream>: 현재 브랜치의 커밋들을 upstream 브랜치 혹은 커밋 위로 배치하게 된다. -
git rebase --onto <newbase> <upstream>:<upstream>과 현재 브랜치 사이의 커밋들을<newbase>위로 배치하게 된다. - ⭐️
-i또는--interactive: 이 옵션을 사용하면 rebase 과정을 인터랙티브하게 만들어서, 재배치할 커밋들을 선택 후 다음 재배치 옵션을 선택할 수 있다.- 커밋을 살림(pick)
- 커밋을 살려두고 커밋 메시지만 수정(reword)
- 이전 커밋과 합치기(squash, 메시지도 합쳐짐)
- 커밋을 살려두고 커밋 메시지와 작업 내용 자체를 수정 가능(edit)
- 여기서 분할까지도 가능(1개의 커밋을 n 개의 기차 형태의 커밋 체이닝 가능)
- 커밋 삭제(drop)
- 이전 커밋과 합치기(fixup, 메시지는 버림)
--abort: rebase 과정에서 충돌이 발생하거나 문제가 생겼을 때, rebase를 시작하기 전의 상태로 돌아감--continue: rebase 중 충돌을 해결한 후 rebase 과정을 계속 진행- 충돌이 일어났을때 해당 파일에 conflict marker 들을 보고 수정해주고
git add후git rebase --continue를 수행
- 충돌이 일어났을때 해당 파일에 conflict marker 들을 보고 수정해주고
--skip: 현재 충돌하는 커밋을 건너뛰고 rebase 과정을 계속 진행
Git cherry-pick
cherry-pick 은 깃에서 특정 커밋만을 골라내어 브랜치로 가져올 때 사용하는 명령어이며, 브랜치를 통째로 merge/rebase 하는 것이 아니라, 필요한 커밋만 선택적으로 복사해오는 기능이다.
체리처럼 골라서 따옴
단일 커밋만 가져오기
git checkout target-branch
git cherry-pick <commit-hash>여러 커밋 한 번에 가져오기
git cherry-pick <commit1> <commit2> <commit3>연속된 커밋 범위 가져오기
git cherry-pick <start-commit>^..<end-commit>(start, end] 구간임, start 안들어감
충돌이 났을 경우는 충돌 해결 후git add해주고,git cherry-pick --continue를 써준다
git cherry-pick -x <commit> # 어디서 가져온 커밋인지 남기기
git cherry-pick --edit <commit> # cherry-pick 으로 가져온 커밋 메시지를 수정하기