728x90
1. Git 저장소 초기화 관련 명령어
- 깃은 작성된 소스 코드 파일의 모든 변경 사항을 관리한다.
- 깃은 이러한 변경 사항을 전용 저장소(repository)(리포지터리)에 저장함
- 이 저장소는 일반적으로 사용하는 폴더와 유사하지만, 조금 차이가 있다. 따라서 깃의 동작 방식을 이해하려면 저장소 동작 원리를 확실히 알아야 한다.
1.1 폴더 vs Git 저장소
일반적인 폴더와 깃 저장소 차이점은 숨겨진 영역이 있는지 여부이다.
폴더 | * 컴퓨터의 파일과 폴더는 운영 체제의 파일 시스템에 의존하여 동작한다. * 파일 시스템은 하드디스크 같은 장치에 데이터를 저장하고 관리한다. * 그 중 폴더는 파일 여러 개를 하나로 관리할 수 있는 논리적 개념이다. 마치 파일을 그룹으로 묶어 놓은 것과 같다. |
Git 저장소 | * 깃 저장소는 외형적으로 폴더와 유사하다. 사용자 입장에서는 일반 폴더와 깃 저장소를 구별 없이 모두 동일하게 사용할 수 있다. * 사실 깃 저장소는 내부적으로 구조가 다르다. 깃 저장소에는 별도의 숨겨진 영역(숨겨진 폴더)이 있는데, 여기에 버전 관리시스템(VCS, Version Control System)에 필요한 파일 변경 이력을 기록한다. * 깃 저장소는 프로젝트의 모든 리비전(revision)(개정)과 히스토리를 가진 데이터베이스와 같다. |
1.2 Git 저장소 초기화 관련 명령어
- 저장소를 생성하려면 먼저 초기화 작업이 필요하다. 초기화란 이미 존재하는 폴더에 초기화 명령어로 VCS 관리를 위한 숨겨진 영역을 생성하는 작업이다.
- 깃 초기화는 완전히 비어 있는 폴더나 기존에 사용하던 폴더에서 모두 가능하다.
# make directory의 약어로 셸(터미널)에서 폴더를 만드는 명령
mkdir [my_project]
# change directory의 약어로 디렉터리를 이동하는 명령
cd [my_project path]
# -------------------- git init --------------------
# git 저장소로 등록(경로명을 입력하지 않으면, 현재 폴더에서 초기화됨)
git init [my_project path]
# git 저장소로 등록(경로명을 입력하지 않으면, 현재 폴더에서 초기화됨)
git init
# git 저장소로 등록(현재 폴더를 의미하는 .을 사용할 수 O)
git init .
# ---------------------------------------------------
# 파일 목록을 출력하는 리눅스 명령어
ls
# 파일 목록을 출력하는 리눅스 명령어(숨겨진 폴더까지 같이)
ls -a
ls -all
1.3 숨겨진 폴더 = .git 폴더
- git init 명령어를 사용하여 우리는 일반적인 폴더를 깃이 관리할 수 있는 저장소로 변경했다.
- 깃 저장소를 초기화한다는 것은 별도의 숨겨진 폴더를 하나 추가하고 환경 설정파일을 생성하는 것이다.
- git init 후 ls -a 명령어 입력하면 .git 폴더가 보일 것이다. 보통 폴더 이름 앞에 점(.)이 있으면 숨겨진 폴더를 의미한다.
- 숨겨진 폴더인 .git 폴더에는 깃 저장소에 필요한 모든 뼈대 파일이 담겨 있다. 뼈대 파일들은 깃 초기화를 통하여 자동 생성된다.
- 깃의 숨겨진 폴더(.git)는 매우 중요하다. 이 폴더에는 깃으로 관리되는 모든 파일 및 브랜치 등 이력을 기록하기 때문이다.
- 컴퓨터에서 깃 저장소를 통째로 복사하고자 할 때는 숨겨진 .git 폴더까지 같이 복사해야 한다.
- 만약 로컬 컴퓨터에서 .git 폴더를 삭제하거나 함께 복제하지 않으면 깃의 모든 이력은 없어진다. 즉, 일반적인 폴더 파일과 동일하게 된다.
2. 워킹 디렉토리 & Stage
깃의 동작을 이해하려면 먼저 워킹 디렉터리(working directory, working tree) 개념을 알아야한다.
2.1 워킹 디렉토리란?
- 깃은 VCS의 특성 때문에 저장 공간을 논리적으로 분리한다.
- 깃은 여러 단계의 논리적인 저장 공간을 가지고 있다. 크게 ‘작업을 하는 공간(working)’과 ‘임시로 저장하는공간(stage)’, ‘실제로 저장하여 기록하는 공간(repository)’으로 나뉜다.
- 논리적으로 공간을 분리하는 것은 깃의 동작과 이력을 좀 더 효율적으로 처리하기 위함이다.
- 워킹 디렉터리는 ‘작업을 하는 공간’을 의미한다. 말 그대로 로컬 저장소에 접근할 수 있으며, 실제로 파일을 생성하고 수정하는 공간이다. 단순하게 파일을 저장하는 공간이라고 생각하면 된다.
2.2. Untracked vs Tracked
- 지정된 파일들의 모든 것을 추적하는 관리시스템이기 때문에 깃이 다른 VCS보다 뛰어나다.
- 깃은 워킹 디렉터리에 있는 파일들을 ‘추적됨(Tracked)’과 ‘추적되지 않음(Untracked)’ 상태로 구분한다.
- 깃이라고 모든 파일을 자동으로 완벽하게 관리할 수는 없다. 수많은 파일을 모두 자동으로 처리해야 한다면 시스템에 엄청난 부하가 발생되기 때문이다. 따라서 요청받은 파일들만 이력을 관리한다면 매번 파일 목록을 추적하려고 많은리소스를 낭비할 필요가 없다.
- 사실 깃 입장에서는 어떤 파일이 정말로 추적 관리가 필요한지 알 수 없다. 따라서 깃은 요청받은 파일들만 추적 관리한다.
- 깃이 tracked와 untracked 개념을 사용하는 것은 시스템 부하를 줄이고, 좀 더효율적으로 파일 이력을 관리하기 위해서이다.
Untracked | * 워킹 디렉터리에 새 파일을 추가하면 ‘추적되지 않음(untracked)’ 상태이다. |
Tracked | * 워킹 디렉터리 안에 추적되지 않는 상태의 파일들은 별도로 명령어를 실행하여추적(tracked) 상태로 변경해 주어야 한다. * 추적 상태: 관리할 파일 목록에 등록된 상태 |
2.3 Stage & Unstage
- 스테이지(stage): ‘임시로 저장하는 공간’ = 임시 영역
- 워킹 디렉터리와 ‘실제로 저장하여 기록하는 공간’ 사이에 있는 임시영역이다.
- 스테이지 영역은 워킹 디렉터리에서 제출된 tracked 파일들을 관리한다. 즉, 워킹 디렉터리에 있는 tracked 상태의 파일들은 스테이지 영역과 긴밀한 상관관계를 맺는다.
- 스테이지 영역으로 등록된 모든 파일은 untracked 상태에서 tracked 상태로 변경된다.
- 스테이지는 워킹 디렉터리 안에 있는 파일들의 추적 상태를 관리하는 역할을 수행한다.
- 이 영역은 나중에 배울 커밋 작업과 연관이 매우 많다.
- 깃은 워킹 디렉터리에서 작업이 끝난 파일을 명령어로 통해 스테이지로 잠시 복사할 수 있다.
- 스테이지가 임시 영역이라고 해서 파일의 콘텐츠 내용을 직접 가지고 있지는 않는다. 단지 커밋하려는 파일의 추적 상태 정보들만 기록한다.
- 버전 관리에서 제외하고 싶은 파일이 있다면 .gitignore 파일에 등록하면 된다.
- 스테이지 영역에 등록된 파일들은 또 다시 stage 상태와 unstage 상태로 구분된다.
stage 상태 | * 깃이 변화 이력을 기록(commit)하려면 파일들의 최종 상태가 stage 상태여야 한다. * add 명령어를 실행하면 지정한 파일은 스테이지 영역으로 등록된다. * 스테이지 영역에 파일이 등록되면 파일은 tracked 상태로 변경된다. |
unstage 상태 | * unstage 상태라면 파일에 변화가 있다는 것을 의미한다. 스테이지 영역에 있는 파일과 워킹 디렉터리 안에 있는 파일 내용에 차이가 있을때는 unstage 상태가 된다. * 넓게 보면 아직 스테이지 영역으로 등록하지 않은 워킹 디렉터리 안의 파일도 unstage 상태라고 생각할 수 있이다. 이 때는 이때는 unstage 상태이자 동시에 untracked 상태이다. * unstage 상태라고 해서 실제 파일이 없어지는 것은 아니다. 단지 파일이 수정되어 임시적으로 스테이지 목록에서 제외된 것이다. * git add 명령어를 사용하면 스테이지에 다시 추가할 수 있다. |
2.4 파일의 modified 상태와 unmodified 상태
modified 상태 | * 스테이지에 등록된 파일은 깃이 추적 관리한다. 따라서 깃이 실제로 기록한 파일이며, 사실상 버전을 의미한다. * 이 때 파일 수정은 개발 과정에서 뗄 수 없는 작업이다. 개발 작업에서 수많은 코드가 변경되고, 깃을 사용하면 이 변경 내역은 영구적으로 기록된다. * 깃은 tracked 상태인 파일만 수정 여부를 관리할 수 있다. * tracked 상태인 파일이 수정되면 스테이지는 파일 상태를 modified 상태로 변경된다. * 수정된 파일은 스테이지에서 잠시 제외된다. * 깃은 수정 여부만 체크해 주기 때문에 modified 상태로 변경된 파일은 스테이지로 재등록해야 한다. * 수정된 파일을 스테이지 영역으로 다시 적용하려면 git add 명령어로 재등록하면 된다. * 필요한 파일만 스테이지 영역에 등록하여 이력을 추적하면 된다. * 스테이지 영역에 등록하지 않은 파일은 커밋 작업에 포함되지 않는다. * 빈 폴더는 스테이지 영역에 등록할 수 없다. 폴더 안에 파일이 하나 이상 있어야 등록이 가능하다. |
unmodified 상태 | * unmodified 상태는 tracked 상태이면서 스테이지에서 한 번도 수정하지 않은 원본 상태를 의미한다. * 수정하지 않은 파일들은 재등록하지 않아도 된다. * 스테이지에 등록한 후 어떤 수정도 하지 않았다면 unmodified 상태이다. * 깃은 파일의 수정 여부를 체크하고, 스테이지 영역의 갱신 작업 여부를 작업자에게 알려 준다. |
2.4 워킹 디렉토리 & Stage 관련 명령어
# 해당 메시지를 파일에 넣어서 파일 생성
echo "메시지" >> [파일명]
# 파일들의 stage 상태 확인
# 상태 개념은 깃의 분리된 저장 영역인 워킹 디렉터리와 스테이지, 추적 여부를 의미
git status
# 파일들의 stage 상태 확인
git ls-files --stage
# 워킹 디렉터리의 파일을 스테이지 영역으로 등록
# 깃은 안정적인 커밋을 할 수 있도록 add 명령어를 기준으로 이전과 이후 단계를 구별함
git add [파일 이름]
# 워킹 디렉터리의 파일을 스테이지 영역으로 등록
# 파일 이름 대신 점(.)을 이용하면 “전체 파일과 폴더를 모두 등록”할 수 있음
git add .
# 파일 등록 취소
# 파일을 등록한 후 커밋하지 않은 상태에서 바로 삭제
git rm --cached [파일 이름]
# 파일 등록 취소
# 한번이라도 커밋을 했다면 reset 명령어를 사용해야 함
# 커밋 후 삭제는 파일이 삭제 또는 변화된 것으로 간주하기 때문에 커밋된 파일은 리셋으로 삭제한 후 정리해 주어야 함
git reset HEAD [파일 이름]
# 깃에서 파일 이름을 변경하고 싶을 때(cf. 깃은 똑똑해서 밖에서 변경된 파일 이름을 자동으로 알고 있음)
git mv [파일 이름] [새 파일 이름]
# 깃에서 파일 이름을 변경하고 싶을 때(리눅스 명령어이므로 명령어 풀어서 3개 쳐야 한다.)
mv [파일 이름] [새 파일 이름]
git rm [파일 이름]
git add [새 파일 이름]
3. Git 저장소 복제
- 외부에 있는 기존 프로젝트(깃허브, 비트버킷)를 기반으로 저장소를 생성하고 싶다면 외부 저장소를 복제해서 생성할 수 있다.
- 외부 저장소를 이용하여 로컬 저장소를 생성하는 것을 ‘깃 저장소 복제’라고 한다.
3.1 공개 저장소
- 깃은 다수의 사람과 코드를 공유하고 협업하여 개발하는 도구이다.
- 이미 깃을 기반으로 하는 공개 저장소가 여럿 있다. 대표적으로 깃허브, 비트버킷 같은 깃 호스팅 사이트가 있다.
- 깃 호스팅 서비스는 공개된 저장소와 비공개된 저장소를 모두 지원한다. 공개된 저장소는 누구나 복제하여 코드를 내려받을 수 있음
- 요즘은 오픈 소스가 활성화되어 저장소를 공개하고 있다. 이미 수많은 오픈 소스를 깃으로 관리하고, 공개 저장소를 이용하여 배포한다.
3.2 다운로드 vs 복제
다운로드 | * 일반적으로 공개된 소스 코드를 얻으려면 웹 사이트에서 압축 파일을 내려받는다. * 소스 코드를 내려받는다는 것은 해당 코드의 최종 복사본을 내 컴퓨터로 가져오는 것이다. * 내려받기는 깃의 이력을 포함한 저장 영역까지 내려받는 것은 아니다. |
복제 | * 깃을 이용하여 저장소를 복제하면, 최종 코드뿐만 아니라 중간에 커밋같은 변화의 모든 이력도 같이 내려받을 수 있다. * 일부 코드를 변경하여 기여하는 것도 가능하다. * git clone 명령어를 사용하면 깃은 자동으로 깃 서버에 접속한다. 그 다음 저장소의 모든 소스 코드를 자동으로 내려받는다. * 복제한 후에는 복제된 폴더 이름을 그대로 사용하지 않아도 된다. * 필요에 따라 깃의 폴더 이름을 변경 해도 괜찮다. |
3.3. 복제 관련 명령어
# 깃의 저장소를 복제하는 명령어
#(복제할 때 폴더 이름을 지정하지 않으면 공개 저장소에서 사용된 폴더와 동일한 이름으로 새 폴더를 만듦)
git clone [원격저장소URL]
# 깃의 저장소를 복제하는 명령어
#(다른 이름으로 복제하길 원한다면 새 폴더 이름을 추가 인자로 적어 줌)
git clone [원격저장소URL] 새폴더이름
4.Commit
4.1 코드의 변화
- 깃은 개발 중인 코드의 이력을 만들 수 있다. 바로 커밋이다.
- 커밋(commit) : 코드 변화를 기록하는 것(의미 있는 변경 작업들을 저장소에 기록하는 동작)
- 개발자는 만일의 경우에 대비하여 중간에 코드 변경 과정을 기록하길 원한다. 변경 시점을 저장해 두면 잘못된 동작을 발견했을 때 특정 시점으로 되돌아갈 수 있기 때문이다.
- 시간에 따라 변화되는 내용만 관리하고 코드가 변화된 시간 순서에 따라서 영구적으로 저장한다.(+ 커밋은 파일의 시간적 변화도 함께 저장한다.)
- 개발자 입장에서는 복잡한 구조의 파일을 관리하지 않아도 되고, 여러 개의 파일보다는 파일 하나로 모든 이력을 처리하기 때문에 유용하다.
- 커밋은 부모 커밋(parent commit)을 기반으로 변화된 부분만 새로운 커밋으로 생성한다.
4.2 고식적 (conventional) 파일 관리 방법 vs Git (VCS)을 이용한 파일 관리 방법
고식적 (conventional) 파일 관리 방법 | * 보통 우리는 의미 있는 변경을 할 때 파일을 복사한다. * 복사한 새 파일에는 추가하거나 변경하고 싶은 내용을 적용한다. * 파일을 복사하는 형태는 파일의 변경 내역을 기록하는 것보다 더 많은 파일을 생성하고 관리해야 하는 부작용이 있다. *모든 내용이 중복되기 때문에 용량도 많이 차지한다. |
Git (VCS)을 이용한 파일 관리 방법 | * 깃의 commit은 새로 변경된 부분만 추출하여 저장한다. * 파일 이름을 변경하지 않고도 동일한 파일 이름으로 하나로 관리가 가능하다. * commit: 시간에 따라 “변화되는 내용만 관리”하고, 코드가 변화된 “시간 순서에 따라서 영구적으로 저장”할 수 있다. |
4.3 HEAD
- HEAD는 커밋을 가리키는 묵시적 참조 포인터이다.
- HEAD는 최종적인 커밋 작업의 위치를 가리킨다.( 작업 중인 브랜치의 마지막 커밋 ID를 가리키는 참조 포인터 )
- 쉽게 말하자면 마지막 커밋 위치를 가리킨다. 즉, HEAD는 커밋이 변화한 최종 시점을 의미한다.
- 깃은 마지막 커밋을 가리키는 HEAD 포인터를 부모 커밋으로 대체하여 사용한다.
- HEAD 포인터를 사용하여 빠르게 스냅샷을 생성할 수 있다.
- 단 깃을 설치하고 처음 커밋할 때는 HEAD의 포인터가 없다. 최소한 한 번 이상 커밋을 해야만 HEAD가 존재한다.
- HEAD는 커밋될 때마다 한 단계씩 이동한다.
4.4 스냅샷
- 깃이 다른 버전 관리 도구와 다른 점은 스냅샷(snapshot) 방식을 이용한다는 것이다.
- 기존에 파일을 복사하는 방식으로는 수정본을 관리하면 같은 내용을 반복해서 저장하기에 많은 용량을 차지한다. 또한 수정된 부분들을 일일이 찾아야 하기 때문에 검색할 때도 매우 불편함
- 깃은 이러한 시스템적인 단점을 해결하려고 변경된 파일 전체를 저장하지 않고, 파일에서 변경된 부분을 찾아 수정된 내용만 저장한다.
- 스냅샷 방식은 마치 변화된 부분만 찾아 사진을 찍는 것과 같다.
- 깃의 스냅샷은 HEAD가 가리키는 커밋을 기반으로 사진을 찍는다.
- 스테이지 영역과 비교하여 새로운 커밋으로 기록한다.
- 깃은 스냅샷 방식을 이용하여 빠르게 버전의 차이점을 처리하고, 용량을 적게 사용한다.
4.5 파일 상태와 커밋
- 커밋은 변화된 내용을 영구적으로 깃 저장소에 기록한다.
- 새롭게 생성된 파일을 커밋하려면 반드시 tracked 상태로 변경해 주어야 한다.
- 커밋을 하려면 스테이지 영역에 새로운 변경 내용이 있어야 한다.
- 수정된 내용이 스테이지 영역으로 등록되지 않으면 커밋을 할 수 없다.
- 커밋하기 전에는 status 명령어로 항상 상태를 확인하는 습관이 필요하다.
- 워킹 디렉터리가 깨끗하게 정리되어 있지 않으면 커밋 명령어를 수행할 수 없다.
- 커밋은 수정된 내용을 한 번만 등록한다.
- 스테이지 영역의 파일이 변경되지 않았다면 커밋을 두 번 실행할 수 없다. 깃은 스테이지 영역의 변경된 내용을 기준으로 스냅샷을 만들어 커밋하기 때문이다.
- 깃의 커밋은 HEAD와 스테이지 영역 간 차이를 비교하여 새로운 객체를 생성한다. 생성된 객체를 깃 저장소에 기록한다.
- 커밋을 할 때 모든 커밋은 반드시 생성된 객체를 기록하는 것과 동시에 이를 구별할 수 있는 메시지를 같이 작성해야 한다.
- 변화된 각 커밋 객체에 꼬리표처럼 설명을 달아 놓는다고 생각하면 된다.
- 커밋은 파일 이름을 여러 개 사용하지 않고 하나만 가진다. 그래서 기존처럼 파일 이름으로 변화된 객체를 구별할 수 없다. 그 대신 깃은 변화된 객체를 구별하고자 메시지 시스템을 도입했으므로, 파일 이름을 사용하지 않고, 별도로 작성한 메시지 문자열로 각 변경 객체들을 쉽게 구분할 수 있는 것이다.
4.6 커밋 관련 명령어
# 커밋하기(메세지 따로 입력하지 않으면 vi 에디터 실행)
git commit
# 파일 등록과 커밋을 동시에 하기
# -a 옵션은 커밋을 하기 전에 자동으로 모든 파일을 등록하는 과정을 미리 수행함
git commit -a
# 커밋하기(메세지 한줄 입력)
git commit -m "커밋메시지 내용"
# 커밋하기(파일 등록 + 한줄 메시지)
git commit -am "커밋메시지 내용"
# 상태 확인
# 커밋을 하면 스테이지 영역은 초기화됨 => working tree clean 메시지(더 이상 추가된 새로운 파일과 수정된 파일이 없다는 의미)
git status
# 시간 순으로 커밋 기록을 출력하는데, 최신 커밋 기록부터 내림차순으로 나열함
git log
5. 서버
5.1 서버 저장소(원격[remote] 저장소)
- 서버 저장소는 로컬 저장소의 코드를 복제한 복사본이라고 할 수 있다.
- 서버를 이용하면 코드를 안전하게 보관할 수 있다.
- 서버에 있는 소스 코드는 다른 사람들과 공유하고 협업하여 개발을 진행할 수 있다.
- 깃은 복수의 원격 저장소를 연결하여 사용할 수 있다.
- 로컬 저장소에 원격 저장소(서버)를 등록하려면 서버 주소가 필요하다.
- 깃허브 같은 저장소를 이용해 보면 프로토콜 + 도메인 주소형태로 된 것을 알수 있다.
- 로컬에 서버 저장소를 생성할 때는 폴더 경로를 사용할 수 있다.
- 저장소를 백업하는 용도로 원격 저장소를 사용할 수 있다. 꼭 다른 사람들과 협업하려고 깃의 원격 저장소를 사용하는 것은 아니다.
- 주소와 별칭
- 별칭:
- 원격 서버의 주소는 긴 문자열로 되어 있다.
- 따라매번 원격 서버에서 작업할 때마다 긴 문자열을 입력하는 것은 피곤하다.
- 간략하게 긴 서버 URL 문자열을 별칭으로 만들어 서버 URL을 대신하여 사용할 수 있다.
- 별칭은 중복하여 선택할 수 없다.
- origin:
- origin은 대표적으로 사용하는 별칭이다.
- 기본적으로 원격 서버와 연결할 때 별칭으로 origin을 사용하는 것을 많이 볼 수있다.
- 자신의 목적에 따라 다른 이름을 사용해도 괜찮다.
- 별칭:
- 그 외 간단한 특징
연속된 작업 |
|
새 멤버 |
|
5.2. 협업 저장소
- 깃은 여러 개발자와 협업하려고 탄생한 도구이다.
- 과거와 달리 요즘 컴퓨터는 항상 인터넷에 접속되어 있다. 아직도 365일 24시간 인터넷에 연결하여 작업할 수 없는 개발 환경도 많이 있다.
- 깃은 이 두 가지 환경을 고려하여 분산형 모델을 선택한다.
5.3. Github
- 독립적인 깃 서버를 직접 운영하여 사용할 수 있다. 그러나 365일 안정적인 서버를 운영하는 것은 쉽지 않다.
- 직접 서버를 운영하지 않아도 전문적인 깃 호스팅으로 서버를 대체할 수 있다. 호스팅을 받으면 직접 서버를 관리하지 않아도 쉽게 원격 저장소를 운영할 수 있다.
- 깃허브는 대표적인 깃호스팅 서비스이다. 깃허브의 모든 서비스는 무료로 사용할 수 있다. ( https://github.com )
5.4. 프로토콜
- 서버와 통신하려면 프로토콜을 사용해야 한다.
- 깃은 서버와 통신할 수 있는 다양한 프로토콜을 지원한다.
- 깃은 기본적으로 Local, HTTP, SSH, Git 네 종류의 전송 방식을 지원한다.
Local | * 로컬 컴퓨터에 원격 저장소를 생성하는 것을 의미한다. * 이 방식은 자신의 컴퓨터를 NFS(Network File System) 등 서버로 이용할 때 편리하다. * 로컬 저장소를 서버로 이용할 때는 폴더 경로만 입력하면 된다. git remote add [원격저장소별칭] [폴더경로] * 로컬은 간단하게 원격 서버를 구축할 수 있을 뿐만 아니라 빠른 동작이 가능하다. * 모든 자료가 자신의 컴퓨터에 집중되는 위험도 있다. |
HTTP | * HTTP는 SSH처럼 많이 사용하는 프로토콜 중 하나이다. * 깃허브, 비트버킷 같은 호스팅 서비스도 기본 HTTP 프로토콜을 지원한다. * 서버에 접속하려면 로그인 절차를 거쳐야 함 * HTTP는 기존 아이디와 비밀번호만으로 접속자를 인증하여 처리할 수 있다. * HTTP는 익명으로도 처리할 수 있으며, 계정을 이용하여 처리할 수도 있다. |
SSH | * SSH는 깃에서 권장하는 프로토콜로, 높은 수준의 보안 통신으로 처리하기 때문에 깃 서버를 좀 더 안전하게 운영할 수 있다. * SSH 프로토콜을 사용하려면 주소 앞에 ‘ssh://계정@주소’처럼 프로토콜 타입을 지정해야 한다. * 계정을 생략하여 현재 로그인된 사용자로 대체할 수도 있다. * SSH 접속을 할 때는 인증서를 만들어 사용한다. * 인증서를 만들어 접속하면 별도의 회원 로그인 절차를 거치지 않아도 된다. * 인증서는 공개키와 개인키로 구분하는데 공개키는 서버에 등록하며, 개인키는 로컬에 저장한다. * SSH는 HTTP와 달리 익명으로 접속할 수 없다. 이러한 점이 기업에서 깃 서버를 운영할 때 적합한 프로토콜이라고 할 수 있다. |
Git | * 깃의 데몬 서비스를 위한 전용 프로토콜 방식을 의미한다. * SSH와 유사하지만 인증 시스템이 없어 보안에 취약할 수 있다. * 실제로 이 프로토콜은 잘 사용하지 않는 편이다. |
5.5. 서버 전송(Push)
- 푸시(push): 원격 저장소로 커밋된 파일들을 업로드하는 동작
- 원격 저장소로 로컬 깃 저장소의 내용을 전송할 때는push 명령어를 사용한다.
- 별칭 이름을 가지는 서버의 master 브랜치에 현재 브랜치를 업로드한다.
- push 명령어를 사용할 때는 충돌을 최소화하기 위해 미리 원격 저장소를 확인한다.
5.6. 내려받기
자동 병합 / 수동 병합
clone(복제) | * 복제는 기존 저장소를 이용하여 새로운 저장소를 생성하는 방법 중 하나이다. * 일반적인 복제와 원격 저장소를 복제하는 방법은 조금 차이가 있다. * 복제는 원격 저장소에서 모든 내용을 한 번에 내려받는다. * 복제할 때는 clone 명령어를 사용한다. * clone 명령어는 초기화 init 명령어 외에 원격 서버 접속에 필요한 추가 설정을 자동으로 수행한다. * 서버의 연결 설정을 마친 후 서버 안에 있는 모든 커밋된 코드 이력들을 한 번에 내려받는다. * 로컬 저장소를 생성한 후, 처음으로 서버에서 코드를 내려받을 때는 clone 명령어를 사용하면 좀 더 편리하다. |
pull | * 복제 후 원격 저장소의 갱신된 내용을 추가로 내려받으려면 pull 명령어를 사용해야 한다. * pull 명령어는 로컬 저장소보다 최신인 갱신된 원격 저장소의 커밋 정보를 현재 로컬 저장소로 내려받는다. * pull 명령어를 주기적으로 사용하면 최신 커밋 정보로 로컬 저장소를 유지할 수 있다. * pull은 원격 서버에서 현재 커밋보다 더 최신 커밋 정보가 있을 때 내려받는다. * 내려받은 커밋 정보는 임시 영역에 저장한다.( 스테이지 영역이 아닌 원격 저장소를 위한 전용 임시 브랜치가 따로 있음 ) * 내려받은 최신 커밋들을 현재 브랜치로 자동으로 병합( 원격 서버 파일과 로컬 파일을 하나로 합치는 과정 )처리한다. * 혼자서 개발하는 프로젝트는 pull 명령어만으로도 편리하게 사용할 수 있다. * 새로운 커밋 갱신이 없는지 pull 명령어를 사용하여 지속적으로 확인하는 것이 좋다. |
fetch | * 여러 개발자와 협업으로 코드를 작성할 때 pull 명령어를 사용한 자동 병합은 가끔씩 문제가 생긴다. * 여러 개발자와 협업하는 과정에서 pull 명령어가 자동으로 브랜치 병합을 하지 못하고 충돌이 발생하기도 한다. * pull 명령어로 자동 병합을 하지 못할 때는 페치 방식을 사용해야 한다. * fetch(페치)는 원격 저장소에서 코드를 수동으로 내려받는 작업을 한다. * 페치는 원격 저장소에서 커밋된 코드를 임시 브랜치로 내려받는다. 내려받은 후 현재 브랜치와 자동 병합하지 않는다. * 따라서 pull 명령어와 달리 fetch 명령어를 실행한 후에는 커밋이 추가된 것을 확인할 수 없다. 페치는 원격 저장소의 커밋들만 가지고 왔을 뿐 로컬 저장소에서 어떤 작업도 하지 않는다. * 내려받은 커밋을 로컬 저장소에 적용하려면 병합 명령을 실행해야 하는데, merge 명령어를 사용함 |
5.7. 순서
- 원격 저장소에는 다수의 개발자가 동시에 커밋을 푸시할 수 없다. 여러 명이 협력해서 개발할때는 순차적으로 푸시해야 한다.
- 최신 상태
- 먼저 원격 저장소에 푸시하려면 자신의 로컬 저장소를 최신 상태로 유지해야 한다. 자신의 저장소가 원격 저장소의 커밋보다 최신이어야 한다는 의미이다.
- 커밋이 순차적이지 않을 때 깃은 푸시 동작을 거부한다. 푸시 동작이 거부되면 풀 또는 페치 작업으로 자신의 로컬 저장소를 갱신해 주어야 한다. 갱신 후에 다시 푸시해주면 된다.
- 충돌 방지
- 깃이 최신 상태에서만 푸시를 허용하는 것은 충돌을 방지하기 위해서이다.
- 원격 저장소의 커밋을 내려받는 풀 작업은 내려받은 커밋들을 현재 브랜치로 자동 병합한다. 이 때 커밋 내용이 순차적이지 않으면 병합 과정에서 충돌이 발생한다.
- 예를 들어 개발자 1과 개발자 2가 서로 다른 작업을 했다면 충돌이 없을 수도 있지만, 같은 영역을 동시에 수정했다면 개발자 2가 풀 작업을 할 때 무조건 충돌이 발생한다. 이 때 개발자 2는 충돌을 해결한 후 커밋하고 푸시해야 하는데, 이를 해결하기가 어렵다.
- 깃은 이를 더 쉽게 해결할 수 있도록 푸시할 때 커밋의 순차적 기록을 확인한다.
- 최대한 충돌을 피할 수 있는 방법은 자신의 저장소와 원격 저장소의 상태를 자주 최신으로 유지하는 것이다.
5.8. 원격 저장소 관련 명령어
# 연결된 원격 저장소의 이름(별칭)을 출력(간략하게)
# cf) 리모트 저장소가 여러 개 있을 때는 목록을 모두 출력한다. 저장소의 권한 정보까지는 알 수 없다.
git remote
# 원격 저장소의 좀 더 상세한 정보를 출력
git remote show [원격저장소별칭]
# -v 옵션을 같이 사용하면 원격 저장소의 별칭 이름과 URL을 확인할 수 있음
# 원격 저장소가 연결되면 fetch와 push 두 주소를 출력해준다.
# push(푸시)는 서버로 전송하는 동작을 의미하고, fetch(페치)는 반대로 서버에서가지고 오는 동작을 의미함
git remote -v
# 로컬 컴퓨터에 원격 저장소를 생성(Local 프로토콜)
git remote add [원격저장소별칭] [폴더경로]
# 원격 저장소에 연결
git remote add [원격저장소별칭] [원격저장소URL]
# 등록된 서버의 별칭 이름을 변경
git remote rename [변경전별칭] [변경후별칭]
# 등록된 원격 저장소 삭제
git remote rm [원격저장소별칭]
# 브랜치를 원격저장소로 전송
git push [원격저장소별칭] [브랜치명]
# 원격 저장소를 로컬 저장소에 복제
git clone [원격저장소URL]
# 원격 저장소의 갱신된 내용을 추가로 내려받기(현재 브랜치와 자동 병합)
git pull
# 원격 저장소의 갱신된 내용을 추가로 내려받기(현재 브랜치와 자동 병합 x)
git fetch [원격저장소URL]
# fetch로 내려받은 커밋을 로컬 저장소에 적용하기 위해 병합 명령 적용
git merge [원격저장소별칭]/[브랜치이름]
6. 브랜치
6.1. 브랜치(branch)
- 브랜치(branch): 나뭇가지, 지사, 분점 등 줄기 하나에서 뻗어 나온 갈림길을 의미한다.
- 큰 나무 줄기에서 작은 줄기가 뻗어 나오는 것처럼 저장 공간 하나에서 가상의 또 다른 저장 공간을 만드는 것이라고 생각하면 된다.
- 브랜치는 공통된 커밋을 가리키는 지점이라고 부르기도 한다. 브랜치를 생성한다는 의미는 기존 브랜치 또는 커밋에 새로운 연결 고리를 하나 더 만드는 것과 같다.
- 브랜치는 깃에서 또 하나의 개발 분기점을 의미하기도 한다.
- 커밋의 SHA1 해시키는 기억하기가 어렵기 때문에 특정 커밋을 가리키는 별칭을 만드는 것이다.
- 브랜치는 커밋처럼 SHA1 해시키를 가리킨다.
- 커밋은 파일의 수정 이력을 관리하는 데 사용한다면, 브랜치는 프로젝트를 독립적으로 관리하는데 사용한다.
- 개발자는 항상 안정된 코드 상태를 유지하고, 개발 중인 작업과 구분하여 관리해야 한다. 잦은 버그 수정과 새로운 기능을 구현할 때마다 작업 폴더를 복사하는 것은 프로젝트를 유지 관리하는 측면에서 좋지 않다. 또한 이러한 많은 프로젝트 폴더를 복제하면 향후 코드를 통합하기도 어렵다.
6.2. 깃 브랜치 특징
- 깃 브랜치는 기존 폴더를 복제하는 것과 다르게 가상 폴더를 사용하여 개발 작업을 구분한다.
- 깃에서 기본적으로 선택되는 브랜치는 master이다. 처음 깃을 초기화할 때 워킹 디렉터리는 master 브랜치를 생성한다. 꼭 기본값인 master 이름을 그대로 사용할 필요는 없다.
- 브랜치는 다음 특징들이 있다.
- 가상 폴더
- 깃의 브랜치는 작업 폴더를 실제로 복사하지 않고, 가상 폴더로 생성한다.
- 외부적으로는 물리적인 파일 하나만 있는 것으로 보인다.
- 생성된 작업 폴더는 물리적으로 복제된 구조보다 유연하게 처리할 수 있다.
- 브랜치로 생성된 가상 폴더는 빠르게 공간 이동이 가능하다.
- 개발자는 쉽게 가상 폴더인 브랜치를 이동하면서 프로젝트를 수행할 수 있다.
- 독립적인 동작
- 브랜치를 이용하면 원본 폴더와 분리하여 독립적으로 개발 작업을 수행할 수 있다.
- 기존에는 소스 코드의 작업 폴더를 별도로 생성하고, 물리적으로 복사된 각자의 폴더에서 코드를 작업한 후 소스 코드 2개를 다시 하나로 합쳐야 했다. 코드를 하나로 합치려면 작업 내역들을 일일이 찾아 정리해야 하기 때문에 소스 코드를 하나로 통합하는 것은 매우 힘든 작업이다.
- 깃과 같은 버전 관리 시스템을 이용하면 분리된 코드를 좀 더 쉽게 병합할 수 있다. 분리된 브랜치에서 소스 코드를 각자 수정한 후 원본 코드에 병합하는 명령만 실행하면 된다.
- 깃의 브랜치는 규모가 큰 코드 수정이나 병합을 처리할 때 매우 유용하다.
- 빠른 동작
- 다양한 버전 관리 도구도 브랜치 기능을 지원한다.
- 다른 버전 관리 시스템은 폴더의 파일 전체를 복사하는 반면, 깃은 41바이트를 가지는 해시(SHA1) 파일 하나만 만들면 되므로 브랜치를 더 빠르게 생성할 수 있다.
- 깃의 브랜치 기능은 다른 버전 관리 도구보다 가볍고, 브랜치 전환이 빠른 것이 특징이다.
- 깃은 Blob 개념을 도입하여 내부를 구조화한다.
- Blob은 포인트와 유사한 객체이다.
- 깃은 브랜치를 변경할 때 포인터를 이동하여 빠르게 전환한다.(checkout)
- 브랜치 명령을 사용하면 내부적으로 커밋을 하나 생성하여 브랜치로 할당한다
- 가상 폴더
6.3. 브랜치 생성
- 브랜치를 생성하면 포인터만 있는 브랜치가 생성된다.
- 기존 작업 영역에는 영향을 주지 않는 새로운 가상 공간(독립된 공간)이다. 따라서 기존 브랜치의 소스 코드에 영향을 주지 않고 새로운 작업을 할 수 있다.
- 일반적으로 브랜치 생성 명령을 실행하면 현재 커밋을 가리키는 HEAD를 기준으로 생성된다.
- CF) HEAD는 현재 마지막 커밋을 가리킨다.
- 처음 생성되는 기본 master 브랜치 외의 브랜치는 사용자가 직접 branch 명령어를 입력하여 생성해야 한다.
- 브랜치를 생성한다는 것은 기본적으로 제공되는 master 브랜치 이외에 사용자가 직접 정의한 사용자 브랜치를 이야기하는 것이다.
- 새로운 개발 분기점(브랜치)이 필요할 때는 브랜치를 추가로 생성할 수 있다.
- 브랜치 생성 개수에는 제한이 없다.
- 필요한 만큼 여러 브랜치를 생성할 수 있으며, 각 브랜치를 구분하려면 브랜치별로 이름을 지정해야 한다.
6.4. 브랜치 이름
- 브랜치 이름을 지을 때 사전 예약된 명칭은 따로 없다.
- 다만 해당 브랜치 작업은 알기 쉬운 이름으로 짓는 것이 좋다.
- 깃 플로(git flow)에서 정의한 브랜치 이름을 적용하는 것도 좋은 방법이다.
- 브랜치 이름은 슬래시(/)를 사용하여 계층적인 구조로 만들어서 사용할 수 있다.
- 작성 규칙은 다음과 같다.
- 기호(-)로 시작할 수 없음
- 마침표(.)로 시작할 수 없음
- 연속적인 마침표(..)를 포함할 수 없음
- 빈칸, 공백 문자, 물결(~), 캐럿(^), 물음표(?), 별표(*), 대괄호([ ]) 등은 포함할 수 없음
- 아스키 제어 문자는 포함할 수 없음
- 주의할 점은 브랜치 이름은 중복해서 사용하지 않아야 한다는 것이다. 이미 생성된 브랜치 이름과 동일한 이름으로 생성한다면 오류가 발생한다.
6.5. 브랜치 동작 원리
- HEAD 정보는 항상 변경된 브랜치의 마지막 커밋을 가리킨다.
- 이처럼 HEAD가 브랜치의 마지막 커밋을 의미하기 때문에 브랜치가 이동하면 HEAD 포인터도 함께 이동한다.
- 변경된 브랜치로 새로운 작업을 할 수 있도록 워킹 디렉터리를 변경(checkout) 한다. 즉, 브랜치가 변경되면 워킹 디렉터리도 같이 변환되는 것이다.
6.6. 워킹 디렉터리 정리
- 브랜치를 변경하려면 기존 브랜치의 워킹 디렉터리를 정리해야 한다. 기존 브랜치의 워킹 디렉터리를 정리하지 않고서는 브랜치를 이동이 제한된다.
- 워킹 디렉터리 안에서 작성하던 내용이 있고, 커밋을 하지 않았다면(작업이 남아 있다면) 체크아웃할 때 경고 메세지를 보여주고 브랜치를 변경할 수 없게 제한한다.
6.7. 브랜치 HEAD
- 브랜치를 이동하면 HEAD 포인트도 이동된다.
- 브랜치가 여러 개면 HEAD 포인트도 여러 개이다. 각각의 브랜치마다 마지막 커밋이 다르기 때문이다.
- 즉, 브랜치마다 마지막 커밋 ID를 가리키는 HEAD 포인터가 하나씩 있다.
6.8. HEAD 포인터의 상대적 위치
- 깃의 HEAD 포인터는 내부적으로 커밋을 생성하고 브랜치를 관리하는 데 매우 유용하다.
- 깃의 다양한 명령어를 입력할 때도 기준점으로 사용한다. 마지막 커밋 위치인 HEAD를 기준으로 상대적 커밋 위치도 지정할 수 있다.
- 상태적 커밋 위치를 지정할 때는 캐럿(^)과 물결(~) 기호를 같이 사용한다.
- ^과 ~은 HEAD를 기준으로 몇 번째인지 상대적인 위치를 지정해주는 것이다.
6.9. AHEAD, BHEAD
- HEAD 앞에 A 또는 B가 붙은 AHEAD와 BHEAD 포인터도 있다.
- 원격 저장소와 연동하여 깃을 관리한다면 브랜치마다 HEAD가 2개 있는 것을 알 수 있다. 로컬 저장소 브랜치의 HEAD 포인터와 원격 저장소 브랜치의 HEAD 포인터이다.
- 원격 저장소와 로컬 저장소는 물리적으로 서로 다른 저장소이다. 따라서 두 저장소의 마지막 커밋 위치가 일치하지 않을 수 있다. 이는 서로 다른 커밋을 가리키는 HEAD 포인터를 가진다는 의미이기도 하다.
- AHEAD와 BHEAD는 서로 다른 저장소 간 HEAD 포인터의 위치 차이를 의미한다. 깃은 항상 원격 저장소의 HEAD와 로컬 저장소의 HEAD를 비교한다.
- HEAD는 브랜치마다 다르기 때문에 브랜치를 여러 개 운영한다면 다수의 AHEAD와 BHEAD가 생길 수 있다.
AHEAD | * HEAD는 서버로 전송되지 않은 로컬 커밋이 있는 것 * 예를 들어 로컬 저장소에 새로운 커밋을 생성하고, 새로운 커밋 정보를 서버로 전송하지 않는 상황 * 로컬 저장소의 HEAD 포인터를 기준으로 로컬 브랜치에 있는 커밋이 서버의 커밋 개수보다 많은 경우 |
BHEAD | * BHEAD는 로컬 저장소로 내려받지 않은 커밋이 있는 것 * 예를 들어 누군가 서버에 새로운 커밋을 했는데 아직 로컬 저장소는 서버의 새로운 커밋을 내려받지 않은 상황 * 다른 개발자가 코드를 수정하여 원격 저장소의 커밋이 자신의 로컬 저장소보다 더 최신 상태인 것을 의미 |
6.10. 원격 브랜치
- 이번에는 브랜치를 이용하여 협업하는 방법을 알아보자
6.10.1. 리모트 브랜치
- 리모트 브랜치 : 원격 저장소에 생성한 브랜치
- 로컬 저장소에 생성한 브랜치는 서버로 공유할 수 있다.
- 그러나 원격 저장소와 연결된 로컬 저장소에서 새로운 브랜치를 생성한다고 해서 자동으로 원격 저장소에도 브랜치가 생성되는 것은 아니다. 원격 저장소에 등록된 브랜치가 자동으로 로컬 저장소를 만들지도 않는다. 별도 명령을 실행하여 저장소를 동기화해야 한다.
- 원격 저장소와 로컬 저장소의 브랜치 이름은 보통 같지만, 반드시 일치하지 않아도 괜찮다.
6.10.2. 브랜치 추적
- 깃의 브랜치는 특정 커밋 해시 값을 가리키는 포인터이다. 리모트 브랜치 또한 원격 저장소의 브랜치를 가리키는 포인터이다.
- 브랜치 추적(=추적 브랜치 = 트래킹 브랜치 = 업스트림(upstream)): 원격 저장소의 브랜치를 가리키는 것
- 원격 브랜치를 가리키는 북마크와 같다.
- 추적 브랜치는 원격 브랜치의 마지막 커밋 해시 값을 가리킨다.
- 이 포인터 정보는 .git/refs 폴더 안에 저장되어 있다.
- 로컬 저장소가 원격 저장소와 연결될 때 원격 브랜치의 트래킹 정보는 자동으로 갱신된다.
- 로컬 저장소는 마지막으로 연결된 리모트 브랜치의 커밋 해시 값을 항상 가지고 있다.
- 리모트 브랜치는 서버간의 통신을 하고 나서 생성된다.(by push)
- 그러고 나면 github에 브랜치가 올라와 있는 것을 확인할 수 있다.
- 업스트림 트래킹 : 로컬 저장소의 브랜치와 원격 저장소의 브랜치는 업로드할 수 있도록 매칭해주는 것
- 트래킹 브랜치(업스트림)는 리모트 브랜치와 로컬 브랜치를 연결해 주는 중간 다리 역할을 해준다.
- clone 명령어로 저장소를 복제할 때 원격 저장소에 등록된 트래킹 브랜치들을 자동으로 함께 설정해준다.
- clone 명령어는 원격 저장소의 모든 브랜치 정보를 한 번에 다 가지고 오지 않는다. git branch -vv로 트래킹 브랜치 목록 확인하면 하나만 표시되는 것을 알 수 있다.
- 이는 불필요한 브랜치를 한 번에 다 가져오는 것은 현명하지 않기 떄문이다. 이는 깃의 효율성과 연관된다.
- 이는 git checkout --track [원격저장소별칭]/[브랜치명]으로 새로운 업스트림을 만들 수 있다.
6.11. 원격저장소 관련 명령어
# 간단하게 브랜치 목록 확인
git branch
# 현재 브랜치가 어떤 커밋 해시값(SHA1)을 가리키는지 확인
# 브랜치의 해시 값과 브랜치를 생성한 기준 커밋의 해시값은 동일하다.
# 즉, 브랜치가 커밋의 해시를 기준으로 생성된다는 것을 알 수 있다.
git rev-parse [브랜치이름]
# 브랜치 세부 사항 확인(브랜치 이름, 커밋ID, 커밋 메시지)
git branch -v
git branch -verbose
# 모든 브랜치 정보 확인
git branch -a
# 브랜치 생성(HEAD 포인터 기준)
git branch [브랜치이름]
# 커밋 ID 기준으로 브랜치 생성
git branch [브랜치이름] [커밋ID]
# 깃에서 특정 브랜치로 이동(마지막 커밋 위치인 HEAD 포인트로 복귀)
git checkout [브랜치이름]
# 브랜치 생성 & 이동을 한번에 하기
git checkout -b [브랜치이름]
# 브랜치 이름 대신 커밋 해시키를 사용하여 브랜치간 이동
git checkout [커밋해시키]
# 현재의 n 단계 전으로 이동하기
git checkout HEAD~n
# 이전 브랜치로 이동(- : 리눅스에서 이전 디렉터리를 의미함)
git checkout -
# 브랜치 로그 출력(모든 로그를 출력)
git branch --graph --all
# 등록된 원격 저장소의 리모트 브랜치 확인
git remote show [원격저장소별칭]
# 로컬 저장소의 브랜치를 원격 저장소에 동기화
git push [원격저장소별칭] [브랜치이름]
# 로컬 저장소의 브랜치를 원격 저장소에 새 이름으로 동기화
git push [원격저장소별칭] [브랜치이름]:[새브랜치이름]
# 원격 저장소의 리모트 브랜치 목록 확인
git branch -r
# 복제 저장소의 트래킹 브랜치 확인
git branch -vv
# 새로운 업스트림(트래킹 브랜치) 만들기
git checkout --track [원격저장소별칭]/[브랜치이름]
# ----원격 브랜치의 브랜치 정보를 로컬 저장소로 가져옴.----
# 1.(브랜치 커밋 가져오기)
git fetch
2. (원격 브랜치 확인)
git branch -r
3. 브랜치 생성 및 이동
git checkout -b [로컬에저장할새이름] [원격저장소별칭]/[브랜치이름]
# --------------------------------------------------------
# 기존에 있는 브랜치를 업스트림으로 직접 설정(기존 브랜치를 특정 원격 브랜치로 추적)
# 한 번만 실행하면 이후로는 계속 업스트림으로 설정되어 작업할 수 있음.
git branch -u [원격저장소별칭]/[브랜치이름]
# 로컬 저장소의 파일들 + 브랜치 정보 + 커밋 모두 전송
git push
# 처음에는 다음과 같이 수동으로 트래킹 브랜치와 업스트림을 설정해야 함.
# 원격 저장소 연결만으로는 업스트림이 자동으로 설정되지는 않는다.
# 깃의 원격 저장소의 어느 브랜치에 어떻게 푸시해야할지 모르기 때문.
git push --set-upstream [원격저장소별칭]/[브랜치이름]
ex) 현재 master 브랜치읆 origin 서버의 master로 업스트림 설정
git push --set-upstream origin master
# 원격 저장소에서 fetch된 커밋들을 새로운 로컬 브랜치로 반영
# 리모트 브랜치 fetch는 일반적인 커밋 fetch와 동일하며
# 리모트 브랜치를 페치한다고 해서 자동으로 로컬 저장소에 새로운 브랜치가 생성되지는 않음.
git merge [원격저장소별칭/브랜치이름]
# fetch된 브랜치를 병합하지 않고 테스트만 하고 싶을 때 원격 브랜치의 포인터를 사용하여 임시 브랜치 생성&체크아웃
git checkout -b 임시브랜치이름 [원격저장소별칭]/[브랜치이름]
# 브랜치 삭제 : 해당 브랜치 내용과 커밋을 모두 삭제(단, 현재 자신이 있는 브랜치는 삭제할수 X)
#1. 스테이지 상태가 깨끗할 때(워킹디렉토리에 작업한 기록이 없거나 add 명령어로 스테이지의 인덱스가 변경된 상태가 아닐 때) 삭제 명령어
git branch -d [브랜치이름]
#2. 강제로 브랜치 삭제
git branch -D [브랜치이름]
7. 스태시 & 워킹 디렉터리 청소
7.1. 스태시란?
- 작업 브랜치를 변경하려면 워킹 디렉터리는 깨끗한(clean) 상태로 정리되어 있어야 한다. 워킹 디렉터리에 작업 중인 내용이나 커밋되지 않은 변경 사항들이 남아 있으면 브랜치를 변경할 수 없다.
- 현재 수정 작업을 멈추고, 다른 브랜치에 있는 코드를 수정하려면 스태시(stash) 기능을 사용할 수 있다. 스태시는 간단히 말해 ‘안전한 보관’임
- 스태시는 ‘현재 워킹 디렉터리 내역을 별도의 스택 영역에 잠시 보관하라’는 명령이다.
- 스태시는 브랜치를 이동할 때 작업 중인 내용 때문에 워킹 디렉터리가 충돌하는 것을 방지하는 데 사용한다.
- 현재 수정하는 작업들이 체크아웃으로 다른 브랜치의 워킹 디렉터리에 영향을 줄 수 있기 때문에!
- 스태시 명령을 실행하면 현재 작업 중인 내용은 영구적인 커밋 기록 대신 임시 스택 영역에 저장되고, 수정 전 마지막 커밋 상태로 돌아간다. 즉, 이전 커밋 후 작업하지 않은 상태의 워킹 디렉터리가 된다.
- 스태시는 스택 구조로 여러 번 실행하여 저장할 수 있다.
- 정상적으로 스태시 복원(pop)이 적용되면 저장된 스택은 자동으로 삭제한다.
7.2. 스태시 복원으로 인한 충돌
- 스태시를 복원할 때 워킹 디렉터리의 상태는 깨끗해야 한다. 스택에 저장된 스태시 내용이 다시 워킹 디렉터리로 복구될 때, 수정된 작업 내용과 현재 워킹 디렉터리를 병합하기 때문이다.
- 복구되는 브랜치의 워킹 디렉터리가 깨끗하지 않다면 병합 과정에서 충돌이 발생할 가능성이 많다.
- 특히 스태시를 복원할 때 같은 파일에서 동일한 부분을 변경했다면 즉시 충돌이 발생한다.
- 스태시를 복원할 때 충돌이 생기면 직접 문제를 해결해야 한다.
- 복원하는 도중 충돌이 생기면 스태시는 스택에 저장된 내용을 자동으로 삭제하지 않는다. 직접 충돌을 해결한 후 스태시 목록을 수동으로 삭제해야 한다.
- 스태시 충돌이 예상된다면 스태시용 브랜치를 하나 생성해서 작업하는 것을 추천한다.
7.3. 스태시 복사
pop | * pop 명령어는 스택 내용을 복원한 후 스택 목록에서 자동으로 삭제 * pop 명령어는 스택 내용을 워킹 디렉터리로 이동하는 것과 같음 |
apply | * 스태시 복원을 하고 난 후 스택 목록을 삭제하고 싶지 않을 때도 있다. 이때 apply 옵션을 사용한다. * 스택 목록을 읽은 후 자동으로 삭제하지 않기 때문에 반복적으로 스택에서 스태시 내용을 읽어 올 수 있다. * apply 명령어는 스태시 내용을 워킹 디렉터리로 복사하는 것과 같음 * apply 명령어는 pop 명령어와 달리 마지막 작업 내용이 아니라, 스택 목록의 중간 작업을 지정하여 적용할 수 있음 |
7.4. 워킹 디렉터리 청소
- 개발하는 과정에서는 컴파일 등 임시로 생성되는 파일들이 생긴다. 이 파일들은 .gitignore 목록에 넣어 관리할 수도 있지만, 반복해서 불필요하게 생성되는 파일들은 귀찮을 뿐이다.
- 이때 clean 명령어를 사용하면 워킹 디렉터리에 있는 추적되지 않는 파일들을 찾아 삭제한다.
- clean 명령어를 사용할 때는 주의할 점이 있는데, clean 명령어를 실행하는 순간 워킹 디렉터리의 추적되지 않는 모든 파일을 삭제한다는 것이다.
7.5. 스태시 & 워킹 디렉토리 청소 관련 명령어
# 스태시의 임시 스택 영역에 작업 중인 코드 저장
git stash
# 스태시 여러개를 생성할 때
git stash save
# 스태시가 여러개 있을 때 각각의 스태시를 구별할 수 있도록 메시지 추가
git stash save "WIP: 메시지~~"
# 스태시에 저장된 스택 목록 확인(목록 번호는 0부터 시작)
git stash list
# 방금 전의 스태시와 현재 워킹 디렉터리 간 차이 확인
git stash show
# 스태시에 저장된 작업 내용 중 제일 마지막에 저장된 내용 읽어오기(복원 후 자동으로 삭제)
git stash pop
# 스태시에 저장된 작업 내용 중 제일 마지막에 저장된 내용 읽어오기(복원 후 자동으로 삭제 X)
git stash apply
# 스택 목록의 n번째에 있는 작업을 복사
git stash apply stash@{n}
# 스태시 삭제
git stash drop
# 새로운 브랜치를 생성한 후 스태시를 적용(충돌 방지를 위해)
git stash branch [브랜치이름]
# 워킹 디렉터리에 있는 추적되지 않는 파일들을 찾아 삭제
git clean
# 워킹 디렉터리에 있는 추적되지 않는 파일들을 찾아 강제 삭제
git clean -f
# 워킹 디렉터리에 있는 추적되지 않는 파일들을 찾아 삭제
# clean 명령어를 사용하여 잘못 삭제하는 것을 미연에 방지하고자 파일을 가상으로 미리 처리해 보고 사용자에게 확인을 요청함
git clean -n
# .gitignore에 등록한 추적되지 않은 파일 까지도 삭제
git clean -x
8. 병합 & 리베이스
- 병합(합치기): 분리된 브랜치를 한 브랜치로 합치는 작업
- 두 코드를 하나씩 직접 비교해가며 수동으로 병합하거나 깃 같은 도구를 사용하여 자동으로 병합할 수 있다.
- merge 명령어는 현재 브랜치를 기준으로 다른 브랜치의 모든 커밋을 병합한다.
- 병합 후 불필요한 브랜치는 삭제하자.
8.1. Git의 병합 방식
Fast-forward 병합 | * 깃의 가장 간단한 브랜치 병합 * 영어 표현을 풀어 쓰면 빨리 감기라고 할 수 있음 * 일반적으로 Fast-Forward 병합 방식은 혼자 개발할 때 사용함 * 순차적 커밋에 맞추어 병합을 처리하는 방법이 Fast-Forward 병합임 * git merge [브랜치이름] * 아래는 병합전 커밋 상태 -> 체크아웃 -> 병합 후 사진이다. * 작업한 브랜치를 원본 브랜치에 병합할 때 작업한 브랜치의 시작 커밋을 원본 브랜치 이후의 커밋으로 가리킴. 이는 단순 커밋 위치를 최신으로 옮기는 것과 비슷함. * Fast-Forward 병합은 병합할 하나의 브랜치 파일을 기준 브랜치로 복사하여 수정된 파일을 원본에 그대로 적용한 것과 같음 * 원본에 추가된 내용이 없다는 가정하에 변경한 파일을 대체하는 것임 |
3-way 병합 | * 3-way 병합은 좀 더 복잡한 병합을 처리할 수 있는 방법 * 여러 개발자와 협업으로 작업하는 경우 대부분 3-way 병합을 사용함 * 공통 조상 커밋을 기준으로 브랜치 모양이 갈라지는 형태로 나뉠 때는 Fast-Forward 방식의 알고리즘을 적용하여 병합할 수 없음. 이때는 다른 병합 알고리즘인 3-way 방식을 이용해야 함 * 깃은 3-way 병합을 할 때 공통 조상 커밋을 자동으로 찾아줌. 이는 다른 VCS들보다 서로 다른 브랜치를 편리하게 병합할 수 있는 깃의 장점임 * 병합을 성공적으로 완료한 후에는 새로운 커밋을 추가로 하나 생성한다. 새로 생성된 커밋을 병합 커밋이라고 함. * 병합 커밋은 부모 커밋이 2개라는 특징이 있음 * 3-way 병합은 Fast-Forward 병합과 달리 병합 메시지가 필요함. 아래 명령어를 입력하면 vi에디터가 열림. * git merge [브랜치이름] --edit * 아래는 병합전 커밋 상태 -> 체크아웃 -> 병합 후 사진이다. |
8.2. 병합 과정 중 충돌
- 충돌이 발생하는 상황
- 여러 사람과 개발 작업을 하다 보면 예상외로 충돌이 자주 발생한다.
- 대부분의 충돌 원인은 같은 위치의 코드를 동시에 수정했기 때문이다.
- 파일을 수정할 때 여러 개발자가 서로 다른 위치를 수정했다면 깃에서 서로 다른 위치의 소스를 자동으로 병합하기 때문에 문제가 없다.
- 그러나 파일에서 동일한 위치에 두 명 이상이 서로 다르게 수정했다면 충돌이 발생한다. 같은 위치를 동시에 수정하면 두 수정 중 어떤 것이 맞는지 깃에서 자동으로 알 수 없기 때문이다.
- 이때 깃은 충돌 오류라고 알려 주고, 개발자에게 직접 수정하여 충돌을 해결하라고 요청한다. 병합 충돌이 발생하면 결국 수동으로 해결해야 한다. 직접 소스 코드를 보고 충돌된 부분을 확인한 후 코드를 수정하면 된다.
- 여기서 알 수 있는 것은 병합 충돌이 발생하면 자동으로 커밋이 생성되지 않는다는 것이다.
- 충돌이 발생하면 깃은 충돌 메시지를 출력하고 병합 작업을 중단한다.
- 충돌 예방하는 방법
- 내부적으로 팀원 간 규칙을 정하고 상의하면서 개발을 진행하면 향후 발생할 충돌을 많이 줄일 수 있다.
- 다른 방안으로는 master 브랜치 내용을 자주 자신의 브랜치로 병합하는 것이다.
- 자주 커밋하고 병합할수록 충돌이 발생할 기회는 적다.
- 많은 내용을 수정할수록 병합할 때 충돌이 발생하기 쉽다.
- 자신의 브랜치 상태가 최신일수록 향후 병합할 때 발생하는 충돌을 최소화할 수 있다.
- 충돌이 발생하면
- 1. 충돌난 코드를 직접 수정(이 때 충돌한 내용을 수정할 때는 깃에서 표시한 충돌 기호도 함께 삭제해야 한다)
- 2. git add .
- 3. git commit
8.2. 리베이스
- 리베이스는 커밋의 트리 구조를 재배열한다.
- 커밋을 재배열하는 변경 결과가 병합과 유사하다.
- 사실 실무에서는 merge 명령어보다는 커밋을 재배열하는 리베이스를 더 선호하는 편이다.
- 그림과 같이 새로운 브랜치가 파생되는 커밋2를 베이스(base)라고 한다. 병합에서는 이를 공통 조상 커밋이라 부른다.
- 위 그림과 같이 브랜치가 많아지면 커밋을 관리하고 파악하기 어렵다. 마치 꼬여 있는 기찻길처럼 단계별로 커밋을 따라가면서 코드를 확인해야 한다.
- 브랜치의 베이스를 변경하는 것(리베이스)은 아래와 같이 커밋의 진행 모습을 단순화하기 위해서 작업하는 것이다.
- 리베이스하게 되면 복잡한 트리 모양의 구조가 아니라 선형 구조로 브랜치를 변경할 수 있다.
- 베이스는 커밋 위치를 재조정할 뿐 브랜치의 HEAD 포인터까지 옮겨 주지는 않는다.
- 리베이스한 후에는 이러한 병합 브랜치의 HEAD를 맞추어야 한다. 즉, 리베이스된 브랜치를 병합해야 한다.
- ex) 순서
- 1. (description 브랜치에서) git rebase master (master 브랜치를 리베이스)
- 2. git checkout master
- 3. (master 브랜치에서) git merge description (HEAD 포인터 조정(병합)
- 리베이스한 후에 실행한 병합 메시지도 Fast-Forward 방식으로 병합했다는 것을 알 수 있다.
- 두 브랜치의 HEAD를 일치시키는 작업까지 해야 최종 병합을 완성할 수 있다.
- 리베이스 및 병합 작업까지 했다면 필요없는 브랜치는 삭제하자.
8.3. 리베이스 충돌
- 리베이스 역시 병합 과정에서 충돌이 발생할 수 있다. 리베이스 충돌 또한 사용자가 직접 수동으로 해결해야 한다.
- 충돌 해결 순서)
- 1. 충돌한 파일 내용에서 직접 수동으로 수정
- 2. git add [충돌한파일] : (다시 스테이지 상태로 변경)
- 3. git rebase --continue : (계속 리베이스 진행)
8.4. 리베이스할 때 주의할 점
- 저장소를 외부에 공개했다면 공개된 순간부터 커밋은 리베이스를 사용하지 않는 것이 원칙이다. 즉, 리베이스는 외부로 코드를 푸시하거나 공개하기 전에 로컬에서만 실행하는 것이 좋다.
- 이유는 리베이스는 커밋 위치와 해시값을 변경하는데 외부에 공개된 커밋을 리베이스하면 커밋 위치와 해시 값이 변경되어 너무 혼란스러워지기 때문이다.
- 공개된 커밋을 변경할 때는 다음 장에서 배울 revert 명령어를 사용한다.
8.5. 병합 vs 리베이스
병합 | * 병합은 파생된 두 브랜치를 하나로 합치는 과정 * 병합하려면 두 브랜치의 공통 조상 커밋을 먼저 찾아야 함 * 공통 조상 커밋을 찾으면 서로 다르게 커밋이 진행된 두 브랜치를 3-way 방식으로 병합할 수 있음 *공통 조상 커밋은 두 브랜치를 병합하는 베이스 커밋임 * 병합하는 두 브랜치는 순차적으로 커밋을 비교하면서 마지막 최종 커밋을 생성함 * merge 명령어를 사용한 병합은 현재의 기준 브랜치에서 다른 브랜치를 읽어와서 결합함. |
리베이스 | * 리베이스는 두 브랜치를 서로 비교하지 않고 순차적으로 커밋 병합을 시도함 * 리베이스에서 브랜치의 커밋을 결합하는 순서를 살펴보자 1. 리베이스를 하면 먼저 공통 조상 커밋을 찾음 2. 리베이스는 베이스 커밋을 변경하여 두 브랜치의 커밋 위치를 바꿈 └ 이때 커밋들은 재배치 작업을 하는데 이 과정에서 커밋의 해시 값이 변경됨 3. 파생된 브랜치의 diff를 임시 공간에 잠시 보관함 4. 기존 베이스 커밋2에서 커밋6으로 베이스 기준점을 변경함 5. 변경하는 기준 브랜치의 마지막 커밋에서 차례로 임시 공간에 저장한 diff를 하나씩 적용함 6. 새로운 베이스 기준점을 기반으로 한 브랜치에서 커밋3 → 커밋4를 커밋6에서 연장하여 수정 재배치함 *3-way 병합은 병합 커밋이 있지만, 리베이스를 하면 병합 커밋은 없음 * 브랜치의 마지막을 가리키는 커밋 위치가 다름(브랜치 A는 커밋4를 가리키지만, master 브랜치는 아직 커밋6을 가리킴) * 리베이스는 병합되는 브랜치 방향이 반대이다. |
8.6. 병합 & 리베이스 관련 명령어
# Fast-forward 방식 병합
git merge [브랜치이름]
# 3-way 방식 결합
git merge [브랜치이름] --edit
# 병합후 불필요한 브랜치 삭제(병합을 완료한 경우에만)
git branch -d [브랜치이름]
# 불필요한 브랜치 삭제(병합을 완료하지 않은 브랜치 삭제)
git branch -D [브랜치이름]
# 리베이스 작업
git rebase [브랜치이름]
# 충돌 해결 후 다시 스테이지 재등록된 상태에서 리베이스 계속 진행
git rebase --continue
'Computer Science > Open Source' 카테고리의 다른 글
Git : 1장 깃과 버전 관리 (0) | 2024.06.09 |
---|---|
Open Source Software에 대해서 (0) | 2024.06.08 |