[Git] Git 작업 & 브랜치 관리 & 원격 저장소 사용법

신현식·2023년 3월 2일
0

CS 지식

목록 보기
17/17
post-thumbnail

버전 관리

버전 관리(version control, revision control), 소스 관리(source control), 소스 코드 관리(source code management, SCM)란 동일한 정보에 대한 여러 버전을 관리하는 것을 말한다.
VCS를 사용하면 각 파일을 이전 상태로 되돌릴 수 있고, 프로젝트를 통째로 이전 상태로 되돌릴 수 있고, 시간에 따라 수정 내용을 비교해 볼 수 있고, 누가 문제를 일으켰는지도 추적할 수 있고, 누가 언제 만들어낸 이슈인지도 알 수 있다. VCS를 사용하면 파일을 잃어버리거나 잘못 고쳤을 때도 쉽게 복구할 수 있다. 이런 모든 장점을 큰 노력 없이 이용할 수 있다.

  • 버전 관리 소프트웨어는 조직의 핵심 자산인 소스 코드의 개정과 백업 절차를 자동화하여 오류 수정 과정을 도와줄 수 있는 시스템으로 이미 다수의 국제 협력 개방 소프트웨어 개발 실무에서도 널리 사용되고 있다. 가장 유명한 도구가 git이다.

버전 관리에서 흔히 사용되는 개념 및 용어

  • 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) : 파일의 현재 버전과 변경 이력 정보를 저장하는 저장소.

Git 참고 문서

git 간편 안내서

분산 버전 관리 시스템

DVCS(분산 버전 관리 시스템)은 대부분의 DVCS 환경에서는 리모트 저장소가 존재한다. 리모트 저장소가 많을 수도 있다. 그래서 사람들은 동시에 다양한 그룹과 다양한 방법으로 협업할 수 있다. 계층 모델 같은 중앙집중식 시스템으로는 할 수 없는 워크플로를 다양하게 사용할 수 있다.

Git 같은 DVCS에서의 클라이언트는 단순히 파일의 마지막 스냅샷을 Checkout 하지 않는다. 그냥 저장소를 히스토리와 더불어 전부 복제한다. 서버에 문제가 생기면 이 복제물로 다시 작업을 시작할 수 있다. 클라이언트 중에서 아무거나 골라도 서버를 복원할 수 있다. Clone은 모든 데이터를 가진 진정한 백업이다.누구든 서버, 저장소가 될 수 있다.

Git 작업의 흐름: 세 가지 상태

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으로 하는 일은 기본적으로 아래와 같다.

  1. 워킹 트리에서 파일을 수정한다. (생성, 삭제, 변경을 진행)

  2. Staging Area에 파일을 Stage 해서 커밋할 스냅샷을 만든다. 모든 파일을 추가할 수도 있고 선택하여 추가할 수도 있다. (리허설 느낌)

  3. Staging Area에 있는 파일들을 커밋해서 Git 디렉토리에 영구적인 스냅샷으로 저장한다.

Git 디렉토리에 있는 파일들은 Committed 상태이다. 파일을 수정하고 Staging Area에 추가했다면 Staged이다. 그리고 Checkout 하고 나서 수정했지만, 아직 Staging Area에 추가하지 않았으면 Modified이다.

추가와 확정(commit)

할 수 있어요.
git add <파일 이름>
git add *

# 실제로 변경 내용을 확정
git commit -m "이번 확정본에 대한 설명"

Git 최초 설정

git config’라는 도구로 설정 내용을 확인하고 변경할 수 있다. Git은 이 설정에 따라 동작한다. 이때 사용하는 설정 파일은 세 가지나 된다.

  1. /etc/gitconfig 파일: 시스템의 모든 사용자와 모든 저장소에 적용되는 설정이다. git config --system 옵션으로 이 파일을 읽고 쓸 수 있다. (이 파일은 시스템 전체 설정파일이기 때문에 수정하려면 시스템의 관리자 권한이 필요하다.)

  2. ~/.gitconfig, ~/.config/git/config 파일: 특정 사용자(즉 현재 사용자)에게만 적용되는 설정이다. git config --global 옵션으로 이 파일을 읽고 쓸 수 있다. 특정 사용자의 모든 저장소 설정에 적용된다.

  3. .git/config : 이 파일은 Git 디렉토리에 있고 특정 저장소(혹은 현재 작업 중인 프로젝트)에만 적용된다. --local 옵션을 사용하면 이 파일을 사용하도록 지정할 수 있다. 하지만 기본적으로 이 옵션이 적용되어 있다. (당연히, 이 옵션을 적용하려면 Git 저장소인 디렉토리로 이동 한 후 적용할 수 있다.)

  • 사용자 정보
    Git을 설치하고 나서 가장 먼저 해야 하는 것은 사용자이름과 이메일 주소를 설정하는 것이다. Git은 커밋할 때마다 이 정보를 사용한다. 한 번 커밋한 후에는 정보를 변경할 수 없다.
# 사용자 이름, 이메일 추가
$ git config --global user.name "hsshin0602"
$ git config --global user.email hsshin0602@naver.com

