버전 관리(version control, revision control), 소스 관리(source control), 소스 코드 관리(source code management, SCM)란 동일한 정보에 대한 여러 버전을 관리하는 것을 말한다.
VCS를 사용하면 각 파일을 이전 상태로 되돌릴 수 있고, 프로젝트를 통째로 이전 상태로 되돌릴 수 있고, 시간에 따라 수정 내용을 비교해 볼 수 있고, 누가 문제를 일으켰는지도 추적할 수 있고, 누가 언제 만들어낸 이슈인지도 알 수 있다. VCS를 사용하면 파일을 잃어버리거나 잘못 고쳤을 때도 쉽게 복구할 수 있다. 이런 모든 장점을 큰 노력 없이 이용할 수 있다.
Repository 저장소, Server, Client, Working Copy 작업 copy, Trunk / Main 주류 / 본류
Add 추가, Revision 개정판, Head 최신, Check out 인출, Check in / Commit 반납 / 예치, Check in message, Change log / History 수정 기록, Update / Sync 동기화, Revert
Branch 가지내기, Diff 차이보기, Merge 합치기/접붙이기/접목하기, Conflict 충돌, Resolve 해소, Locking 잠그기
체크 아웃(Check Out) : 저장소(Repository)에서 파일을 가져온다.
체크 인(Check In, Commit) : 체크 아웃(Check Out)한 파일의 수정이 끝난 경우 저장소(Repository)에 새로운 버전으로 갱신 하는 일이다. 이때 이전에 갱신된 것이 있는 경우 충돌(conflict)을 알려 주며 diff 도구를 이용해 수정하고 commit하는 과정을 거치게 된다.
가져오기(Import) : (버전 관리되고 있지 않은) 로컬 디렉토리의 파일을 처음으로 저장소(Repository)에 복사한다.
저장소(Repository) : 파일의 현재 버전과 변경 이력 정보를 저장하는 저장소.
DVCS(분산 버전 관리 시스템)은 대부분의 DVCS 환경에서는 리모트 저장소가 존재한다. 리모트 저장소가 많을 수도 있다. 그래서 사람들은 동시에 다양한 그룹과 다양한 방법으로 협업할 수 있다. 계층 모델 같은 중앙집중식 시스템으로는 할 수 없는 워크플로를 다양하게 사용할 수 있다.
Git 같은 DVCS에서의 클라이언트는 단순히 파일의 마지막 스냅샷을 Checkout 하지 않는다. 그냥 저장소를 히스토리와 더불어 전부 복제한다. 서버에 문제가 생기면 이 복제물로 다시 작업을 시작할 수 있다. 클라이언트 중에서 아무거나 골라도 서버를 복원할 수 있다. Clone은 모든 데이터를 가진 진정한 백업이다.누구든 서버, 저장소가 될 수 있다.
Git은 파일을 Committed, Modified, Staged 이렇게 세 가지 상태로 관리한다.
Committed란 데이터가 로컬 데이터베이스에 안전하게 저장됐다는 것을 의미한다.
Modified는 수정한 파일을 아직 로컬 데이터베이스에 커밋하지 않은 것을 말한다.
Staged란 현재 수정한 파일을 곧 커밋할 것이라고 표시한 상태를 의미한다.
이 세 가지 상태는 Git 프로젝트의 세 가지 단계와 연결돼 있다. Git 디렉토리, 워킹 트리, Staging Area 이렇게 세 가지 단계를 이해하고 넘어가자.
Git 디렉토리는 Git이 프로젝트의 메타데이터와 객체 데이터베이스를 저장하는 곳을 말한다. 이 Git 디렉토리가 Git의 핵심이다. 다른 컴퓨터에 있는 저장소를 Clone 할 때 Git 디렉토리가 만들어진다. 현재 만든 깃 디렉터리는 로컬 저장소이다.
워킹 트리는 프로젝트의 특정 버전을 Checkout 한 것이다. Git 디렉토리는 지금 작업하는 디스크에 있고 그 디렉토리 안에 압축된 데이터베이스에서 파일을 가져와서 워킹 트리를 만든다.
Staging Area는 Git 디렉토리에 있다. 단순한 파일이고 곧 커밋할 파일에 대한 정보를 저장한다. Git에서는 기술용어로는 “Index” 라고 하지만, “Staging Area” 라는 용어를 써도 상관 없다.
stage fixes를 깃에 'add' 한다라고 말한다.
Git으로 하는 일은 기본적으로 아래와 같다.
워킹 트리에서 파일을 수정한다. (생성, 삭제, 변경을 진행)
Staging Area에 파일을 Stage 해서 커밋할 스냅샷을 만든다. 모든 파일을 추가할 수도 있고 선택하여 추가할 수도 있다. (리허설 느낌)
Staging Area에 있는 파일들을 커밋해서 Git 디렉토리에 영구적인 스냅샷으로 저장한다.
Git 디렉토리에 있는 파일들은 Committed 상태이다. 파일을 수정하고 Staging Area에 추가했다면 Staged이다. 그리고 Checkout 하고 나서 수정했지만, 아직 Staging Area에 추가하지 않았으면 Modified이다.
추가와 확정(commit)
할 수 있어요.
git add <파일 이름>
git add *
# 실제로 변경 내용을 확정
git commit -m "이번 확정본에 대한 설명"
git config’라는 도구로 설정 내용을 확인하고 변경할 수 있다. Git은 이 설정에 따라 동작한다. 이때 사용하는 설정 파일은 세 가지나 된다.
/etc/gitconfig 파일: 시스템의 모든 사용자와 모든 저장소에 적용되는 설정이다. git config --system 옵션으로 이 파일을 읽고 쓸 수 있다. (이 파일은 시스템 전체 설정파일이기 때문에 수정하려면 시스템의 관리자 권한이 필요하다.)
~/.gitconfig, ~/.config/git/config 파일: 특정 사용자(즉 현재 사용자)에게만 적용되는 설정이다. git config --global 옵션으로 이 파일을 읽고 쓸 수 있다. 특정 사용자의 모든 저장소 설정에 적용된다.
.git/config : 이 파일은 Git 디렉토리에 있고 특정 저장소(혹은 현재 작업 중인 프로젝트)에만 적용된다. --local 옵션을 사용하면 이 파일을 사용하도록 지정할 수 있다. 하지만 기본적으로 이 옵션이 적용되어 있다. (당연히, 이 옵션을 적용하려면 Git 저장소인 디렉토리로 이동 한 후 적용할 수 있다.)
# 사용자 이름, 이메일 추가
$ git config --global user.name "hsshin0602"
$ git config --global user.email hsshin0602@naver.com
# 내용 확인
git config -l
# 새로운 창이 아닌 외부에서 내용을 읽을 수 있도록 설정
git config --global core.pager ''
주로 다음 두 가지 중 한 가지 방법으로 Git 저장소를 쓰기 시작한다.
# 폴더를 하나 만들고, 그 안에서 아래 명령을 실행
cd ~
mkdir git-test
cd git-test
# .git 디렉토리에는 저장소에 필요한 뼈대 파일(Skeleton)이 들어 있다.
git init
ls -a
# 상태 확인
git status
깃 디렉터리라는 것을 표시해준다. 기본적으로 마스터로 되어있다.
워킹 디렉토리의 모든 파일은 크게 Tracked(관리대상임)와 Untracked(관리대상이 아님)로 나눈다. Tracked 파일은 이미 스냅샷에 포함돼 있던 파일이다. Tracked 파일은 또 Unmodified(수정하지 않음)와 Modified(수정함) 그리고 Staged(커밋으로 저장소에 기록할) 상태 중 하나이다. 간단히 말하자면 Git이 알고 있는 파일이라는 것이다.
그리고 나머지 파일은 모두 Untracked 파일이다. Untracked 파일은 워킹 디렉토리에 있는 파일 중 스냅샷에도 Staging Area에도 포함되지 않은 파일이다. 처음 저장소를 Clone 하면 모든 파일은 Tracked이면서 Unmodified 상태이다. 파일을 Checkout 하고 나서 아무것도 수정하지 않았기 때문에 그렇다.
# 파일 추가, git 이 버전을 관리하지 않는 상태
echo "hello" > hello.txt
# 변경된 파일은 아래 명령어로 (인덱스에) 추가, stage 상태로 만들기
git add hello.txt
# 확인
git status
# stage 상태에서 내리기
git rm --cached <파일명>
# 파이썬 파일 생성, 이 또한 untracked 상태임
echo 'printf("hello world")' > hello.py
# 하나더 생성, 이 또한 깃에 의해 생성되지 않음
vi hello.md
hello world
# 안에 있는 파일 모두 stage 상태로 만듬
git add .
수정한 것을 커밋하기 위해 Staging Area에 파일을 정리했다. Unstaged 상태의 파일은 커밋되지 않는다는 것을 기억해야 한다.
커밋 메세지는 대부분 영어로 작성한다. 또한 시제를 현재시제로 작성한다. 자동으로 생성되는 커밋 메시지의 첫 라인은 비어 있고 둘째 라인부터 git status 명령의 결과가 채워진다.
git commit
Add Hello World series # 제목
Add hello.md file # 내용추가
Add hello.py file
Add hello.txt file
# 기존에 존재하는 내용
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
#
# Initial commit
#
# Changes to be committed:
# new file: hello.md
# new file: hello.py
# new file: hello.txt
#
# 저장해주고 나가면 자동으로 commit이 완료된다.
# 간단하게 커밋 메세지 추가
git commit -m '메세지 내용'
# 저장소의 히스토리를 확인, commit 히스토리를 의미
git log
init, status, add, comiit, log 명령어 사용방법 숙지 필요
gitingore 파일이 있는 경우 CLI는 컨텍스트를 수정하여 패턴과 일치하는 파일 및 디렉터리를 제외한다. 이렇게 하면 크거나 민감한 파일 및 디렉토리를 데몬에 불필요하게 보내고 잠재적으로 add를 사용하여 stage에 추가하는 것을 방지할 수 있다.
vi gitignore
*.log # .log 파일을 추적하지 않도록 함
*.txt # .txt 파일을 추적하지 않도록 함
git add .
git log
이후 위 형식을 파일을 만들어서 추가해도 파일이 추적되지 않는다.
모든 버전 관리 시스템은 브랜치를 지원한다. 개발을 하다 보면 코드를 여러 개로 복사해야 하는 일이 자주 생긴다. 코드를 통째로 복사하고 나서 원래 코드와는 상관없이 독립적으로 개발을 진행할 수 있는데, 이렇게 독립적으로 개발하는 것이 브랜치다.
Git은 브랜치를 만들어 작업하고 나중에 Merge 하는 방법을 권장한다.
# 브랜치 종류 확인
git branch
# 뒤에 이름을 작성하면 브랜치 생성
git branch feature1
# 브랜치 변경하고 확인,
git switch feature1
git status
feature1에서 기존에 있던 파일 수정하고 보기
# 내용 추가
vi hello.md
# add hello Korea
git add .
git commit -m 'Modify heelo.md'
# 한줄로 로그를 보기 쉽게 해줌
git log --oneline
git checkout testing 으로 브랜치를 이동할수 있다. but 이 명령보다는 switch를 사용하는 것이 깔끔하다.
마스터와 새로운 브랜치의 파일의 같다. 하지만 feature1에서 hello.md 파일의 내용을 수정하였으니 마스터와 내용은 다르게 저장되어 있다.
새로운 파일을 추가한 이후 판단해보자. feature1에서는 새로만든 파일이 존재하고 마스터에서는 존재하지 않는 것을 볼 수 있다.
# feature1에서 파일 하나 새로 추가
echo 'korea' korea.md
git add .
git commit -m 'Add korea.md'
ls
git switch master
ls
마스터 브랜치: 최종본
버그(hotfixes) 브랜치: 긴급 배치
릴리스 브랜치: 소프트웨어를 배포하는 것으로 완전한 상태를 유지함
개발 브랜치: 개발을 위한 브랜치
기능 브랜치: 개발중에 어떤 기능을 구현하기 위한 만든 브랜치로 기능을 만든 후에 개발 브랜치에 합병시킨다.
# 기존에 세팅했던 .git 깃을 제거한 이후 새롭게 생성
# 파일 1 커밋
echo 'hello1' > hello1.txt
git add .
git commit -m 'hello1.txt'
# 파일 2 커밋
echo 'hello2' > hello2.txt
git add .
git commit -m 'hello2.txt'
git log
# 브랜치 생성 후 확인
git branch feature1
git switch feature1
git log --oneline
현재는 같은 head를 바라보고 있음
feature1 브랜치에서 작업을 실행한 이후 commit 작업을 실행하면 head는 feature1을 바라보게 된다. 이떄부터 브랜치가 마스터와 갈라지게 되는 것이다.
# feature1에서 파일 3 커밋
echo 'hello3' > hello3.txt
git add .
git commit -m 'hello3.txt'
git log --oneline
# 기존에 존재하는 파일 수정
vi hello2.txt
hello2 -> feature1
git add .
git commit -m 'modify hello2.txt'
git merge feature1
# 브랜치 전체를 그래프로 보여줌
git log --oneline --all --graph
# git merge feature1 를 진행했을떄 충돌이 없다면 새로운 commit을 통해 저장되도록 나온다.
같은 파일의 같은 라인을 서로 다르게 수정한다면 이 둘을 병합할떄는 충돌이 발생한다. Merge 하는 두 브랜치에서 같은 파일의 한 부분을 동시에 수정하고 Merge 하면 Git은 해당 부분을 Merge 하지 못하는 것이다.
Auto-merging을 진행할 때 CONFLICT가 발생했다고 나오고 git status로 보면 unmerged 상태라고 나온다.
이를 해결하기 위해선 해당 부분을 수동으로 해결해야 한다. 둘중 하나로 선택할지, 둘다 내용을 추가할지 등을 선택하여 파일을 수정한 이후 merge를 진행해주면 된다. 이때 충돌났을때 발생하는 기호인 <<<<<<<, =======, >>>>>>>가 포함된 행을 삭제했다. 이렇게 충돌한 부분을 해결하고 git add 명령으로 다시 Git에 저장한다.
git branch -d feature2
이전 파일과의 현재 수정한 파일과 차이를 보여준다.
echo 'hello1' > hello1.txt
git add .
git commit -m 'add hello.txt'
vi hello1.txt
hello world # 추가
git diff
# 이전 커밋과 현재 상태를 비교
# 여러 파일을 수정했을 경우, 보고자 하는 파일명을 넣으면 그 파일의 내용만 확인 가능
git diff --staged hello1.txt
git diff --cached
'git add .' 를 진행한 이후 git diff를 해보면 내용이 나오지 않는다.
git branch f1
git diff master..f1
# 헤드리스
git switch --detach <커밋명> # fd... 등
git log --oneline --all
# 헤드 위치를 알려줌, 또한 현재 디렉터리의 파일 내용도 이 당시의 내용으로 바뀜
git status
# 현재 헤드에서 바로 전으로 헤드를 바꾸겠다는 의미
git switch --detach HEAD~1
# 지금 헤드와 이전 헤드를 비교
git diff HEAD~1
get restore --staged <파일 들>
커밋 자체를 삭제할 수도 있는데 reset 명령을 통해 커밋을 삭제할 수 있다. 하지만 파일의 그대로 놔두기 때문에 파일의 내용을 수정해주진 않는다. 시간만 과거로 돌리고 워킹 디렉터리는 그대로 놔둔 것이다.
수동으로 파일의 내용을 원래 상태롤 바꿔준다면 git status로 확인해봤을 떄 아무 작업을 하지 않은 것으로 나온다.
완전한 과거상태로 돌아가고 싶다면 --hard 옵션을 붙여줘야한다. 이렇게 되면 위킹 디렉터리 또한 과거 상태로 돌아간다.
git diff HEAD~1
# 커밋을 제거해준다.
git reset HEAD~1
git reset <커밋명>
# 파일의 내용 자체도 수정해준다. 즉 완전한 과거상태로 돌아간다는 것이다.
git reset --hard <커밋명>
git revert HEAD~1
원격 저장소에 이미 저장되어있는 저장소를 가져올 수 있다.
git clone <저장소 주소>
안에 ls -a 로 확인해보면 .git 파일이 존재하는 것을 확인 할 수 있다.
# origin 확인, 주소 대신 사용
git remote
# 상세 주소 확인
git remote -v
깃허브에 로그인 한 후 레포지토리 만들기를 한다.
퍼블릭으로 저장소를 만들었다고 했을 때 push는 인증이 필요하지만 pull/fetch로 가져오는 것은 인증이 따로 필요없다.
# 새로운 저정소 생성
mkdir git-test
echo "# git-test" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main # 브랜치 이름 변경
git remote add origin https://github.com/hsshin0602/git-test.git # remote 세팅
git push -u origin main # 저장소 올리기
# 기존 저장소 push
git remote add origin https://github.com/hsshin0602/git-test.git
git branch -M main
git push -u origin main
git push
git branch mf
git switch mf
echo 'ss' > new-mf.txt
git add .
git commit -m 'add new mf'
# 브랜치를 push해준다는 의미
git push origin mf
git push --force origin main
HTTPS: 토큰으로 인증을 진행하기에 불편하다.
SSH: ssh-keygen으로 키를 생성하여 키 관리를 유용하게 진행 가능하다.
# 키가 없다면 키 생성
ssh-keygen
# 퍼블릭 키파일 복사
ls -/.ssh/id_rsa.pub
New ssh 키에 등록을 해준다. 기존에 있던 저장소에 origin을 제거해준 이후 새롭게 origin를 추가해준다.
git remote add origin <저장소의 ssh 주소를 복붙>
git remote -v
이후는 인증을 계속 할 필요없이 안에 있는 프라이빗키를 사용하여 자동으로 인증이 된다.