[Withdrawn] PEP 103 - Collecting information about git

원문 링크: PEP 103 - Collecting information about git

상태: Withdrawn 유형: Informational 작성일: 01-Jun-2015

PEP 103 – Git 정보 수집 (철회됨)

작성자: Oleg Broytman 상태: 철회됨 (Withdrawn) 유형: 정보성 (Informational) 생성일: 2015년 6월 1일 이력: 2015년 9월 12일


개요 (Abstract)

이 정보성 PEP는 Git에 대한 정보를 수집합니다. Git에 대한 많은 문서가 이미 존재하므로, 이 PEP는 Python 개발과 더 관련 있는 복잡한 문제, 시나리오 및 예시에 집중합니다.

향후 이 PEP는 Mercurial에서 Git으로 Python 개발을 마이그레이션하는 데 도움이 되도록 Mercurial과 Git 시나리오의 등가성에 대한 정보를 수집하여 확장될 계획이었습니다.

PEP의 저자는 현재 Mercurial에서 Git으로 Python 개발을 마이그레이션하는 Process PEP를 작성할 계획이 없습니다.

중요: 이 PEP는 철회되었습니다. 너무 일반적이며 Python 개발과 직접적인 관련이 적습니다. 더 이상 업데이트되지 않습니다. 내용은 Python Wiki로 옮겨졌으며, 이후 업데이트는 위키에서 이루어집니다.

문서 (Documentation)

Git은 온라인과 오프라인 모두 풍부한 문서를 제공합니다.

초보자를 위한 문서 (Documentation for starters)

  • Git Tutorial: part 1, part 2
  • Git User’s manual
  • Everyday GIT With 20 Commands Or So
  • Git workflows

고급 문서 (Advanced documentation)

  • Git Magic (다양한 번역본 제공)
  • Pro Git (The Book about git): Amazon에서 구매하거나 PDF, mobi, ePub 형식으로 다운로드 가능하며, 다양한 언어로 번역되어 있습니다.
  • Git Wiki
  • Git Buch (독일어)

오프라인 문서 (Offline documentation)

Git은 내장된 도움말 기능을 제공합니다: git help $TOPIC을 실행하여 확인할 수 있습니다. 예를 들어, git help git 또는 git help help를 실행할 수 있습니다.

빠른 시작 (Quick start)

다운로드 및 설치 (Download and installation)

  • Unix 사용자: 패키지 관리자를 사용하여 다운로드 및 설치합니다.
  • Microsoft Windows: git-for-windows를 다운로드합니다.
  • MacOS X: XCode와 함께 설치된 Git을 사용하거나, MacPorts 또는 git-osx-installer에서 다운로드하거나, Homebrew (brew install git)를 통해 설치합니다.
  • git-cola (repository): Python으로 작성된 GPL 라이선스 Git GUI입니다 (Linux, Windows, MacOS X).
  • TortoiseGit: TortoiseSVN 기반의 Windows Shell 인터페이스 Git 클라이언트입니다 (오픈 소스).

초기 설정 (Initial configuration)

Git은 모든 커밋(commit)에 작성자(author) 및 커미터(committer)의 이름/이메일을 저장하므로, 실제 이름과 선호하는 이메일을 설정하는 것이 중요합니다.

$ git config --global user.name "User Name"
$ git config --global user.email user.name@example.org

이 PEP의 예시 (Examples in this PEP)

이 PEP의 Git 명령 예시는 다음과 같은 가정을 따릅니다. 사용자는 python이라는 로컬 저장소(local repository)에서 작업하며, origin이라는 상위(upstream) 원격 저장소(remote repo)를 가지고 있습니다. 로컬 저장소에는 v1master 두 개의 브랜치(branch)가 있습니다. 대부분의 예시에서는 현재 체크아웃(checkout)된 브랜치가 master입니다. 즉, 다음과 같은 작업을 수행했다고 가정합니다.

$ git clone https://git.python.org/python.git
$ cd python
$ git branch v1 origin/v1

첫 번째 명령은 원격 저장소를 python이라는 로컬 디렉토리로 클론(clone)하고, 새로운 로컬 브랜치 master를 생성하며, remotes/origin/master를 해당 브랜치의 상위 원격 추적 브랜치(upstream remote-tracking branch)로 설정하고 작업 디렉토리로 체크아웃합니다.

마지막 명령은 새로운 로컬 브랜치 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를 해당 브랜치의 상위 원격 추적 브랜치로 설정하며 작업 디렉토리로 체크아웃합니다.

브랜치와 브랜치 (Branches and branches)