# 내용 확인
git config -l

# 새로운 창이 아닌 외부에서 내용을 읽을 수 있도록 설정
git config --global core.pager ''

새로운 저장소 만들기

주로 다음 두 가지 중 한 가지 방법으로 Git 저장소를 쓰기 시작한다.

  • 다른 어딘가에서 Git 저장소를 Clone 하는 방법
  • 아직 버전관리를 하지 않는 로컬 디렉토리 하나를 선택해서 Git 저장소를 적용하는 방법, 즉 기존 디렉토리를 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 명령어 사용방법 숙지 필요

gitignore

gitingore 파일이 있는 경우 CLI는 컨텍스트를 수정하여 패턴과 일치하는 파일 및 디렉터리를 제외한다. 이렇게 하면 크거나 민감한 파일 및 디렉토리를 데몬에 불필요하게 보내고 잠재적으로 add를 사용하여 stage에 추가하는 것을 방지할 수 있다.


vi gitignore

*.log   # .log 파일을 추적하지 않도록 함
*.txt   # .txt 파일을 추적하지 않도록 함

git add .

git log

이후 위 형식을 파일을 만들어서 추가해도 파일이 추적되지 않는다.
  • 이미 추적중인 파일은 계속 추적이 되기 때문에 처음에 gitignore파일을 만들어 주는 것이 좋다. 추적을 없애기 위해서는 지워줘야하지만 이미 히스토리에 기록이 있기 때문에 처음에 잘 설정하는 것이 좋다.

git 브랜치

모든 버전 관리 시스템은 브랜치를 지원한다. 개발을 하다 보면 코드를 여러 개로 복사해야 하는 일이 자주 생긴다. 코드를 통째로 복사하고 나서 원래 코드와는 상관없이 독립적으로 개발을 진행할 수 있는데, 이렇게 독립적으로 개발하는 것이 브랜치다.
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

git branch 기본 사용 구조

