‘git pull’은 어떤 경우에 해로울 수 있습니까? 동료가 있는데

나는 git pull유해하다고 주장하는 동료가 있는데 누군가 그것을 사용할 때마다 화가납니다.

git pull명령은 로컬 저장소를 업데이트하는 정식 방법 인 것 같습니다. 사용 git pull하면 문제가 발생합니까? 어떤 문제가 발생합니까? 자식 저장소를 업데이트하는 더 좋은 방법이 있습니까?



답변

요약

기본적으로 git pull코드 기록에 노이즈와 복잡성을 추가하는 병합 커밋을 만듭니다. 또한 pull수신 변경으로 인해 변경 사항이 어떻게 영향을 받을지 쉽게 생각할 수 없습니다.

git pull명령은 너무 오래 그것은 단지 빨리 감기 병합을 수행으로 안전합니다. 경우 git pull에 구성된 경우에만 빨리 감기 병합을하고 빨리 감기 병합 할 수없는 경우, 다음 힘내 오류와 함께 종료됩니다. 이렇게하면 들어오는 커밋을 연구하고 이들이 커밋이 로컬 커밋에 어떤 영향을 줄지 생각하고 최상의 작업 과정 (병합, 리베이스, 재설정 등)을 결정할 수 있습니다.

Git 2.0 이상에서는 다음을 실행할 수 있습니다.

git config --global pull.ff only

기본 동작을 빨리 감기로 변경합니다. 1.6.6에서 1.9.x 사이의 Git 버전을 사용하면 입력 습관을 들여야합니다.

git pull --ff-only

그러나 모든 버전의 Git에서는 git up다음과 같이 별칭을 구성하는 것이 좋습니다 .

git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'