Git 용어는 혼란스러울 수 있습니다. 예를 들어, “branch”라는 용어는 Git에서 두 가지 의미를 가집니다. 하나는 커밋(commit)들의 지향성 있는 선(Directed line of commits, 병합(merge)을 포함할 수 있음)이고, 다른 하나는 커밋들의 선에 할당된 레이블(label) 또는 포인터(pointer)입니다. 커밋 자체에 대해 이야기하는지, 아니면 커밋의 레이블에 대해 이야기하는지 구별하는 것이 중요합니다. 커밋들의 선은 그 자체로 이름이 없으며 일반적으로 길어지고 병합될 뿐입니다. 반면, 레이블은 자유롭게 생성, 이동, 이름 변경 및 삭제될 수 있습니다.

원격 저장소 및 원격 브랜치 (Remote repositories and remote branches)

원격 추적 브랜치(remote-tracking branches)는 로컬 저장소에 있는 브랜치(커밋에 대한 포인터)입니다. 이는 Git(및 사용자)이 어떤 브랜치와 커밋이 어떤 원격 저장소에서 풀(pull)되거나 푸시(push)되었는지 기억하기 위한 것입니다 (여러 원격에서 풀하거나 푸시할 수 있습니다). 원격 추적 브랜치는 remotes/$REMOTE 네임스페이스 아래에 존재합니다 (예: remotes/origin/master).

원격 추적 브랜치의 상태를 보려면 다음을 실행합니다.

$ git branch -rv

로컬 및 원격 추적 브랜치(및 태그)가 가리키는 커밋을 보려면 다음을 실행합니다.

$ git log --decorate

원격 추적 브랜치에서는 직접 개발하지 않습니다. 원격 브랜치를 상위로 하는 로컬 브랜치를 생성하고 해당 로컬 브랜치에서 개발을 수행합니다. 푸시 시 Git은 커밋을 원격 저장소로 푸시하고 원격 추적 브랜치를 업데이트하며, 풀 시 Git은 원격 저장소에서 커밋을 페치(fetch)하고 원격 추적 브랜치를 업데이트하며 로컬 브랜치를 Fast-forward, 병합 또는 리베이스(rebase)합니다.

다음과 같이 초기 클론을 수행할 때:

$ git clone -b v1 https://git.python.org/python.git

Git은 원격 저장소 https://git.python.org/python.gitpython 디렉토리로 클론하고, origin이라는 원격을 생성하며, 원격 추적 브랜치를 생성하고, 로컬 브랜치 v1을 생성하며, 이를 상위 remotes/origin/v1 브랜치를 추적하도록 구성하고 v1을 작업 디렉토리로 체크아웃합니다.

git status --branchgit branch --verbose와 같은 일부 명령은 로컬 브랜치와 원격 브랜치 간의 차이를 보고합니다. 이들은 로컬 저장소의 원격 추적 브랜치와만 비교하며, 해당 원격 추적 브랜치의 상태는 오래되었을 수 있음을 기억하십시오. 원격 추적 브랜치를 업데이트하려면 원격 저장소에서 커밋을 페치하고 병합(또는 리베이스)하거나, 로컬 브랜치를 업데이트하지 않고 원격 추적 브랜치만 업데이트해야 합니다.

로컬 및 원격 추적 브랜치 업데이트 (Updating local and remote-tracking branches)

로컬 브랜치를 업데이트하지 않고 원격 추적 브랜치를 업데이트하려면 git remote update [$REMOTE...]를 실행합니다. 예를 들어:

$ git remote update
$ git remote update origin

Fetch 및 Pull (Fetch and pull)

다음 두 명령 사이에는 큰 차이가 있습니다.

$ git fetch $REMOTE $BRANCH
$ git fetch $REMOTE $BRANCH:$BRANCH

첫 번째 명령은 $REMOTE 저장소의 $BRANCH에서 로컬 저장소에 없는 커밋을 페치하고, 원격 추적 브랜치를 업데이트하며, 헤드(head) 커밋의 ID(해시)를 .git/FETCH_HEAD 파일에 남깁니다.

두 번째 명령은 $REMOTE 저장소의 $BRANCH에서 로컬 저장소에 없는 커밋을 페치하고, 로컬 브랜치 $BRANCH와 해당 상위 원격 추적 브랜치를 모두 업데이트합니다. 그러나 Fast-forward가 아닌 경우에는 브랜치 업데이트를 거부합니다. 또한 현재 브랜치(현재 체크아웃된 브랜치, HEAD가 가리키는 곳) 업데이트도 거부합니다.

첫 번째 명령은 git pull에 의해 내부적으로 사용됩니다.

$ git pull $REMOTE $BRANCH는 다음 명령과 동일합니다.

$ git fetch $REMOTE $BRANCH
$ git merge FETCH_HEAD

이 경우 $BRANCH는 현재 브랜치여야 합니다. 다른 브랜치를 현재 브랜치로 병합하려면 먼저 해당 비현재 브랜치를 업데이트한 다음 병합합니다.

$ git fetch origin v1:v1 # v1 업데이트
$ git pull --rebase origin master # 병합 대신 리베이스를 사용하여 현재 master 브랜치 업데이트
$ git merge v1