마스터 브랜치: 최종본
버그(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'

  • 현재는 feature1와 마스터의 내용이 다르다. feature1 브랜치에서 작업한 것이 마스터 브랜치에는 반영이 되지 않기 때문이다.
  • 마스터를 기준으로 브랜치를 병합할 것이면 마스터 브랜치에 와서 작업을 진행한다.
git merge feature1

  • fast-forwarding: 마스터는 feature1에 비해 단계가 뒤쳐져 있기 떄문에 이를 잡기위해 빨리 따라간다는 의미이다. 브랜치에서 다른 B 브랜치를 Merge 할 때 B 브랜치가 A 브랜치 이후의 커밋을 가리키고 있으면 그저 A 브랜치가 B 브랜치와 동일한 커밋을 가리키도록 이동시킬 뿐이다.
  • 마스터와 feature1에서 각각 새로운 내용을 작업을 하게되면 둘은 완전히 독립된 상태가 된다. 이 경우에는 fast-forwarding이 불가능하다.
    따라서 3-way-merge 방식을 사용해서 브랜치를 다시 새로운 상태를 만든다.
# 브랜치 전체를 그래프로 보여줌
git log --oneline --all --graph

# git merge feature1 를 진행했을떄 충돌이 없다면 새로운 commit을 통해 저장되도록 나온다.

충돌 해결

같은 파일의 같은 라인을 서로 다르게 수정한다면 이 둘을 병합할떄는 충돌이 발생한다. Merge 하는 두 브랜치에서 같은 파일의 한 부분을 동시에 수정하고 Merge 하면 Git은 해당 부분을 Merge 하지 못하는 것이다.
Auto-merging을 진행할 때 CONFLICT가 발생했다고 나오고 git status로 보면 unmerged 상태라고 나온다.

  • 같은 파일의 떨어진 다른 라인을 수정했을 때는 충돌이 발생하지 않는다. but 바로 붙어있는 라인을 수정했을떄에는 충돌이 발생 할 수도 있다.

이를 해결하기 위해선 해당 부분을 수동으로 해결해야 한다. 둘중 하나로 선택할지, 둘다 내용을 추가할지 등을 선택하여 파일을 수정한 이후 merge를 진행해주면 된다. 이때 충돌났을때 발생하는 기호인 <<<<<<<, =======, >>>>>>>가 포함된 행을 삭제했다. 이렇게 충돌한 부분을 해결하고 git add 명령으로 다시 Git에 저장한다.

  • -d 옵션을 사용해 브랜치를 제거할 수 있다.
git branch -d feature2

diff

이전 파일과의 현재 수정한 파일과 차이를 보여준다.

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
  • 현재 브랜치에서 가장 최근에 만들어진 스냅샷이 head 였는데 다음 명령을 통해 head를 띄어낼 수 있다. head를 바꿀 수 있는 것이다.
# 헤드리스
git switch --detach <커밋명> # fd... 등
git log --oneline --all

# 헤드 위치를 알려줌, 또한 현재 디렉터리의 파일 내용도 이 당시의 내용으로 바뀜
git status

# 현재 헤드에서 바로 전으로 헤드를 바꾸겠다는 의미
git switch --detach HEAD~1

# 지금 헤드와 이전 헤드를 비교
git diff HEAD~1
  • add를 진행하면 안되는 것들은 add 하였을때 restore --staged 명령을 통해서 stage 했던 것을 취소할 수 있다.
get restore --staged <파일 들>
  • 커밋 자체를 삭제할 수도 있는데 reset 명령을 통해 커밋을 삭제할 수 있다. 하지만 파일의 그대로 놔두기 때문에 파일의 내용을 수정해주진 않는다. 시간만 과거로 돌리고 워킹 디렉터리는 그대로 놔둔 것이다.
    수동으로 파일의 내용을 원래 상태롤 바꿔준다면 git status로 확인해봤을 떄 아무 작업을 하지 않은 것으로 나온다.

  • 완전한 과거상태로 돌아가고 싶다면 --hard 옵션을 붙여줘야한다. 이렇게 되면 위킹 디렉터리 또한 과거 상태로 돌아간다.


git diff HEAD~1

# 커밋을 제거해준다.
git reset HEAD~1
git reset <커밋명>


# 파일의 내용 자체도 수정해준다. 즉 완전한 과거상태로 돌아간다는 것이다.
git reset --hard <커밋명>
  • 바로 직전 커밋으로 돌아가겠다는 것은 reset고 같은데 여기서는 메세지를 적어주도록 나온다. 얘는 이전에 했던 작업만 삭제하겠다는 의미이다. a -> b -> c 순서로 커밋을 진행 했을때, 밑 명령을 실행하면 a -> c 만 존재하게 된다. 즉 특정 커밋만 취소하겠다는 의미이다.
git revert HEAD~1

Git 서버

원격 저장소에 이미 저장되어있는 저장소를 가져올 수 있다.

git clone <저장소 주소>

안에 ls -a 로 확인해보면 .git 파일이 존재하는 것을 확인 할 수 있다.

  • 가지고오는 서버와 보내는 서버를 다르게 지정할 수도 있다.
# origin 확인, 주소 대신 사용
git remote 

# 상세 주소 확인
git remote -v 

내 저장소 만들기

깃허브에 로그인 한 후 레포지토리 만들기를 한다.
퍼블릭으로 저장소를 만들었다고 했을 때 push는 인증이 필요하지만 pull/fetch로 가져오는 것은 인증이 따로 필요없다.

  • 새로운 저장소를 만들 것인지, 기존에 있는 저장소를 push 할 것인지 지정할 수 있다.
# 새로운 저정소 생성
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
  • 패스워드를 위한 토큰 생성
    classic 토큰을 생성하면 되는데 repo 관련 사항 모두 선택 후 generated token를 해준다. 이후 토큰을 복사한 이후 이 토큰을 패스워드로 사용하면 된다.
  • 깃허브에서 파일을 새롭게 추가했을때 우리 로컬 저장소는 파일이 새로 생성된지 모른다. 따라서 git pull을 통해 원격의 저장소를 가져와야한다.
  • 처음에 main을 origin에 올리라고 등록을 해두었기 때문에 뒤에를 생략해도 자동으로 origin 에 등록이 된다.
git push
  • 다른 사용자가 origin를 수정했다면 다른 사용자는 이 origin을 pull해서 가져온 다음 내가 수정한 내용을 포함해서 다시 push를 해줘야한다.
    이런 방식으로 하게 된다면 협업을 진행하기가 정말 어려워진다. 따라서 별도의 브랜치를 만들어서 main가 별도의 브랜치를 만들어주고 수정한 내용을 나중에 병합하는 방식을 사용한다.
git branch mf
git switch mf
echo 'ss' > new-mf.txt
git add .
git commit -m 'add new mf'

# 브랜치를 push해준다는 의미
git push origin mf
  • 하나를 로컬에서 이전 단계롤 돌려버리게 된다면 원격 저장소로 push가 되지 않는다. 원격 저장소의 내용이 더 미래의 내용을 가지고있기 때문에 서로 맞지 않아 안되는 것이다. 하지만 원격 저장소에 잘못된 내용을 수정하기 위해 알맞는 로컬 저장소의 내용을 강제로 push하는 방법이 --force 옵선을 사용하는 것이다.
    하지만 이렇게 될 경우 다른 사용자들이 origin이 바뀌었기 때문에 오류가 발생할 가능성이 높아진다.
git push --force origin main

SSH 키 인증방식

HTTPS: 토큰으로 인증을 진행하기에 불편하다.
SSH: ssh-keygen으로 키를 생성하여 키 관리를 유용하게 진행 가능하다.

# 키가 없다면 키 생성
ssh-keygen

# 퍼블릭 키파일 복사
ls -/.ssh/id_rsa.pub

New ssh 키에 등록을 해준다. 기존에 있던 저장소에 origin을 제거해준 이후 새롭게 origin를 추가해준다.

git remote add origin <저장소의 ssh 주소를 복붙>
git remote -v

이후는 인증을 계속 할 필요없이 안에 있는 프라이빗키를 사용하여 자동으로 인증이 된다.

profile
전공 소개

0개의 댓글