git up대신에 사용 합니다 git pull. 다음과 같은 이유로이 별칭을 선호합니다 git pull --ff-only.

  • Git의 모든 (고대가 아닌) 버전에서 작동합니다.
  • 현재 진행중인 지점뿐만 아니라 모든 업스트림 지점을 가져옵니다.
  • origin/*더 이상 업스트림에 존재하지 않는 오래된 가지를 정리합니다 .

문제 git pull

git pull제대로 사용하면 나쁘지 않습니다. Git의 최근 몇 가지 변경 사항으로 git pull올바르게 사용하기가 쉽지만 불행히도 평야의 기본 동작 git pull에는 몇 가지 문제가 있습니다.

  • 그것은 역사에서 불필요한 비선형 성을 소개합니다
  • 의도적으로 업스트림 아웃백 된 커밋을 실수로 쉽게 다시 도입 할 수 있습니다.
  • 예상치 못한 방식으로 작업 디렉토리를 수정합니다.
  • 다른 사람의 작업을 검토하기 위해하고있는 일을 일시 중지하면 git pull
  • 원격 브랜치에 올바르게 리베이스하기가 어렵습니다.
  • 원격 저장소에서 삭제 된 분기를 정리하지 않습니다.

이러한 문제는 아래에 더 자세히 설명되어 있습니다.

비선형 역사

기본적 git pull으로이 명령은 running git fetch다음에 오는 것과 같습니다 git merge @{u}. 로컬 리포지토리에 푸시되지 않은 커밋이 있으면 병합 부분이 git pull병합 커밋 을 만듭니다.

병합 커밋에 대해 본질적으로 나쁜 것은 없지만 위험 할 수 있으므로 존중해야합니다.

  • 병합 커밋은 본질적으로 검사하기가 어렵습니다. 합병이 무엇을하는지 이해하려면 모든 부모와의 차이점을 이해해야합니다. 기존의 차이점은이 다차원 정보를 잘 전달하지 못합니다. 반대로 일련의 일반 커밋은 검토하기 쉽습니다.
  • 병합 충돌 해결은 까다 롭고 병합 커밋을 검토하기가 어렵 기 때문에 실수가 오랫동안 감지되지 않는 경우가 많습니다.
  • 병합은 정기적 커밋의 영향을 조용히 대체 할 수 있습니다. 이 코드는 더 이상 증분 커밋의 합계가 아니므로 실제로 변경된 사항에 대한 오해가 발생합니다.
  • 병합 커밋은 일부 지속적인 통합 체계를 방해 할 수 있습니다 (예 : 두 번째 부모가 진행중인 불완전한 작업을 가리키는 가정 된 규칙에 따라 첫 번째 부모 경로 만 자동 빌드).

물론 병합 할 시간과 장소가 있지만 병합을 사용하거나 사용하지 않아야 할시기를 이해하면 리포지토리의 유용성을 향상시킬 수 있습니다.

Git의 목적은 코드베이스의 진화를 쉽게 공유하고 소비 할 수 있도록하는 것이며, 전개 된대로 정확하게 기록을 기록하지는 않습니다. (동의하지 않는 경우 rebase명령 및 생성 된 이유를 고려하십시오 .) 생성 된 병합 커밋은 git pull유용한 의미를 다른 사람에게 전달하지 않습니다. 다른 사람이 변경 작업을 수행하기 전에 다른 사람이 저장소로 푸시했다고 말합니다. 다른 사람들에게 의미가없고 위험 할 수있는 커밋을 병합 한 이유는 무엇입니까?

git pull병합 대신 리베이스 하도록 구성 할 수도 있지만 문제가 있습니다 (나중에 설명). 대신 git pull빨리 감기 병합 만 수행하도록 구성해야합니다.

재 확보 된 커밋의 재 도입

누군가가 지점을 리베이스하고 강제로 밀고 있다고 가정하십시오. 이것은 일반적으로 발생하지 않아야하지만 때때로 필요합니다 (예 : 실수로 커밋되어 푸시 된 50GiB 로그 파일 제거). 병합을 수행 git pull하면 업스트림 분기의 새 버전이 로컬 리포지토리에 여전히 존재하는 이전 버전으로 병합됩니다 . 결과를 누르면 피치 포크와 횃불이 나옵니다.

일부는 실제 문제가 강제 업데이트라고 주장 할 수 있습니다. 예, 일반적으로 가능할 때마다 강제 푸시를 피하는 것이 좋지만 때로는 피할 수없는 경우도 있습니다. 개발자는 때때로 업데이트되기 때문에 강제 업데이트를 처리 할 준비가되어 있어야합니다. 이것은 일반적인 커밋을 통해 이전 커밋에서 맹목적으로 병합하지 않음을 의미 git pull합니다.

놀라운 작업 디렉토리 수정

작업 디렉토리 나 색인 git pull이 완료 될 때까지 어떻게 보일지 예측할 방법이 없습니다 . 다른 작업을 수행하기 전에 해결해야 할 병합 충돌이있을 수 있습니다. 누군가 실수로 파일을 푸시했거나 작업중인 디렉토리의 이름을 바꿀 수 있기 때문에 작업 디렉토리에 50GiB 로그 파일이 나타날 수 있습니다.

git remote update -p(또는 git fetch --all -p)를 사용하면 병합 또는 리베이스를 결정하기 전에 다른 사람의 커밋을 살펴볼 수 있으므로 조치를 취하기 전에 계획을 수립 할 수 있습니다.

다른 사람의 커밋을 검토하기 어려움

당신이 약간의 변화를 겪고 있고 누군가 다른 사람들이 방금 밀었던 커밋을 검토하기를 원한다고 가정 해보십시오. git pull의 병합 (또는 리베이스) 작업은 작업 디렉토리와 색인을 수정합니다. 즉, 작업 디렉토리와 색인이 깨끗해야합니다.

을 사용한 git stash다음을 사용할 수 git pull있지만 검토가 끝나면 어떻게합니까? 원래 위치로 돌아가려면 만든 병합을 실행 취소 git pull하고 숨김을 적용해야합니다.

git remote update -p(또는 git fetch --all -p)는 작업 디렉토리 또는 색인을 수정하지 않으므로 단계적 및 / 또는 단계적 변경 사항이 없더라도 언제든지 실행하는 것이 안전합니다. 작업중인 작업을 일시 중지하고 작업중인 커밋을 정리하거나 마무리하지 않아도 다른 사람의 커밋을 검토 할 수 있습니다. git pull유연성을 제공하지 않습니다.

원격 지점에 기초

일반적인 Git 사용 패턴은 도입 된 병합 커밋을 제거하기 위해 git pull최신 변경 사항을 가져온 다음에 a git rebase @{u}를 수행하는 것입니다 git pull. 이 망할 놈의 말에 의해 하나의 단계로이 두 단계를 줄이기 위해 일부 구성 옵션을 가지고 일반적인 충분 git pull대신 병합의 REBASE을 수행 할을합니다 (참조 branch.<branch>.rebase, branch.autosetuprebasepull.rebase옵션).

불행하게도, 유지하려는 푸시 master되지 않은 병합 커밋이있는 경우 (예 : 푸시 된 기능 분기를에 병합하는 커밋 ) rebase-pull ( git pullbranch.<branch>.rebase설정 됨 true)이나 merge-pull (기본 git pull동작) 다음에 rebase가 작동합니다. 옵션 git rebase없이 병합을 제거 하기 때문입니다 (DAG를 선형화 함) --preserve-merges. rebase-pull 작업은 병합을 유지하도록 구성 할 수 없으며 merge-pull 다음에 오는 것은 merge-pull로 git rebase -p @{u}인한 병합을 제거하지 않습니다. 업데이트 : 힘내 v1.8.5 추가 git pull --rebase=preserve하고 git config pull.rebase preserve. 이것은 업스트림 커밋을 가져온 후 git pull수행 git rebase --preserve-merges됩니다. ( 헤드 업을위한 funkaster 에 감사 합니다!)

삭제 된 분기 정리

git pull원격 저장소에서 삭제 된 분기에 해당하는 원격 추적 분기를 제거하지 않습니다. 예를 들어, 누군가 foo원격 리포지토리에서 분기 를 삭제해도 여전히 표시 origin/foo됩니다.

이로 인해 사용자는 죽인 지점이 여전히 활성 상태라고 생각하기 때문에 실수로 부활하게됩니다.

더 나은 대안 : git up대신 사용git pull

대신 git pull다음 git up별칭을 만들어 사용하는 것이 좋습니다 .

git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'

이 별명은 모든 업스트림 브랜치에서 모든 최신 커밋을 다운로드하고 (불량 브랜치 정리) 로컬 브랜치를 업스트림 브랜치의 최신 커밋으로 빨리 전달하려고합니다. 성공하면 로컬 커밋이 없으므로 병합 충돌의 위험이 없습니다. 로컬 (푸시되지 않은) 커밋이 있으면 빨리 감기에 실패하여 작업을 수행하기 전에 업스트림 커밋을 검토 할 수 있습니다.

이것은 여전히 ​​예측할 수없는 방식으로 작업 디렉토리를 수정하지만 로컬 변경이없는 경우에만 가능합니다. 달리 git pull, git up당신이 병합 충돌을 해결하기 위해 기대 프롬프트로 드롭하지 않습니다.

다른 옵션 : git pull --ff-only --all -p

다음은 위의 git up별명에 대한 대안입니다 .

git config --global alias.up 'pull --ff-only --all -p'

이 버전 git up은 다음을 git up제외하고 이전 별명 과 동일한 동작을합니다 .

  • 로컬 브랜치가 업스트림 브랜치로 구성되지 않은 경우 오류 메시지가 조금 더 복잡합니다.
  • 향후 버전의 Git에서 변경 될 수 있는 문서화되지 않은 기능 (에 -p전달 된 인수) 에 의존합니다.fetch

Git 2.0 이상을 실행중인 경우

Git 2.0 이상 git pull에서는 기본적으로 빨리 감기 만 수행 하도록 구성 할 수 있습니다 .

git config --global pull.ff only

이처럼 git pull작동 git pull --ff-only하지만 여전히 모든 업스트림 커밋을 가져 오거나 이전 origin/*분기를 정리하지 않으므로 여전히 선호합니다 git up.


답변

내 대답 은 HackerNews에서 발생한 토론에서 가져 왔습니다 .

베테 릿 헤드 라인 법칙 (Betteridge Law of Headlines)을 사용하여 질문에 대답하고 싶은 마음이 듭니다. 왜 git pull유해한 것으로 간주됩니까? 그렇지 않습니다.

  • 비선형 성은 본질적으로 나쁘지 않습니다. 그들이 실제 역사를 나타내면 괜찮습니다.
  • 업스트림 기반 업스트림 커밋의 실수로 재 도입은 히스토리 업스트림을 잘못 재 작성한 결과입니다. 히스토리가 여러 리포지토리와 함께 복제 된 경우 히스토리를 다시 쓸 수 없습니다.
  • 작업 디렉토리를 수정하면 예상되는 결과입니다. 즉, hg / monotone / darcs / other_dvcs_predating_git의 행동에 직면 한 논쟁의 유용성이지만 본질적으로 나쁘지는 않습니다.
  • 병합을 위해서는 다른 사람의 작업을 검토하기 위해 일시 ​​중지해야하며 다시 git pull에서 예상되는 동작입니다. 병합하지 않으려면 git fetch를 사용해야합니다. 다시 말하지만, 이것은 이전의 인기있는 dvc와 비교할 때 git의 특질이지만, 본질적으로 나쁘지는 않습니다.
  • 원격 브랜치에 대해 리베이스하기 어렵게 만드는 것이 좋습니다. 꼭 필요한 경우가 아니면 역사를 다시 쓰지 마십시오. 나는 (가짜) 선형 역사의 추구를 이해하지 못합니다.
  • 가지를 정리하지 않는 것이 좋습니다. 각 레포는 보유하고자하는 것을 알고 있습니다. Git은 마스터-슬레이브 관계에 대한 개념이 없습니다.

답변

Git을 올바르게 사용하면 유해한 것으로 간주되지 않습니다. 유스 케이스가 부정적인 영향을 미치는지 알지만 공유 기록을 수정하지 않으면 문제를 피할 수 있습니다.


답변

허용 된 답변 주장

병합을 유지하도록 리베이스 풀 작업을 구성 할 수 없습니다.

그러나 Git 1.8.5 부터 답변을 게시 할 수 있습니다.

git pull --rebase=preserve

또는

git config --global pull.rebase preserve

또는

git config branch.<name>.rebase preserve

문서는 말을

하는 경우 preserve,도 통과 --preserve-merges로컬에 최선을 다하고 병합 커밋은 ‘자식 풀’을 실행하여 평평하게되지 않도록 ‘자식 REBASE’에 함께.

이 이전 논의는 더 자세한 정보와 다이어그램을 가지고 자식 풀은 –preserve-병합 –rebase . 또한 왜 옳지 않은 git pull --rebase=preserve것과 같은지 설명합니다 git pull --rebase --preserve-merges.

이 다른 이전 논의는 보존-병합이 REBASE 실제로는 훨씬 더 복잡한 정규 REBASE보다 어떻게합니까, 그리고 변형에 대해 설명 : 정확히 자식의 무엇 “REBASE –preserve-병합”할 (? 그리고 왜)


답변

당신이 이전 자식 저장소에 가면 자식까지 그들이 제안 별칭이 다릅니다.
https://github.com/aanand/git-up

git config --global alias.up 'pull --rebase --autostash'

이것은 나에게 완벽하게 작동합니다.


답변