'git pull'은 어떤 경우에 해로울 수 있습니까?


409

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

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



8
또는 git pull --rebase새로운 브랜치에 대해이 전략을 기본값으로 설정할 수 있습니다git config branch.autosetuprebase
knoopx

4
knoopx는 로컬과 원격 --rebasegit pull동기화 하기 위해 플래그를 추가 한 다음 업데이트 된 로컬 위에서 로컬 변경 사항을 재생합니다. 그런 다음 모든 것을 푸시하면 새 커밋을 원격 끝에 추가합니다. 꽤 간단합니다.
Heath Lilley

4
감사합니다 @BenMcCormick. 나는 이미 그렇게했지만 질문의 타당성에 관한 토론은 질문 아래의 주석에서 일어나고있는 것 같습니다. 그리고 저는 여러분의 개인적인 의견을 제시 할 수있는 플랫폼을 만드는 질문을하는 것이 SO의 Q & A 구조가 실제로는 아닙니다.
mcv

4
@RichardHansen, 그것은 포인트 시스템을 속이는 방법처럼 보입니다. 특히 당신의 대답이 톤의 급격한 차이와 짧은 시간 간격을 갖는 답변과 같습니다. Q & A 모델을 사용하여 우리는 모두 이전 지식을 사용하여 질문하고 스스로 대답 할 수 있습니다. 이 시점에서 블로그 게시물을 여러 번 더 적절하게 작성하는 것이 좋습니다. Q & A는 구체적으로 다른 사람들의 지식을 추구합니다. 블로그 게시물은 자신의 것을 보여줍니다.
Josh Brown

답변:


546

요약

기본적으로 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.


6
@brianz : git remote update -p와 같습니다 git fetch --all -p. git remote update -p옛날 옛적에 옵션 fetch이 없었기 때문에 타이핑 습관 이 -p있습니다. 선두에 대해서는 !, 설명을 참조 alias.*하여 git help config. "별칭 확장명 앞에 느낌표가 있으면 쉘 명령으로 취급됩니다."
Richard Hansen

13
Git 2.0은 pull.ff별명없이 동일한 것을 달성하는 것처럼 보이는 구성을 추가 합니다.
Danny Thomas

51
"풀이 다른 사람들이 미친 짓을 할 때 문제가 발생할 수 있습니다." 아니요, 문제를 일으키는 업스트림 리포지토리에서 커밋을 리베이스하는 것과 같은 미친 일입니다. IMO 리베이스는 아직 푸시되지 않은 커밋에서 로컬로 수행 할 때만 안전합니다. 예를 들어 푸시하기 전에 당기면 로컬 커밋을 리베이스하면 히스토리를 선형으로 유지하는 데 도움이됩니다 (리니어 히스토리는 그다지 중요하지 않습니다). 여전히 git up흥미로운 대안처럼 들립니다.
mcv

16
당신의 요점의 대부분은 당신이 무언가 잘못하고 있기 때문입니다 : 당신은 자신의 작업 브랜치에서 코드를 검토하려고합니다 . 좋은 생각은 아닙니다. 새 분기를 만들고 --rebase = preserve를 가져온 다음 해당 분기를 던지십시오 (또는 원하는 경우 병합).
funkaster

5
@funkaster의 요점은 특히 "다른 사람들의 커밋을 검토하는 어려움"이라는 의미가 있습니다. 이것은 대부분의 Git 사용자가 사용하는 리뷰 흐름이 아니며, 내가 본 적이없는 곳이며, 제목 아래에 설명 된 불필요한 추가 작업이 모두 원인이 아닙니다 git pull.
Ben Regenspan

195

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

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

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

13
동의한다. 에 대해 본질적으로 해로운 것은 없습니다 git pull. 그러나 꼭 필요한 것보다 더 많은 기록을 다시 작성하려는 등의 일부 유해한 사례와 충돌 할 수 있습니다. 그러나 git은 유연하므로 다른 방식으로 사용하려면 반드시 그렇게하십시오. 그러나 그것은 당신 (잘, @Richard Hansen)이 git에서 특이한 일을하기를 원하기 때문 git pull입니다.
mcv

28
더 동의하지 못했습니다. 사람들이 옹호 git rebase하고 git pull유해한 것을 고려하고 있습니까? 정말?
Victor Moroz 2016 년

10
누군가가 도덕성을 축으로 사용하여 그래프를 만들고 git 명령을 좋거나 나쁘거나 중간에 어딘가로 분류하는 것을 보는 것이 좋습니다. 이 차트는 개발자마다 다르지만 git을 사용하는 사람에 대해서는 많이 말할 것입니다.
michaelt

5
옵션이 git pull없는 문제 --rebase는 병합 방향입니다. 차이를 볼 때 해당 병합의 모든 변경 사항은 이제 변경 한 사람이 아니라 당기는 사람의 것입니다. 병합이 두 개의 개별 분기 (A-> B)에 대해 예약 된 워크 플로우를 좋아하므로 병합 커밋은 도입 된 내용이 명확하고 rebasing은 동일한 분기 (원격 A-> 로컬 A에 최신 상태로 유지되도록 예약 됨) )
Craig Kochis 2014 년

4
그렇다면 누군가가 다른 사람이나 다른 방법으로 몇 초 전에 잡아 당겼다면 어떻게 알 수 있습니까? 나는 이것이 단지 소음 일뿐 아니라 실제로 관련된 역사를 난독 화하고 있다고 생각합니다. 이것은 심지어 역사의 가치를 줄입니다. 좋은 역사는 a) 깨끗하고 b) 실제로 중요한 역사를 가져야한다.
David Ongaro

26

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


10
이것을 자세히 설명하기 위해 : 모든 사람들이 자신의 브랜치에서 일한다면 (내 의견으로는 git을 사용하는 적절한 방법입니다), git pull어떤 문제도 아닙니다. 자식으로 분기하는 것이 저렴합니다.
AlexQueue

18

허용 된 답변 주장

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

그러나 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-병합"할 (? 그리고 왜)


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.