아직 v1에 커밋을 푸시하지 않았다면, 시나리오는 좀 더 복잡해집니다. Git은 Fast-forward 불가능한 브랜치 업데이트를 거부하며, 강제 풀(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

푸시는 약간 더 간단합니다. push 명령은 하나뿐입니다. 다음을 실행할 때:

$ git push origin v1 master

Git은 로컬 v1을 원격 v1으로, 로컬 master를 원격 master로 푸시합니다. 다음 명령과 동일합니다.

$ git push origin v1:v1 master:master

Git은 커밋을 원격 저장소로 푸시하고 원격 추적 브랜치를 업데이트합니다. Git은 Fast-forward 불가능한 커밋 푸시를 거부합니다. 어쨌든 강제 푸시를 할 수 있지만, 자신의 저장소에만 강제 푸시해야 하며, 공개 또는 공유 저장소에는 강제 푸시하지 마십시오. Git이 Fast-forward 불가능한 커밋 푸시를 거부하는 경우, 원격 저장소에서 커밋을 페치하고 병합(또는 페치된 커밋 위에 자신의 커밋을 리베이스)한 다음 푸시하는 것이 좋습니다. 무엇을 왜 하는지 아는 경우에만 강제 푸시하십시오. 아래 “Commit editing and caveats” 섹션을 참조하십시오.

Git을 설정하여 몇 개의 브랜치 또는 모든 브랜치를 한 번에 푸시하도록 할 수 있으므로, 간단히 다음을 실행할 수 있습니다.

$ git push origin

또는 심지어

$ git push

푸시를 위한 기본 원격 저장소는 origin입니다. Git 2.0 이전 버전에서 푸시할 기본 참조 세트는 매칭 알고리즘을 사용하여 계산됩니다. Git은 양쪽 끝에 동일한 이름을 가진 모든 브랜치를 푸시합니다. Git 2.0 이상 버전에서 푸시할 기본 참조 세트는 간단한 알고리즘을 사용하여 계산됩니다. Git은 현재 브랜치를 @ {upstream}으로 다시 푸시합니다.

Git 2.0 이전 버전에서 새로운 동작으로 설정하려면 다음을 실행합니다.

$ git config push.default simple

Git 2.0 이상 버전에서 이전 동작으로 설정하려면 다음을 실행합니다.

$ git config push.default matching

Git은 원격 non-bare 저장소에서 현재 브랜치인 경우 브랜치 푸시를 허용하지 않습니다. Git은 원격 작업 디렉토리 업데이트를 거부합니다. bare 저장소에만 푸시해야 합니다. non-bare 저장소의 경우 Git은 풀(pull) 기반 워크플로우를 선호합니다.

원격 호스트에 코드를 배포하고 푸시만 사용할 수 있는 경우(워크스테이션이 방화벽 뒤에 있어 풀할 수 없기 때문에), 두 개의 저장소를 사용하여 두 단계로 수행합니다. 워크스테이션에서 원격 호스트의 bare 저장소로 푸시하고, 원격 호스트에 SSH로 연결한 다음 bare 저장소에서 non-bare 배포 저장소로 풀합니다.

이는 Git 2.3에서 변경되었지만, 주의 사항은 블로그 게시물을 참조하십시오. 2.4에서는 push-to-deploy 기능이 더욱 개선되었습니다.

태그 (Tags)

Git은 페치/풀 중에 페치되는 커밋을 가리키는 태그를 자동으로 페치합니다. 모든 태그(및 태그가 가리키는 커밋)를 페치하려면 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로 태그를 제거하지 마십시오.

개인 정보 (Private information)

클론/페치/풀/푸시 시 Git은 데이터베이스 객체(커밋, 트리, 파일 및 태그)와 심볼릭 참조(브랜치 및 경량 태그)만 복사합니다. 그 외의 모든 것은 저장소에 비공개이며 절대 클론, 업데이트 또는 푸시되지 않습니다. 이는 사용자의 설정, 후크(hooks), 비공개 제외 파일 등입니다.

후크를 배포하려면 작업 트리(working tree)로 복사하고, 추가(add), 커밋(commit), 푸시(push)한 다음 팀원들에게 수동으로 후크를 업데이트하고 설치하도록 지시하십시오.

커밋 편집 및 주의사항 (Commit editing and caveats)

게시된(푸시된) 커밋을 편집하지 말라는 경고는 문서에도 나와 있지만, 매우 중요하므로 여기에 다시 반복합니다.

강제 푸시(forced push)에서 복구하는 것은 가능하지만, 팀 전체에게 매우 번거로운 일입니다. 피해주십시오.

아직 게시되지 않은 커밋을 보려면 브랜치의 헤드와 해당 상위 원격 추적 브랜치를 비교합니다.

$ git log origin/master.. # origin/master부터 HEAD(master의)까지
$ git log origin/v1..v1 # origin/v1부터 v1의 헤드까지

상위 원격 추적 브랜치가 있는 모든 브랜치에 대해 Git은 @ {upstream}(줄여서 @ {u})이라는 별칭(alias)을 유지하므로, 위 명령은 다음과 같이 주어질 수 있습니다.

$ git log @{u}..
$ git log v1@{u}..v1

모든 브랜치의 상태를 보려면:

$ git branch -avv

로컬 브랜치의 상태를 원격 저장소와 비교하려면:

$ git remote show origin

상위 리베이스에서 복구하는 방법은 git help rebase를 참조하십시오.

한편, 커밋 편집에 대해 너무 두려워하지 마십시오. 아직 푸시되지 않은 커밋은 안전하게 편집, 재정렬, 제거, 결합 및 분할할 수 있습니다. 자신의 (백업) 저장소에 커밋을 푸시한 다음 나중에 편집하고 편집된 커밋을 강제 푸시하여 이미 푸시된 것을 대체할 수도 있습니다. 커밋이 공개 또는 공유 저장소에 있지 않는 한 문제가 되지 않습니다.

되돌리기 (Undo)

무슨 일을 하든 당황하지 마십시오. Git의 거의 모든 것은 되돌릴 수 있습니다.

git checkout: 파일 내용 복원 (git checkout: restore file's content)

예를 들어, git checkout은 파일의 내용을 특정 커밋의 내용으로 복원하는 데 사용될 수 있습니다. 다음과 같이:

git checkout HEAD~ README

이 명령은 현재 브랜치에서 마지막에서 두 번째 커밋의 README 파일 내용을 복원합니다. 기본적으로 커밋 ID는 단순히 HEAD입니다. 즉, git checkout READMEREADME를 최신 커밋으로 복원합니다.

(커밋에 있는 파일의 내용을 보려면 git checkout을 사용하지 마십시오. git cat-file -p를 사용하십시오. 예를 들어 git cat-file -p HEAD~:path/to/README.)

git reset: (푸시되지 않은) 커밋 제거 (git reset: remove (non-pushed) commits)

git reset은 현재 브랜치의 헤드를 이동합니다. 헤드는 어떤 커밋이든 가리키도록 이동될 수 있지만, 주로 브랜치 상단에서 하나 또는 몇 개의 (가능하면 푸시되지 않은) 커밋을 제거하는 데 사용됩니다. 즉, 몇 개의 (푸시되지 않은) 커밋을 되돌리기 위해 브랜치를 뒤로 이동시키는 것입니다.

git reset에는 soft, hard, mixed 세 가지 작동 모드가 있습니다. 기본값은 mixed입니다. ProGit은 차이점을 매우 명확하게 설명합니다. bare 저장소에는 인덱스나 작업 트리가 없으므로 bare 저장소에서는 soft 리셋만 가능합니다.

스테이징 해제 (Unstaging)

경로 또는 경로가 있는 mixed 모드 리셋은 변경 사항을 스테이징 해제(unstage)하는 데 사용될 수 있습니다. 즉, 커밋을 위해 git add로 추가된 변경 사항을 인덱스에서 제거하는 것입니다. 스테이징 해제 및 기타 되돌리기 트릭에 대한 자세한 내용은 The Book을 참조하십시오.

git reflog: 참조 로그 (git reflog: reference log)

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는 (일반적으로) 현재 브랜치를 나타내는 심볼릭 참조입니다. HEADgit checkout $BRANCH로 변경됩니다.

기본적으로 git reflogHEAD의 이동을 보여줍니다. 즉, 이 명령은 git reflog HEAD와 동일합니다. 브랜치 헤드의 이동을 보려면 git reflog $BRANCH 명령을 사용합니다.

따라서 git reset을 되돌리려면 git reflog에서 원래 커밋을 찾아 git show 또는 git log로 확인하고 git reset $COMMIT_ID를 실행합니다. Git은 브랜치 헤드의 이동을 reflog에 저장하므로, 나중에 그 되돌리기를 다시 되돌릴 수도 있습니다.

더 복잡한 상황에서는 브랜치의 헤드를 리셋하는 것과 함께 일부 커밋을 이동하고 싶을 것입니다. 이를 새 브랜치로 체리픽(cherry-pick)하십시오. 예를 들어, master 브랜치를 원래 커밋으로 되돌리되 현재 브랜치에서 생성된 두 개의 커밋을 보존하려면 다음과 같이 수행합니다.

$ git branch save-master # master를 저장하는 새 브랜치 생성
$ git reflog # master의 원래 위치 찾기
$ git reset $COMMIT_ID
$ git cherry-pick save-master~ save-master
$ git branch -D save-master # 임시 브랜치 제거

git revert: 커밋 되돌리기 (git revert: revert a commit)

git revert는 하나 또는 여러 커밋을 되돌립니다. 즉, 주어진 커밋의 효과를 되돌리는 새로운 커밋을 생성합니다. 이는 게시된 커밋을 되돌리는 유일한 방법입니다 (git commit --amend, git rebase, git reset은 브랜치를 Fast-forward 불가능한 방식으로 변경하므로 푸시되지 않은 커밋에만 사용해야 합니다).

병합 커밋(merge commit)을 되돌리는 데는 문제가 있습니다. git revert는 병합 커밋으로 인해 생성된 코드를 되돌릴 수 있지만, 병합 사실 자체를 되돌릴 수는 없습니다. “How to revert a faulty merge” 토론을 참조하십시오.

되돌릴 수 없는 한 가지 (One thing that cannot be undone)

무엇을 되돌리든, 되돌릴 수 없는 한 가지가 있습니다. 덮어씌워진 커밋되지 않은 변경 사항입니다. 커밋되지 않은 변경 사항은 Git에 속하지 않으므로 Git은 이를 보존하는 데 도움이 될 수 없습니다.

대부분의 경우 Git은 커밋되지 않은 변경 사항을 덮어씌울 명령을 실행할 때 경고를 표시합니다. Git은 git checkout으로 브랜치를 전환하는 것을 허용하지 않습니다. 작업 트리가 깨끗하지 않은 상태에서 리베이스하려고 할 때 Git은 중단시킵니다. 커밋되지 않은 파일 위에 새 커밋을 풀하는 것을 거부합니다.

그러나 정확히 파일 덮어쓰기를 수행하는 명령이 있습니다. git checkout $PATHs 또는 git reset --hard와 같은 명령은 커밋되지 않은 변경 사항을 포함하여 파일을 자동으로 덮어씌웁니다.

이를 염두에 두고 “일찍 커밋하고 자주 커밋하라(commit early, commit often)”는 입장을 이해할 수 있습니다. 가능한 한 자주 커밋하십시오. 편집기 또는 IDE에서 저장할 때마다 커밋하십시오. 푸시하기 전에 커밋을 편집할 수 있습니다. 커밋 메시지를 편집하고, 커밋을 변경하고, 재정렬하고, 결합하고, 분할하고, 제거할 수 있습니다. 그러나 변경 사항을 Git 데이터베이스에 저장하십시오. 변경 사항을 커밋하거나 최소한 git stash로 스태시(stash)하십시오.

병합 또는 리베이스? (Merge or rebase?)

인터넷은 “병합 또는 리베이스?”라는 주제에 대한 열띤 토론으로 가득합니다. 대부분은 의미가 없습니다. 많은 브랜치가 있는 크고 복잡한 프로젝트에서 대규모 팀이 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 mastergit pull --rebase origin master와 동일해집니다.

메인라인 브랜치를 업데이트하기 위해 리베이스를 사용하는 동안 별도의 기능 또는 토픽 브랜치에서 새 커밋을 생성하는 것이 좋습니다. 토픽 브랜치가 준비되면 메인라인으로 병합합니다. 한 번에 많은 수의 충돌을 해결하는 번거로운 작업을 피하려면 토픽 브랜치를 때때로 메인라인으로 병합한 다음 토픽 브랜치로 다시 전환하여 작업을 계속할 수 있습니다. 전체 워크플로우는 다음과 같습니다.

$ git checkout -b issue-42 # 새 이슈 브랜치를 생성하고 전환
...edit/test/commit...
$ git checkout master
$ git pull --rebase origin master # 상위에서 master 업데이트
$ git merge issue-42
$ git branch -d issue-42 # 토픽 브랜치 삭제
$ git push origin master

토픽 브랜치가 삭제되면 레이블만 제거되고 커밋은 데이터베이스에 남아 있습니다. 이제 커밋은 master에 병합되었습니다.

o--o--o--o--o--M--< master - 메인라인 브랜치
 \ /
  --*--*--* - 토픽 브랜치, 이제 이름 없음

토픽 브랜치는 작은 토픽 브랜치로 인한 브랜치 네임스페이스의 혼란을 피하기 위해 삭제됩니다. 어떤 문제가 해결되었는지 또는 어떤 기능이 구현되었는지에 대한 정보는 커밋 메시지에 있어야 합니다.

그러나 장기 병합된 브랜치의 경우 이처럼 적은 양의 리베이스도 너무 클 수 있습니다. v1master 브랜치에서 모두 작업하고 v1master로 정기적으로 병합한다고 가정해 보십시오. 얼마 후 master에 많은 병합 및 비병합 커밋이 있을 것입니다. 그런 다음 완료된 작업을 공유 저장소에 푸시하려고 할 때 누군가가 v1에 몇 개의 커밋을 푸시한 것을 발견합니다. 이제 두 가지 똑같이 나쁜 대안 중에서 선택해야 합니다. v1을 페치하고 리베이스한 다음 master에서 모든 작업을 다시 생성해야 하거나(master를 원본으로 리셋하고 v1을 병합한 다음 이전 master에서 모든 비병합 커밋을 체리픽), 새 v1을 병합하고 선형 히스토리의 아름다움을 잃게 됩니다.

Null-병합 (Null-merges)

Git에는 Python 핵심 개발자들이 “null-merge”라고 부르는 것에 대한 내장 병합 전략이 있습니다.

$ git merge -s ours v1 # master에 v1 null-병합

브랜칭 모델 (Branching models)

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 확장을 구현했습니다.

고급 구성 (Advanced configuration)

줄 끝 (Line endings)

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

유용한 자산 (Useful assets)

  • GitAlias (repository): 별칭(aliases)의 방대한 컬렉션입니다. 자주 사용되는 명령에 대한 신중한 별칭 선택은 많은 키 입력 시간을 절약할 수 있습니다!
  • GitIgnorehttps://github.com/github/gitignore: 모든 종류의 IDE 및 프로그래밍 언어에 대한 .gitignore 파일 컬렉션입니다. Python도 포함되어 있습니다!
  • pre-commit (repositories): 다국어 pre-commit 후크를 관리하고 유지 관리하기 위한 프레임워크입니다. 이 프레임워크는 Python으로 작성되었으며 많은 프로그래밍 언어에 대한 많은 플러그인을 제공합니다.

고급 주제 (Advanced topics)

스테이징 영역 (Staging area)

스테이징 영역(Staging area), 일명 인덱스(index), 일명 캐시(cache)는 Git의 특징적인 기능입니다. 스테이징 영역은 Git이 커밋하기 전에 패치를 수집하는 곳입니다. 패치 수집 단계와 커밋 단계의 분리는 Git의 매우 유용한 기능을 제공합니다. 커밋하기 전에 수집된 패치를 검토하고 편집할 수도 있습니다. 일부 덩어리(hunks)를 제거하고, 새로운 덩어리를 추가하고, 다시 검토할 수 있습니다.

파일을 인덱스에 추가하려면 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”를 참조하십시오.

Root

Git은 어떤 명령을 실행하기 전에 루트( .git 서브디렉토리가 존재하는 프로젝트의 최상위 디렉토리)로 전환합니다. 그러나 Git은 전환하기 전에 현재 디렉토리가 무엇이었는지 기억합니다. 일부 프로그램은 현재 디렉토리를 고려합니다. 예를 들어, git status는 현재 디렉토리를 기준으로 변경 및 알 수 없는 파일의 파일 경로를 보여줍니다. git grep은 현재 디렉토리 아래를 검색합니다. git apply는 현재 디렉토리 아래의 파일을 건드리는 패치에서 해당 덩어리만 적용합니다.

그러나 대부분의 명령은 루트에서 실행되며 현재 디렉토리를 무시합니다. 예를 들어, v1 브랜치용 작업 트리와 master 브랜치용 작업 트리 두 개가 있다고 가정해 보십시오. 두 번째 작업 트리의 하위 디렉토리에서 v1을 병합하려면 최상위 디렉토리에 있는 것처럼 명령을 작성해야 합니다. 예를 들어, project-v1project 두 개의 작업 트리를 사용해 봅시다.

$ 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” 섹션을 참조하십시오.

데이터베이스 유지 관리 (Database maintenance)

Git 객체 데이터베이스 및 .git 아래의 기타 파일/디렉토리는 주기적인 유지 관리 및 정리가 필요합니다. 예를 들어, 커밋 편집은 참조되지 않은 객체(Git 용어로는 dangling objects)를 남기며, 이러한 객체는 DB에 불필요한 데이터를 쌓는 것을 방지하기 위해 제거되어야 합니다. git gc 명령은 유지 관리에 사용됩니다. Git은 일부 명령의 일부로 git gc --auto를 자동으로 실행하여 빠른 유지 관리를 수행합니다. 사용자에게는 git gc --aggressive를 가끔 실행하는 것이 권장됩니다. git help gc는 몇백 개의 변경 사항마다 실행하는 것을 권장하며, 더 집중적인 프로젝트의 경우 일주일에 한 번 정도, 활동이 적은 프로젝트의 경우 더 드물게(2주에 한 번 또는 한 달에 한 번) 실행해야 합니다.

git gc --aggressive는 dangling 객체를 제거할 뿐만 아니라, 객체 데이터베이스를 인덱싱되고 더 최적화된 팩(pack)으로 재패킹합니다. 또한 심볼릭 참조(브랜치 및 태그)도 패킹합니다. 이를 수행하는 또 다른 방법은 git repack을 실행하는 것입니다.

Linus Torvalds의 git gc --aggressive의 “어리석음”에 관한 잘 알려진 메시지가 있습니다. 이 메시지는 이제 안전하게 무시할 수 있습니다. 오래되고 시대에 뒤떨어진 메시지이며, git gc --aggressive는 그 이후로 훨씬 나아졌습니다.

여전히 git gc --aggressive보다 git repack을 선호하는 사람들을 위한 권장 매개변수는 git repack -a -d -f --depth=20 --window=250입니다. 이 매개변수의 효과에 대한 설명은 이 자세한 실험을 참조하십시오.

때때로 git fsck [--strict]를 실행하여 데이터베이스의 무결성을 확인합니다. git fsck는 dangling 객체 목록을 생성할 수 있습니다. 이는 오류가 아니라 정기적인 유지 관리를 수행해야 한다는 알림입니다.

팁과 트릭 (Tips and tricks)

명령줄 옵션 및 인수 (Command-line options and arguments)

git help cli는 짧은 옵션/플래그를 결합하지 말 것을 권장합니다. 대부분의 경우 결합이 작동합니다. git commit -av는 완벽하게 작동하지만, 작동하지 않는 상황도 있습니다. 예를 들어, git log -p -5git log -p5로 결합할 수 없습니다.

일부 옵션에는 인수가 있으며, 일부는 기본 인수도 있습니다. 이 경우 해당 옵션의 인수는 ` -Oarg와 같이 붙여서 입력해야 합니다. -O arg는 안 됩니다. 왜냐하면 기본 인수가 있는 옵션의 경우 후자는 "-O 옵션에 기본값을 사용하고 arg를 옵션 파서에 추가로 전달"한다는 의미이기 때문입니다. 예를 들어, git grep에는 찾은 파일 이름 목록을 프로그램에 전달하는 -O 옵션이 있습니다. -O의 기본 프로그램은 페이저(일반적으로 less`)이지만, 편집기를 사용할 수 있습니다.

$ git grep -Ovim # 하지만 -O vim은 안 됩니다

덧붙여, Git이 less를 페이저로 사용하도록 지시받은 경우(pager가 Git에 전혀 구성되지 않은 경우 기본적으로 less를 사용하거나, GIT_PAGER 또는 PAGER 환경 변수에서 less를 얻거나, git config [--global] core.pager less로 구성되었거나, git grep -Oless 명령에서 less가 사용된 경우) git grep+/$pattern 옵션을 less에 전달하는데, 이는 매우 편리합니다. 불행히도, 페이저가 정확히 less가 아닌 경우(less에 매개변수가 있는 경우에도, 예를 들어 git config [--global] core.pager less -FRSXgimq) git grep은 패턴을 전달하지 않습니다. 다행히도 git grep -Oless는 항상 패턴을 전달합니다.

Bash/Zsh 완성 (bash/zsh completion)

git rebase --interactive --preserve-merges HEAD~5를 수동으로 입력하는 것은 명령줄 사용을 즐기는 사람들에게도 다소 어렵습니다. 이때 쉘 완성(shell completion)이 큰 도움이 됩니다. Bash/Zsh는 프로그래밍 가능한 완성 기능을 제공하며, 종종 자동으로 설치 및 활성화되므로 Bash/Zsh 및 Git이 설치되어 있다면 이미 완료되었을 가능성이 높습니다. 명령줄에서 사용하기만 하면 됩니다.

필요한 요소가 설치되어 있지 않은 경우 bash_completion 패키지를 설치하고 활성화하십시오. Git 완성 기능을 최신 버전으로 업그레이드하려면 Git contrib에서 필요한 파일을 다운로드하십시오.

Git-for-windows에는 Bash 완성이 설치 및 활성화된 git-bash가 함께 제공됩니다.

Bash/Zsh 프롬프트 (bash/zsh prompt)

명령줄 애호가를 위해 쉘 프롬프트는 많은 유용한 정보를 담을 수 있습니다. 프롬프트에 Git 정보를 포함하려면 git-prompt.sh를 사용하십시오. 파일의 자세한 지침을 읽으십시오.

다른 프롬프트 변형을 찾으려면 인터넷에서 “git prompt”를 검색하십시오.

SSH 연결 공유 (SSH connection sharing)

SSH 연결 공유는 OpenSSH 및 PuTTY와 같은 파생 제품의 기능입니다. SSH 연결 공유는 하나의 연결을 설정하고 동일한 서버에 연결하는 모든 후속 클라이언트에 대해 재사용함으로써 SSH 클라이언트 시작 시간을 줄이는 방법입니다. SSH 연결 공유는 scp, sftp, rsync 및 물론 SSH를 통한 Git과 같은 많은 짧은 SSH 세션 속도를 높이는 데 사용될 수 있습니다. SSH를 통해 액세스할 수 있는 원격 저장소에서 정기적으로 페치/풀/푸시하는 경우 SSH 연결 공유를 사용하는 것이 좋습니다.

SSH 연결 공유를 켜려면 ~/.ssh/config에 다음과 같은 내용을 추가합니다.

Host *
  ControlMaster auto
  ControlPath ~/.ssh/mux-%r@%h:%p
  ControlPersist 600

OpenSSH wikibook을 참조하고 더 많은 정보를 검색하십시오.

SSH 연결 공유는 GitHub, GitLab 및 SourceForge 저장소에서 사용할 수 있지만, BitBucket은 허용하지 않으며 짧은 비활성 기간 후에 마스터 연결을 강제로 닫으므로 SSH에서 다음과 같은 오류가 표시될 수 있습니다. “Connection to bitbucket.org closed by remote host.”

서버의 Git (git on server)

하나 또는 여러 저장소를 게시하는 가장 간단한 방법은 git daemon입니다. 데몬은 익명 액세스를 제공하며, 기본적으로 읽기 전용입니다. 저장소는 Git 프로토콜(git:// URL)로 액세스할 수 있습니다. 쓰기 액세스를 활성화할 수 있지만, 이 프로토콜은 인증 수단이 없으므로 신뢰할 수 있는 LAN 내에서만 활성화해야 합니다. 자세한 내용은 git help daemon을 참조하십시오.

SSH를 통한 Git은 인증 및 저장소 수준 권한 부여를 제공합니다. 저장소는 사용자 또는 그룹 쓰기 가능으로 만들 수 있습니다(git help config에서 core.sharedRepository 매개변수 참조). 일부 프로젝트 요구 사항에 너무 허용적이거나 너무 제한적인 경우, 세분화된 액세스를 허용하도록 구성할 수 있는 래퍼(wrapper) gitolite가 있습니다. gitolite는 Perl로 작성되었으며 많은 문서를 가지고 있습니다.

저장소를 탐색하는 웹 인터페이스는 gitweb 또는 cgit을 사용하여 만들 수 있습니다. 둘 다 CGI 스크립트(Perl 및 C로 작성됨)입니다. 웹 인터페이스 외에도 둘 다 Git에 대한 읽기 전용 dumb HTTP 액세스(http(s):// URL)를 제공합니다. Klaus는 웹 인터페이스와 Git smart HTTP 전송을 모두 구현하는 작고 간단한 WSGI 웹 서버입니다. Python 2 및 Python 3을 지원하며 구문 강조를 수행합니다.

사용자, 그룹 및 프로젝트를 관리하는 기능, 비공개, 그룹 액세스 가능 및 공개 저장소, 이슈 트래커, 위키 페이지, 풀 리퀘스트 및 개발 및 통신을 위한 기타 도구를 포함하는 더 고급 웹 기반 개발 환경도 있습니다. 이러한 환경 중에는 Kallithea 및 pagure가 있으며, 둘 다 Python으로 작성되었습니다. pagure는 Fedora 개발자에 의해 작성되었으며 일부 Fedora 프로젝트 개발에 사용되고 있습니다. GitPrep은 Perl로 작성된 또 다른 GitHub 클론입니다. Gogs는 Go로 작성되었습니다. GitBucket은 Scala로 작성되었습니다.

마지막으로, GitLab. Git을 위한 가장 고급 웹 기반 개발 환경일 것입니다. Ruby로 작성되었으며, 커뮤니티 에디션은 무료 오픈 소스(MIT 라이선스)입니다.

Mercurial에서 Git으로 (From Mercurial to git)

Mercurial 저장소를 Git으로 변환하는 많은 도구가 있습니다. 가장 유명한 것은 아마도 hg-gitfast-export일 것입니다(몇 년 전에는 hg2git이라는 이름으로 알려졌습니다).

그러나 더 나은 도구, 아마도 최고는 git-remote-hg입니다. 이는 Git에서 Mercurial 저장소로 투명한 양방향(풀 및 푸시) 액세스를 제공합니다. 그 저자는 대부분 객관적인 대안 비교를 작성했습니다.

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 Wiki의 “Mercurial for Git users” 페이지를 참조하십시오. 페이지의 두 번째 절반에는 해당 Mercurial 및 Git 명령을 나열하는 표가 있습니다. 양방향으로 완벽하게 작동해야 합니다.

Python 개발자 가이드에도 Git과 Hg의 몇 가지 차이점을 문서화하는 “Mercurial for git developers” 장이 있습니다.

Git 및 GitHub (Git and GitHub)

gitsome: Git/GitHub 명령줄 인터페이스(CLI)입니다. Python으로 작성되었으며 MacOS, Unix, Windows에서 작동합니다. 자동 완성 기능을 갖춘 Git/GitHub CLI로, 모든 쉘에서 작동하는 많은 GitHub 통합 명령, 쉘 명령과 함께 Python 명령을 실행할 수 있는 내장 Xonsh (Python REPL), 명령 히스토리, 사용자 정의 가능한 하이라이팅, 철저한 문서화를 포함합니다.

이 문서는 퍼블릭 도메인에 공개되었습니다. 원본: https://github.com/python/peps/blob/main/peps/pep-0103.rst 최종 수정: 2024년 4월 14일 20:08:31 GMT

⚠️ 알림: 이 문서는 AI를 활용하여 번역되었으며, 기술적 정확성을 보장하지 않습니다. 정확한 내용은 반드시 원문을 확인하시기 바랍니다.

Comments