PEP 103 – git에 관한 정보 수집
- Author:
- Oleg Broytman <phd at phdru.name>
- Status:
- Withdrawn
- Type:
- Informational
- Created:
- 01-Jun-2015
- Post-History:
- 12-Sep-2015
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
개요
이 정보성 PEP는 git에 관한 정보를 수집합니다. 물론 git에 대한 문서는 많이 있으므로, 이 PEP는 더 복잡한(그리고 Python 개발과 더 관련이 있는) 문제, 시나리오, 예제에 초점을 맞춥니다.
Mercurial에서 git으로 Python 개발을 이주하는 데 도움이 되도록, 향후 Mercurial과 git 시나리오 간의 동등성에 관한 정보를 수집하여 이 PEP를 확장할 계획입니다.
이 PEP의 저자는 현재 Mercurial에서 git으로 Python 개발을 이주하는 것에 관한 프로세스 PEP를 작성할 계획이 없습니다.
문서
git에는 온라인과 오프라인 모두에서 많은 문서가 함께 제공됩니다.
입문자를 위한 문서
고급 문서
Git Magic는 다양한 번역본을 제공합니다.
Pro Git. git에 관한 책입니다. Amazon에서 구매하거나 PDF, mobi, ePub 형식으로 다운로드할 수 있습니다. 다양한 언어로 번역되어 있습니다. GArik에서 러시아어 번역본을 다운로드하십시오.
Git Buch (독일어).
오프라인 문서
Git에는 내장 도움말이 있습니다: git help $TOPIC을 실행하십시오. 예를 들어, git help git 또는 git help help를 실행하십시오.
빠른 시작
다운로드 및 설치
Unix 사용자: 패키지 관리자를 사용하여 다운로드하고 설치하십시오.
Microsoft Windows: git-for-windows를 다운로드하십시오.
MacOS X: XCode와 함께 설치된 git을 사용하거나, MacPorts 또는 git-osx-installer에서 다운로드하거나, Homebrew로 git을 설치하십시오: brew install git.
git-cola (repository)는 Python으로 작성되었으며 GPL 라이선스가 적용된 Git GUI입니다. Linux, Windows, MacOS X.
TortoiseGit는 TortoiseSVN을 기반으로 한 Git용 Windows 셸 인터페이스이며, 오픈소스입니다.
초기 설정
이 간단한 코드는 문서에 자주 등장하지만 중요하므로 여기서도 다시 한 번 반복하겠습니다. Git은 모든 커밋에 작성자와 커미터의 이름/이메일을 저장하므로, 실제 이름과 선호하는 이메일 주소를 설정하십시오.:
$ git config --global user.name "User Name"
$ git config --global user.email user.name@example.org
이 PEP의 예제
이 PEP에서 Git 명령어 예제는 다음과 같은 방식을 사용합니다. 사용자인 여러분은 origin이라는 업스트림 원격 저장소를 가진 python이라는 로컬 저장소로 작업한다고 가정합니다. 여러분의 로컬 저장소에는 v1과 master라는 두 개의 브랜치가 있습니다. 대부분의 예제에서 현재 체크아웃된 브랜치는 master입니다. 즉, 여러분이 다음과 같은 작업을 이미 수행했다고 가정합니다.:
$ git clone https://git.python.org/python.git
$ cd python
$ git branch v1 origin/v1
첫 번째 명령은 원격 저장소를 로컬 디렉터리 python으로 클론하고, 새 로컬 브랜치 master를 생성하며, remotes/origin/master를 해당 브랜치의 업스트림 원격 추적 브랜치로 설정하고, 이를 작업 디렉터리에 체크아웃합니다.
마지막 명령은 새 로컬 브랜치 v1을 생성하고, remotes/origin/v1을 해당 브랜치의 업스트림 원격 추적 브랜치로 설정합니다.
동일한 결과는 다음 명령으로도 얻을 수 있습니다.:
$ git clone -b v1 https://git.python.org/python.git
$ cd python
$ git checkout --track origin/master
마지막 명령은 새 로컬 브랜치 master를 생성하고, remotes/origin/master를 해당 브랜치의 업스트림 원격 추적 브랜치로 설정하며, 이를 작업 디렉터리에 체크아웃합니다.
브랜치와 브랜치
Git 용어는 다소 오해의 소지가 있습니다. 예를 들어 “브랜치”라는 용어를 살펴보겠습니다. git에서는 이 용어가 두 가지 의미를 가집니다. 브랜치는 (병합을 포함할 수도 있는) 커밋들의 방향성 있는 선입니다. 그리고 브랜치는 커밋들의 선에 부여된 레이블이나 포인터이기도 합니다. 커밋에 대해 이야기하는 것인지 그것들의 레이블에 대해 이야기하는 것인지 구분하는 것이 중요합니다. 커밋들의 선 자체는 이름이 없으며 보통 길어지거나 병합될 뿐입니다. 반면에 레이블은 자유롭게 생성, 이동, 이름 변경, 삭제할 수 있습니다.
원격 저장소와 원격 브랜치
원격 추적 브랜치는 로컬 저장소에 있는 브랜치(커밋에 대한 포인터)입니다. 이는 git이(그리고 사용자가) 어떤 브랜치와 커밋이 어떤 원격 저장소로부터 풀되었고 어떤 원격 저장소로 푸시되었는지 기억하도록 하기 위해 존재합니다(여러 원격 저장소로부터 풀하고 여러 원격 저장소로 푸시할 수 있습니다). 원격 추적 브랜치는 remotes/$REMOTE 네임스페이스아래에 존재합니다. 예를 들면 remotes/origin/master와 같습니다.
원격 추적 브랜치의 상태를 확인하려면 다음을 실행하십시오:
$ git branch -rv
커밋을 가리키는 로컬 및 원격 추적 브랜치(및 태그)를 확인하려면:
$ git log --decorate
원격 추적 브랜치에서 직접 개발을 수행하는 일은 결코 없습니다. 원격 브랜치를 업스트림으로 하는 로컬 브랜치를 생성하고, 그 로컬 브랜치에서 개발을 수행합니다. 푸시 시 git은 커밋을 원격 저장소로 푸시하고 원격 추적 브랜치를 업데이트하며, 풀 시 git은 원격 저장소에서 커밋을 가져와 원격 추적 브랜치를 업데이트하고 로컬 브랜치를 패스트포워드, 병합 또는 리베이스합니다.
다음과 같이 최초 클론을 수행하는 경우:
$ git clone -b v1 https://git.python.org/python.git
git은 원격 저장소 https://git.python.org/python.git을 python 디렉터리로 클론하고, origin이라는 이름의 원격을 생성하고, 원격 추적 브랜치들을 생성하고, 로컬 브랜치 v1을 생성하고, 이를 업스트림 remotes/origin/v1 브랜치를 추적하도록 설정하고, 작업 디렉터리로 v1을 체크아웃합니다.
git status --branch나 git branch --verbose와 같은 일부 명령은 로컬 브랜치와 원격 브랜치 간의 차이를 보고합니다. 이러한 명령들은 로컬 저장소 내의 원격 추적 브랜치와만 비교를 수행하며, 그 원격 추적 브랜치의 상태는 오래되었을 수 있음을 기억하십시오. 원격 추적 브랜치를 업데이트하려면 원격 저장소로부터 커밋을 페치하여 병합(또는 리베이스)하거나, 로컬 브랜치를 업데이트하지 않고 원격 추적 브랜치만 업데이트해야 합니다.
로컬 브랜치와 원격 추적 브랜치 업데이트하기
로컬 브랜치를 업데이트하지 않고 원격 추적 브랜치만 업데이트하려면 git remote update [$REMOTE...]를 실행하십시오. 예를 들어:
$ git remote update
$ git remote update origin
페치와 풀
다음 사이에는 큰 차이가 있습니다
$ git fetch $REMOTE $BRANCH
그리고
$ git fetch $REMOTE $BRANCH:$BRANCH
첫 번째 명령은 $REMOTE 저장소의 이름이 지정된 $BRANCH에서 여러분의 저장소에 없는 커밋을 페치하고, 원격 추적 브랜치를 업데이트하며, 헤드 커밋의 id(해시)를 .git/FETCH_HEAD 파일에 남깁니다.
두 번째 명령은 $REMOTE 저장소에 있는 $BRANCH라는 이름의 브랜치로부터 사용자의 저장소에 없는 커밋을 가져와서, 로컬 브랜치 $BRANCH와 그 업스트림 원격 추적 브랜치를 모두 갱신합니다. 하지만 되감기가 불가능한(non-fast-forward) 경우에는 브랜치 갱신을 거부합니다. 또한 현재 브랜치(HEAD가 가리키는, 현재 체크아웃된 브랜치)의 갱신도 거부합니다.
첫 번째 명령은 git pull이 내부적으로 사용하는 명령입니다.
$ git pull $REMOTE $BRANCH
다음과 동등합니다
$ git fetch $REMOTE $BRANCH
$ git merge FETCH_HEAD
물론 이 경우 $BRANCH는 사용자의 현재 브랜치여야 합니다. 다른 브랜치를 현재 브랜치에 병합하고자 한다면, 먼저 현재 브랜치가 아닌 그 브랜치를 갱신한 다음 병합하십시오:
$ git fetch origin v1:v1 # Update v1
$ git pull --rebase origin master # Update the current branch master
# using rebase instead of merge
$ git merge v1
하지만 v1에 아직 커밋을 푸시하지 않았다면, 시나리오가 조금 더 복잡해져야 합니다. Git은 되감기가 불가능한 브랜치의 갱신을 거부하며, 강제 풀(force-pull)을 하고 싶지는 않을 것입니다. 그렇게 하면 아직 푸시하지 않은 커밋이 제거되어 이를 복구해야 하기 때문입니다. 그래서 v1을 리베이스하고 싶지만, 현재 브랜치가 아닌 브랜치는 리베이스할 수 없습니다. 따라서 병합하기 전에 v1을 체크아웃하여 리베이스하십시오:
$ git checkout v1
$ git pull --rebase origin v1
$ git checkout master
$ git pull --rebase origin master
$ git merge v1
git이 몇 개의 브랜치 또는 모든 브랜치를 한 번에 페치/풀하도록 설정할 수 있으므로, 다음과 같이 간단히 실행할 수 있습니다
$ git pull origin
또는 다음과 같이도 가능합니다
$ git pull
페치/풀을 위한 기본 원격 저장소는 origin입니다. 페치할 참조의 기본 집합은 매칭 알고리즘을 사용하여 계산됩니다. 즉, git은 양쪽에서 같은 이름을 가진 모든 브랜치를 페치합니다.
푸시
푸시는 조금 더 간단합니다. push 명령 하나만 있습니다. 다음을 실행하면
$ git push origin v1 master
git은 로컬 v1을 원격 v1로, 로컬 master를 원격 master로 푸시합니다. 다음과 같습니다:
$ git push origin v1:v1 master:master
Git은 커밋을 원격 저장소로 푸시하고 원격 추적 브랜치를 갱신합니다. Git은 패스트포워드할 수 없는 커밋의 푸시를 거부합니다. 그래도 강제 푸시를 할 수는 있지만, 자신의 저장소에는 강제 푸시를 해도 되지만 공개 저장소나 공유 저장소에는 강제 푸시를 하지 말아야 한다는 점을 기억하십시오. git이 패스트포워드할 수 없는 커밋의 푸시를 거부한다면, 원격 저장소에서 커밋을 페치하여 병합하거나(또는 페치한 커밋 위에 자신의 커밋을 리베이스한 다음) 푸시하는 것이 좋습니다. 자신이 무엇을 왜 하는지 알고 있을 때만 강제 푸시를 하십시오. 아래의 Commit editing and caveats 절을 참조하십시오.
몇 개의 브랜치 또는 모든 브랜치를 한 번에 푸시하도록 git을 설정할 수 있으므로, 간단히 다음을 실행하면 됩니다
$ git push origin
또는 심지어
$ git push
푸시를 위한 기본 원격 저장소는 origin입니다. git 2.0 이전에서 푸시할 참조의 기본 집합은 매칭 알고리즘을 사용하여 계산됩니다: git은 양쪽 끝에서 이름이 같은 모든 브랜치를 푸시합니다. git 2.0 이상에서 푸시할 참조의 기본 집합은 단순 알고리즘을 사용하여 계산됩니다: git은 현재 브랜치를 그 @{upstream}으로 다시 푸시합니다.
2.0 이전 버전의 git을 새로운 동작 방식으로 설정하려면 다음을 실행하십시오.:
$ git config push.default simple
2.0 이상 버전의 git을 이전 동작 방식으로 설정하려면 다음을 실행하십시오.:
$ git config push.default matching
Git은 원격 저장소가 비베어(non-bare) 저장소인 경우, 해당 브랜치가 원격 저장소의 현재 브랜치일 때는 푸시를 허용하지 않습니다: git은 원격 작업 디렉터리를 갱신하는 것을 거부합니다. 정말로 베어(bare) 저장소에만 푸시해야 합니다. 비베어(non-bare) 저장소에 대해서는 git이 풀(pull) 기반 워크플로를 선호합니다.
원격 호스트에 코드를 배포하고자 하는데 푸시만 사용할 수 있는 경우(작업 환경이 방화벽 뒤에 있어 그곳에서 풀을 받을 수 없기 때문에), 두 개의 저장소를 사용하는 두 단계로 이를 수행합니다: 작업 환경에서 원격 호스트의 베어 저장소로 푸시한 다음, ssh로 원격 호스트에 접속하여 베어 저장소에서 비베어 배포 저장소로 풀을 받습니다.
이는 git 2.3에서 변경되었지만, 주의 사항은 해당 블로그 게시물을 참고하십시오. 2.4에서는 push-to-deploy 기능이 추가로 개선되었습니다.
태그
Git은 fetch/pull 중에 가져오는 커밋을 가리키는 태그를 자동으로 가져옵니다. 모든 태그(및 그것들이 가리키는 커밋)를 가져오려면 git fetch --tags origin을 실행하십시오. 특정 태그만 가져오려면 명시적으로 가져오십시오.:
$ git fetch origin tag $TAG1 tag $TAG2...
예를 들면:
$ git fetch origin tag 1.4.2
$ git fetch origin v1:v1 tag 2.1.7
Git은 태그를 자동으로 푸시하지 않습니다. 이 덕분에 비공개 태그를 가질 수 있습니다. 태그를 푸시하려면 명시적으로 나열하십시오:
$ git push origin tag 1.4.2
$ git push origin v1 master tag 2.1.7
또는 모든 태그를 한 번에 푸시하십시오:
$ git push --tags origin
태그가 게시된 후에는 git tag -f로 태그를 이동하거나 git tag -d로 태그를 삭제하지 마십시오.
비공개 정보
클론/페치/풀/푸시를 할 때 git은 데이터베이스 객체(커밋, 트리, 파일, 태그)와 심볼릭 참조(브랜치와 경량 태그)만 복사합니다. 그 외의 모든 것은 저장소에 비공개이며 절대 클론, 업데이트 또는 푸시되지 않습니다. 이는 여러분의 설정, 여러분의 훅, 여러분의 비공개 제외 파일입니다.
훅을 배포하고 싶다면, 이를 작업 트리에 복사하고 add, commit, push한 후 팀에게 훅을 수동으로 업데이트하고 설치하도록 안내하십시오.
커밋 편집과 주의 사항
게시된(푸시된) 커밋을 편집하지 말라는 경고는 문서에도 나와 있지만, 매우 중요한 내용이므로 여기서도 반복합니다.
강제 푸시로부터 복구하는 것이 가능하기는 하지만 팀 전체에게 매우 성가신 일입니다. 이를 피하십시오.
아직 게시되지 않은 커밋을 확인하려면 브랜치의 head를 해당 브랜치의 업스트림 리모트 추적 브랜치와 비교하십시오:
$ git log origin/master.. # from origin/master to HEAD (of master)
$ git log origin/v1..v1 # from origin/v1 to the head of v1
업스트림 원격 추적 브랜치가 있는 모든 브랜치에 대해 git은 @{upstream}(축약형 @{u})이라는 별칭을 유지하므로, 위 명령들은 다음과 같이 줄여 쓸 수 있습니다:
$ git log @{u}..
$ git log v1@{u}..v1
모든 브랜치의 상태를 확인하려면:
$ git branch -avv
로컬 브랜치의 상태를 원격 저장소와 비교하려면:
$ git remote show origin
업스트림 리베이스로부터 복구하는 방법을 읽으십시오. 이는 git help rebase에 있습니다.
반면, 커밋 편집에 대해 너무 두려워하지 마십시오. 아직 푸시되지 않은 커밋은 안전하게 편집, 재정렬, 제거, 결합, 분할할 수 있습니다. 자신의 (백업) 저장소에 커밋을 푸시했다가 나중에 편집하여, 이미 푸시된 것을 대체하도록 편집된 커밋을 강제 푸시할 수도 있습니다. 커밋이 공개 또는 공유 저장소에 들어가기 전까지는 문제가 되지 않습니다.
되돌리기
무엇을 하든, 당황하지 마십시오. git에서는 거의 모든 것을 되돌릴 수 있습니다.
git checkout: 파일 내용 복원
예를 들어 git checkout은 파일의 내용을 어느 커밋의 내용으로 복원하는 데 사용할 수 있습니다. 이렇게:
git checkout HEAD~ README
이 명령은 현재 브랜치에서 마지막에서 두 번째 커밋의 README 파일 내용을 복원합니다. 기본적으로 커밋 ID는 단순히 HEAD입니다. 즉, git checkout README는 README를 최신 커밋으로 복원합니다.
(커밋 안에 있는 파일의 내용을 보려면 git checkout을 사용하지 말고 git cat-file -p를 사용하십시오; 예: git cat-file -p HEAD~:path/to/README).
git reset: (푸시되지 않은) 커밋 제거
git reset은 현재 브랜치의 헤드를 이동시킵니다. 헤드는 어떤 커밋이든 가리키도록 이동할 수 있지만, 흔히 브랜치 맨 위에서 커밋 하나 또는 여러 개(가급적 푸시되지 않은 것)를 제거하는 데 사용됩니다 - 즉, (푸시되지 않은) 커밋 몇 개를 되돌리기 위해 브랜치를 뒤로 이동시키는 것입니다.
git reset은 soft, hard, mixed라는 세 가지 동작 모드를 가지고 있습니다. 기본값은 mixed입니다. ProGit은 그 차이를 아주 명확하게 설명합니다. 베어 리포지토리에는 인덱스나 작업 트리가 없으므로, 베어 리포지토리에서는 soft reset만 가능합니다.
언스테이징
경로 하나 또는 여러 경로를 지정한 mixed 모드 리셋은 변경 사항을 언스테이징하는 데 사용할 수 있습니다 - 즉, 커밋을 위해 git add로 추가된 변경 사항을 인덱스에서 제거하는 것입니다. 언스테이징과 그 밖의 되돌리기 기법에 대한 자세한 내용은 The Book을 참조하십시오.
git reflog: 참조 로그
git reset으로 커밋을 제거하거나 브랜치의 헤드를 이동시키는 것은 위험하게 들리며, 실제로 위험합니다. 하지만 되돌리는 방법이 있습니다: 원래 커밋으로 다시 리셋하는 것입니다. Git은 커밋을 즉시 제거하지 않습니다; 참조되지 않은 커밋(git 용어로는 “dangling commits”라고 부릅니다)은 일정 시간(기본값은 2주) 동안 데이터베이스에 남아 있으므로, 원래 커밋으로 다시 리셋하거나 원래 커밋을 가리키는 새 브랜치를 만들 수 있습니다.
브랜치의 헤드가 이동할 때마다 - git commit, git checkout, git fetch, git pull, git rebase, git reset 등으로 - git은 참조 로그(줄여서 reflog)를 저장합니다. 이동이 있을 때마다 git은 헤드가 있던 위치를 저장합니다. git reflog 명령을 사용하여 이 로그를 확인(및 조작)할 수 있습니다.
git은 모든 브랜치의 헤드 이동뿐만 아니라 HEAD의 이동도 저장하는데, HEAD는 (보통) 현재 브랜치를 가리키는 심볼릭 참조입니다. HEAD는 git checkout $BRANCH로 변경됩니다.
기본적으로 git reflog는 HEAD의 이동을 보여주며, 즉 이 명령은 git reflog HEAD와 동등합니다. 브랜치의 헤드 이동을 보려면 git reflog $BRANCH 명령을 사용하십시오.
따라서 git reset을 되돌리려면 git reflog에서 원래 커밋을 찾아 git show 또는 git log로 확인한 다음 git reset $COMMIT_ID를 실행하십시오. Git은 브랜치 헤드의 이동을 reflog에 저장하므로, 나중에 그 되돌리기를 다시 되돌릴 수 있습니다.
더 복잡한 상황에서는 브랜치의 헤드를 리셋하면서 일부 커밋을 함께 옮기고 싶을 수 있습니다. 그것들을 새 브랜치에 체리픽하십시오. 예를 들어, master 브랜치를 원래 커밋으로 되돌리되 현재 브랜치에서 생성된 두 개의 커밋은 보존하고 싶다면 다음과 같이 하십시오.:
$ git branch save-master # create a new branch saving master
$ git reflog # find the original place of master
$ git reset $COMMIT_ID
$ git cherry-pick save-master~ save-master
$ git branch -D save-master # remove temporary branch
git revert: 커밋 되돌리기
git revert는 하나 이상의 커밋을 되돌리는데, 즉 주어진 커밋들의 효과를 되돌리는 새 커밋(들)을 생성합니다. 이는 이미 공개된 커밋을 되돌릴 수 있는 유일한 방법입니다(git commit --amend, git rebase, git reset은 브랜치를 패스트포워드가 불가능한 방식으로 변경하므로, 아직 푸시되지 않은 커밋에만 사용해야 합니다.)
병합 커밋을 되돌리는 데는 문제가 있습니다. git revert는 병합 커밋으로 생성된 코드는 되돌릴 수 있지만 병합이라는 사실 자체는 되돌릴 수 없습니다. 잘못된 병합을 되돌리는 방법 논의를 참조하십시오.
되돌릴 수 없는 한 가지
무엇을 되돌리든, 되돌릴 수 없는 한 가지가 있습니다 - 덮어쓰인 커밋되지 않은 변경 사항입니다. 커밋되지 않은 변경 사항은 git에 속하지 않으므로 git은 이를 보존하는 데 도움을 줄 수 없습니다.
대부분의 경우 git은 커밋되지 않은 변경 사항을 덮어쓰는 명령을 실행하려 할 때 경고합니다. Git은 git checkout으로 브랜치를 전환하는 것을 허용하지 않습니다. 작업 트리가 깨끗하지 않은 상태로 리베이스하려 할 때 이를 막습니다. 커밋되지 않은 파일 위로 새 커밋을 풀하는 것을 거부합니다.
하지만 정확히 그런 일을 하는 명령들이 있습니다 - 작업 트리의 파일을 덮어씁니다. git checkout $PATHs나 git reset --hard 같은 명령은 커밋되지 않은 변경 사항을 포함한 파일을 조용히 덮어씁니다.
이를 염두에 두면 “일찍 커밋하고, 자주 커밋하라”는 입장을 이해할 수 있습니다. 가능한 한 자주 커밋하십시오. 에디터나 IDE에서 저장할 때마다 커밋하십시오. 푸시하기 전에 커밋을 편집할 수 있습니다 - 커밋 메시지 편집, 커밋 변경, 순서 재배치, 합치기, 나누기, 제거하기입니다. 하지만 변경 사항은 git 데이터베이스에 저장하십시오 - 변경 사항을 커밋하거나 최소한 git stash로 스태시해 두십시오.
병합인가, 리베이스인가?
인터넷에는 “병합인가, 리베이스인가?”라는 주제에 관한 열띤 논쟁이 넘쳐납니다. 그중 대부분은 무의미합니다. 많은 브랜치를 가진 크고 복잡한 프로젝트를 진행하는 대규모 팀에서 DVCS를 사용할 때는, 병합을 피할 방법이 전혀 없습니다. 그래서 질문은 “리베이스를 사용할지, 그리고 사용한다면 언제 사용할지”로 축소됩니다. 이미 공개된 커밋은 리베이스하지 않는 것이 강력히 권장된다는 점을 고려하면, 질문은 한층 더 축소되어 “아직 푸시하지 않은 커밋에 리베이스를 사용할지”가 됩니다.
그 작은 질문에 대한 결정은 팀의 몫입니다. 선형 히스토리의 아름다움을 유지하려면, 풀할 때 리베이스를 사용하는 것, 즉 git pull --rebase를 실행하는 것이 권장되며, 새 브랜치마다 리베이스가 자동으로 설정되도록 구성할 수도 있습니다:
$ git config branch.autosetuprebase always
그리고 기존 브랜치에 대해서도 리베이스를 구성할 수 있습니다:
$ git config branch.$NAME.rebase true
예를 들면:
$ git config branch.v1.rebase true
$ git config branch.master.rebase true
그 이후로는 git pull origin master가 git pull --rebase origin master와 동일해집니다.
메인라인 브랜치를 업데이트할 때는 리베이스를 사용하되, 새 커밋은 별도의 기능 브랜치나 토픽 브랜치에서 생성하는 것이 권장됩니다. 토픽 브랜치가 준비되면 메인라인에 병합하십시오. 한꺼번에 많은 수의 충돌을 해결해야 하는 지루한 작업을 피하려면, 때때로 토픽 브랜치를 메인라인에 병합한 다음 다시 토픽 브랜치로 전환하여 작업을 계속할 수 있습니다. 전체 작업 흐름은 대략 다음과 같습니다:
$ git checkout -b issue-42 # create a new issue branch and switch to it
...edit/test/commit...
$ git checkout master
$ git pull --rebase origin master # update master from the upstream
$ git merge issue-42
$ git branch -d issue-42 # delete the topic branch
$ git push origin master
토픽 브랜치가 삭제되면 레이블만 제거될 뿐, 커밋은 데이터베이스에 그대로 남아 있으며, 이제 master에 병합된 상태입니다:
o--o--o--o--o--M--< master - the mainline branch
\ /
--*--*--* - the topic branch, now unnamed
토픽 브랜치는 작은 토픽 브랜치들로 브랜치 네임스페이스가 지저분해지는 것을 피하기 위해 삭제됩니다. 어떤 이슈가 수정되었는지, 또는 어떤 기능이 구현되었는지에 대한 정보는 커밋 메시지에 담겨 있어야 합니다.
그러나 오래 지속되는 병합 브랜치의 경우에는 그 정도의 적은 리베이스조차 너무 클 수 있습니다. v1과 master 브랜치 양쪽에서 작업하면서, v1을 master에 정기적으로 병합한다고 가정해 보십시오. 얼마 후에는 master에 병합 커밋과 비병합 커밋이 많이 쌓이게 됩니다. 그런 다음 완성된 작업을 공유 저장소에 푸시하려고 하는데, 누군가 이미 v1에 몇 개의 커밋을 푸시한 것을 발견합니다. 이제 여러분에게는 똑같이 나쁜 두 가지 대안 중 하나를 선택해야 하는 상황이 벌어집니다: v1을 페치하고 리베이스한 다음 master에서의 모든 작업을 다시 만들거나(master를 원본으로 리셋하고, v1을 병합한 뒤 이전 master의 모든 비병합 커밋을 체리픽), 새로운 v1을 병합하여 선형 히스토리의 아름다움을 잃거나입니다.
널 병합(Null-merges)
Git에는 Python 핵심 개발자들이 “널 병합(null-merge)”이라고 부르는 것을 위한 내장 병합 전략이 있습니다.:
$ git merge -s ours v1 # null-merge v1 into master
브랜칭 모델
Git은 브랜칭 및 병합과 관련하여 특정한 개발 모델을 가정하지 않습니다. 어떤 프로젝트는 패치를 가장 오래된 브랜치에서 가장 최신 브랜치로 승격시키는 방식을 선호하고, 어떤 프로젝트는 커밋을 거꾸로 체리픽하는 방식을 선호하며, 어떤 프로젝트는 스쿼시(여러 커밋을 하나로 합치는 것)를 사용합니다. 무엇이든 가능합니다.
시작할 만한 몇 가지 예가 있습니다. git help workflows는 git 저자들이 직접 git을 개발하는 방식을 설명합니다.
ProGit 책에는 여러 프로젝트의 브랜치 관리에 할애된 몇몇 장이 있습니다: Git Branching - Branching Workflows와 Distributed Git - Contributing to a Project입니다.
Vincent Driessen이 쓴 A successful Git branching model이라는 유명한 글도 있습니다. 이 글은 메인라인, 토픽, 버그픽스 브랜치를 생성하고 관리하는 것에 대한 매우 상세한 규칙 세트를 권장합니다. 이 모델을 지원하기 위해 저자는 git flow 확장을 구현했습니다.
고급 설정
줄 끝 문자
Git에는 서로 다른 줄 끝 스타일을 사용하는 플랫폼 간에 줄 끝 문자를 처리하는 내장 메커니즘이 있습니다. git이 CRLF 변환을 수행하도록 하려면 .gitattributes를 사용하여 파일에 text 속성을 지정하십시오. 특정 줄 끝 문자를 가져야 하는 파일에는 eol 속성을 지정하십시오. 바이너리 파일의 경우 속성은 당연히 binary입니다.
예를 들어:
$ cat .gitattributes
*.py text
*.txt text
*.png binary
/readme.txt eol=CRLF
파일에 대해 git이 어떤 속성을 사용하는지 확인하려면 git check-attr 명령을 사용하십시오. 예를 들어:
$ git check-attr -a -- \*.py
유용한 자료
GitAlias (repository)은 별칭들의 방대한 모음입니다. 자주 사용하는 명령어에 대해 신중하게 별칭을 선택하면 많은 타이핑을 절약할 수 있습니다!
GitIgnore와 https://github.com/github/gitignore 는 모든 종류의 IDE와 프로그래밍 언어를 위한 .gitignore 파일 모음입니다. Python도 포함됩니다!
pre-commit (repositories)은 다국어 pre-commit 훅을 관리하고 유지하기 위한 프레임워크입니다. 이 프레임워크는 Python으로 작성되었으며 다양한 프로그래밍 언어를 위한 많은 플러그인을 가지고 있습니다.
고급 주제
스테이징 영역
스테이징 영역(일명 인덱스, 일명 캐시)은 git의 독특한 특징입니다. 스테이징 영역은 git이 커밋하기 전에 패치를 수집하는 곳입니다. 패치 수집 단계와 커밋 단계의 분리는 git의 매우 유용한 기능을 제공합니다: 커밋 전에 수집된 패치를 검토할 수 있으며 심지어 편집할 수도 있습니다 - 일부 헝크를 제거하고, 새 헝크를 추가하고, 다시 검토할 수 있습니다.
인덱스에 파일을 추가하려면 git add를 사용하십시오. 커밋하기 전에 패치를 수집한다는 것은 새 (추적되지 않은) 파일을 추가할 때뿐만 아니라 모든 변경 사항에 대해 이를 수행해야 한다는 의미입니다. 검토 없이 그냥 모든 것을 커밋하고 싶은 경우 커밋을 단순화하려면 git commit --all (또는 그냥 -a)을 실행하십시오 - 이 명령은 변경된 모든 추적 파일을 인덱스에 추가한 다음 커밋합니다. 인덱스에 수집된 패치와 상관없이 파일 하나 또는 여러 파일을 커밋하려면 git commit [--only|-o] -- $FILE...를 실행하십시오.
패치의 헝크를 인덱스에 추가하려면 git add --patch (또는 그냥 -p)를 사용하십시오. 인덱스에서 수집된 파일을 제거하려면 git reset HEAD -- $FILE...를 사용하십시오. 수집된 헝크를 추가/검사/제거하려면 git add --interactive (-i)를 사용하십시오.
인덱스와 마지막 커밋 사이의 차이(즉, 수집된 패치)를 보려면 git diff --cached를 사용하십시오. 작업 트리와 인덱스 사이의 차이(즉, 수집되지 않은 패치)를 보려면 그냥 git diff를 사용하십시오. 작업 트리와 마지막 커밋 사이의 차이(즉, 수집된 패치와 수집되지 않은 패치 모두)를 보려면 git diff HEAD를 실행하십시오.
Git Wiki의 WhatIsTheIndex와 IndexCommandQuickref를 참조하십시오.
루트
Git은 어떤 명령을 실행하기 전에 루트(.git 서브디렉터리가 존재하는 프로젝트의 최상위 디렉터리)로 전환합니다. 하지만 Git은 전환 전에 현재 디렉터리였던 위치를 기억합니다. 일부 프로그램은 현재 디렉터리를 고려합니다. 예를 들어, git status는 변경되거나 알 수 없는 파일들의 경로를 현재 디렉터리를 기준으로 표시하며, git grep은 현재 디렉터리 이하를 검색하고, git apply는 패치 중 현재 디렉터리 이하의 파일에 영향을 주는 헝크만 적용합니다.
하지만 대부분의 명령은 루트에서 실행되며 현재 디렉터리를 무시합니다. 예를 들어, v1 브랜치용과 master용으로 두 개의 작업 트리가 있다고 상상해 보십시오. 두 번째 작업 트리 내부의 서브디렉터리에서 v1을 병합하려면 마치 최상위 디렉터리에 있는 것처럼 명령을 작성해야 합니다. 예를 들어, project-v1과 project라는 두 개의 작업 트리가 있다고 합시다:
$ cd project/subdirectory
$ git fetch ../project-v1 v1:v1
$ git merge v1
서브디렉터리에서 명령을 실행하고 있음에도 불구하고 git fetch ../project-v1 v1:v1의 경로가 ../../project-v1이 아니라 ../project-v1임에 유의하십시오.
ReReRe
Rerere는 반복되는 병합 충돌을 해결하는 데 도움을 주는 메커니즘입니다. 반복되는 병합 충돌의 가장 흔한 원인은 메인라인에 병합된 후 병합 커밋이 제거되는 토픽 브랜치입니다. 이는 토픽 브랜치를 테스트하고 rerere를 훈련시키기 위해 종종 수행됩니다. 깔끔한 선형 히스토리를 유지하고 마지막 병합 커밋 하나로만 토픽 브랜치를 마무리하기 위해 병합 커밋이 제거됩니다.
Rerere는 성공적인 커밋 전후의 트리 상태를 기억함으로써 작동합니다. 이 방식으로 rerere는 같은 파일에서 충돌이 발생하면 자동으로 이를 해결할 수 있습니다.
Rerere는 git rerere 명령어와 함께 수동으로 사용될 수 있지만, 대부분의 경우 자동으로 사용됩니다. 작업 트리에서 다음 명령어로 rerere를 활성화하십시오:
$ git config rerere.enabled true
$ git config rerere.autoupdate true
rerere를 전역으로 켤 필요는 없습니다 - bare 저장소나 단일 브랜치 저장소에서는 rerere를 원하지 않을 것입니다; 병합을 자주 수행하고 병합 충돌을 해결하는 저장소에서만 rerere가 필요합니다.
The Book에서 Rerere를 참고하십시오.
데이터베이스 유지 관리
Git 오브젝트 데이터베이스와 .git 아래의 다른 파일/디렉터리들은 주기적인 유지 관리와 정리가 필요합니다. 예를 들어, 커밋 편집은 참조되지 않는 오브젝트(git 용어로 dangling object)를 남기며, DB에 잡동사니가 쌓이는 것을 피하기 위해 이러한 오브젝트들은 정리(prune)되어야 합니다. git gc 명령어는 유지 관리에 사용됩니다. Git은 빠른 유지 관리를 수행하기 위해 일부 명령어의 일환으로 git gc --auto를 자동으로 실행합니다. 사용자는 때때로 git gc --aggressive를 실행할 것을 권장합니다; git help gc는 수백 개의 변경 집합마다 실행할 것을 권장합니다; 더 활발한 프로젝트라면 일주일에 한 번 정도, 덜 활발한 프로젝트라면 그보다 덜 자주(격주 또는 매월) 실행하는 것이 좋습니다.
git gc --aggressive는 dangling object를 제거할 뿐만 아니라, 오브젝트 데이터베이스를 색인화되고 더 잘 최적화된 팩(pack)으로 재압축하며, 심볼릭 참조(브랜치와 태그)도 패킹합니다. 다른 방법은 git repack을 실행하는 것입니다.
git gc --aggressive의 “어리석음”에 관한 리누스 토르발스(Linus Torvalds)의 잘 알려진 메시지가 있습니다. 이제 이 메시지는 안전하게 무시해도 됩니다. 오래되고 낡은 것으로, 그 이후로 git gc --aggressive는 훨씬 더 나아졌습니다.
git gc --aggressive보다 git repack을 여전히 선호하는 사람들을 위한 권장 매개변수는 git repack -a -d -f --depth=20 --window=250입니다. 이러한 매개변수의 효과에 대한 설명은 이 상세한 실험을 참조하십시오.
데이터베이스의 무결성을 검증하기 위해 가끔 git fsck [--strict]를 실행하십시오. git fsck는 매달린(dangling) 객체 목록을 출력할 수 있는데, 이는 오류가 아니라 정기적인 유지보수를 수행하라는 알림일 뿐입니다.
팁과 요령
명령줄 옵션과 인자
git help cli는 짧은 옵션/플래그를 결합하지 말 것을 권장합니다. 대부분의 경우 결합해도 잘 동작합니다: git commit -av는 완벽하게 작동하지만, 그렇지 않은 상황도 있습니다. 예를 들어, git log -p -5는 git log -p5로 결합할 수 없습니다.
일부 옵션은 인자를 가지며, 일부는 기본 인자까지 가지고 있습니다. 이 경우 해당 옵션의 인자는 붙여서 표기해야 합니다: -Oarg처럼 말이며, 결코 -O arg처럼 쓰면 안 됩니다. 기본 인자를 가진 옵션의 경우 후자는 “옵션 -O에 대해 기본값을 사용하고 arg를 옵션 파서로 더 전달하라”는 의미이기 때문입니다. 예를 들어, git grep은 발견된 파일들의 이름 목록을 프로그램에 전달하는 -O 옵션을 가지고 있습니다. -O의 기본 프로그램은 페이저(보통 less)이지만, 여러분의 편집기를 사용할 수도 있습니다:
$ git grep -Ovim # but not -O vim
그런데, git이 페이저로 less를 사용하도록 지시받은 경우(즉, git에 페이저가 전혀 설정되지 않아 기본값으로 less를 사용하는 경우, 또는 GIT_PAGER나 PAGER 환경 변수에서 less를 가져오는 경우, 또는 git config [--global] core.pager less로 설정된 경우, 또는 git grep -Oless 명령에서 less가 사용된 경우) git grep은 less에 +/$pattern 옵션을 전달하는데, 이는 매우 편리합니다. 안타깝게도, git grep은 페이저가 정확히 less가 아니면 패턴을 전달하지 않는데, 매개변수가 붙은 less인 경우에도 마찬가지입니다(예를 들어 git config [--global] core.pager less -FRSXgimq와 같은 경우). 다행히 git grep -Oless는 항상 패턴을 전달합니다.
bash/zsh 자동완성
git rebase --interactive --preserve-merges HEAD~5를 수동으로 입력하는 것은 명령줄 사용을 즐기는 사람들에게조차 다소 힘든 일이며, 바로 이 지점에서 셸 자동완성이 큰 도움이 됩니다. Bash/zsh에는 프로그래밍 가능한 자동완성 기능이 함께 제공되며, 대개 자동으로 설치되고 활성화되어 있으므로, bash/zsh와 git이 설치되어 있다면 이미 준비가 되어 있을 가능성이 높습니다 - 그냥 명령줄에서 사용해 보십시오.
필요한 구성 요소가 설치되어 있지 않다면, bash_completion 패키지를 설치하고 활성화하십시오. git 완성 기능을 최신 버전으로 업그레이드하고 싶다면 git contrib에서 필요한 파일을 다운로드하십시오.
Git-for-windows에는 bash 완성 기능이 설치되고 활성화된 git-bash가 포함되어 있습니다.
bash/zsh 프롬프트
명령줄을 애용하는 사람들에게 셸 프롬프트는 많은 유용한 정보를 담을 수 있습니다. 프롬프트에 git 정보를 포함하려면 git-prompt.sh를 사용하십시오. 파일에 있는 자세한 안내를 읽으십시오.
다른 프롬프트 변형을 찾으려면 인터넷에서 “git prompt”를 검색하십시오.
SSH 연결 공유
SSH 연결 공유는 OpenSSH 및 PuTTY 같은 파생 프로그램의 기능입니다. SSH 연결 공유는 하나의 연결을 맺어두고 같은 서버에 접속하는 이후의 모든 클라이언트가 이를 재사용하게 함으로써 ssh 클라이언트의 시작 시간을 줄이는 방법입니다. SSH 연결 공유는 scp, sftp, rsync, 그리고 물론 ssh를 통한 git처럼 짧은 ssh 세션이 많을 때 속도를 높이는 데 사용할 수 있습니다. ssh로 접근 가능한 원격 저장소에 정기적으로 fetch/pull/push를 수행한다면 ssh 연결 공유를 사용하는 것이 권장됩니다.
ssh 연결 공유를 켜려면 ~/.ssh/config에 다음과 같은 내용을 추가하십시오:
Host *
ControlMaster auto
ControlPath ~/.ssh/mux-%r@%h:%p
ControlPersist 600
더 자세한 정보는 OpenSSH wikibook과 search를 참고하십시오.
SSH 연결 공유는 GitHub, GitLab, SourceForge 저장소에서 사용할 수 있지만, BitBucket은 이를 허용하지 않으며 짧은 비활성 시간이 지나면 주 연결을 강제로 닫는다는 점에 유의하십시오. 그래서 ssh로부터 “Connection to bitbucket.org closed by remote host.”와 같은 오류가 나타날 것입니다.
서버에서의 git
저장소나 저장소 그룹을 공개하는 가장 간단한 방법은 git daemon입니다. 이 데몬은 익명 접근을 제공하며, 기본적으로 읽기 전용입니다. 저장소는 git 프로토콜(git:// URL)을 통해 접근할 수 있습니다. 쓰기 접근을 활성화할 수는 있지만 이 프로토콜에는 어떠한 인증 수단도 없으므로, 신뢰할 수 있는 LAN 내에서만 활성화해야 합니다. 자세한 내용은 git help daemon을 참고하십시오.
SSH를 통한 Git은 저장소를 사용자 또는 그룹 단위로 쓰기 가능하게 만들 수 있으므로(git help config의 core.sharedRepository 매개변수 참고) 인증과 저장소 수준의 권한 부여를 제공합니다. 이것이 어떤 프로젝트의 필요에 비해 너무 개방적이거나 너무 제한적이라면, 매우 세밀하게 접근을 구성할 수 있는 래퍼 gitolite가 있습니다. gitolite는 Perl로 작성되었으며 방대한 문서를 갖추고 있습니다.
저장소를 둘러볼 수 있는 웹 인터페이스는 gitweb나 cgit을 사용해 만들 수 있습니다. 둘 다 CGI 스크립트입니다(Perl과 C로 작성됨). 웹 인터페이스 외에도 두 도구 모두 git을 위한 읽기 전용 덤(dumb) HTTP 접근(http(s):// URL)을 제공합니다. Klaus는 웹 인터페이스와 git 스마트 HTTP 전송을 모두 구현하는 작고 단순한 WSGI 웹 서버로, Python 2와 Python 3을 지원하며 구문 강조를 수행합니다.
사용자, 그룹, 프로젝트를 관리할 수 있는 기능과 비공개, 그룹 접근 가능, 공개 저장소를 포함하는 더 발전된 웹 기반 개발 환경들도 있습니다. 이들은 흔히 이슈 추적기, 위키 페이지, 풀 리퀘스트 및 개발과 소통을 위한 다른 도구들을 포함합니다. 이러한 환경 중에는 Kallithea와 pagure가 있으며, 둘 다 Python으로 작성되었습니다. pagure는 Fedora 개발자들이 작성했으며 일부 Fedora 프로젝트 개발에 사용되고 있습니다. GitPrep은 또 다른 GitHub 클론으로, Perl로 작성되었습니다. Gogs는 Go로 작성되었습니다. GitBucket은 Scala로 작성되었습니다.
마지막으로 빼놓을 수 없는 GitLab입니다. 이는 아마도 git을 위한 가장 발전된 웹 기반 개발 환경일 것입니다. Ruby로 작성되었으며, 커뮤니티 에디션은 무료이자 오픈 소스(MIT 라이선스)입니다.
Mercurial에서 git으로
Mercurial 저장소를 git으로 변환하는 도구는 많습니다. 아마 가장 유명한 것은 hg-git와 fast-export일 것입니다(수 년 전에는 hg2git라는 이름으로 알려져 있었습니다).
하지만 더 나은, 아마도 최고의 도구는 git-remote-hg입니다. git에서 Mercurial 저장소로 투명한 양방향(pull 및 push) 접근을 제공합니다. 작성자는 대체로 객관적으로 보이는 대안 비교를 작성했습니다.
git-remote-hg를 사용하려면 이를 설치하거나 클론한 뒤 PATH에 추가하고(또는 스크립트 git-remote-hg를 이미 PATH에 있는 디렉터리로 복사하고), Mercurial URL 앞에 hg::를 붙이십시오. 예를 들어:
$ git clone https://github.com/felipec/git-remote-hg.git
$ PATH=$PATH:"`pwd`"/git-remote-hg
$ git clone hg::https://hg.python.org/peps/ PEPs
저장소를 다루려면 git fetch/pull/push를 포함한 일반적인 git 명령을 사용하기만 하면 됩니다.
Mercurial 습관을 git으로 바꾸기 시작하려면 Mercurial 위키의 Git 사용자를 위한 Mercurial 페이지를 참고하십시오. 페이지 후반부에는 대응하는 Mercurial 명령과 git 명령을 나열한 표가 있습니다. 양방향 모두 완벽하게 동작할 것입니다.
파이썬 개발자 가이드에도 git과 hg의 몇 가지 차이점을 다루는 git 개발자를 위한 Mercurial 장이 있습니다.
Git과 GitHub
gitsome- Git/GitHub 명령줄 인터페이스(CLI)입니다. Python으로 작성되었으며, MacOS, Unix, Windows에서 동작합니다. 자동완성 기능을 갖춘 Git/GitHub CLI로서, 모든 셸에서 동작하는 다수의 GitHub 통합 명령을 포함하고, 셸 명령과 함께 Python 명령을 실행할 수 있는 Python REPL이 내장된 xonsh를 갖추고 있으며, 명령 기록, 사용자 지정 가능한 강조 표시 기능을 제공하고, 문서화가 충실히 되어 있습니다.
Copyright
This document has been placed in the public domain.