[git] 이 최신 Git 하위 트리를 푸시 할 수없는 이유는 무엇입니까?

몇 가지 기본 코드를 공유하기 위해 작업중인 몇 가지 프로젝트와 함께 Git 하위 트리를 사용하고 있습니다. 기본 코드는 자주 업데이트되고 모든 프로젝트에서 업그레이드가 발생할 수 있으며 결국 모든 프로젝트가 업데이트됩니다.

git이 내 하위 트리가 최신 상태라고보고했지만 푸시가 거부되는 문제가 발생했습니다. 예를 들면 :

#! git subtree pull --prefix=public/shared project-shared master
From github.com:****
* branch            master     -> FETCH_HEAD
Already up-to-date.

밀면 밀면 안된다는 메시지가 나오면 … 맞죠? 권리? 🙁

#! git subtree push --prefix=public/shared project-shared master
git push using:  project-shared master
To git@github.com:***
! [rejected]        72a6157733c4e0bf22f72b443e4ad3be0bc555ce -> master (non-fast-forward)
error: failed to push some refs to 'git@github.com:***'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

그 이유는 무엇일까요? 푸시가 실패하는 이유는 무엇입니까?



답변

이 블로그 댓글 https://coderwall.com/p/ssxp5q 에서 답변을 찾았습니다.

푸시 할 때 “현재 브랜치의 끝이 뒤쳐져 서 업데이트가 거부되었습니다. 원격 변경 사항을 병합하십시오 (예 : ‘git pull’)”문제가 발생하는 경우 그런 다음 heroku에 강제로 푸시 할 수 있도록 git 명령을 중첩해야합니다. 예를 들어, 위의 예에서 :

git push heroku `git subtree split --prefix pythonapp master`:master --force


답변

Windows에서는 중첩 된 명령이 작동하지 않습니다.

git push heroku `git subtree split --prefix pythonapp master`:master --force

먼저 중첩 된 비트를 실행할 수 있습니다.

git subtree split --prefix pythonapp master

이것은 (많은 숫자 이후) 토큰을 반환합니다.

157a66d050d7a6188f243243264c765f18bc85fb956

포함 명령에서 이것을 사용하십시오. 예 :

git push heroku 157a66d050d7a6188f243243264c765f18bc85fb956:master --force


답변

다음 --onto플래그를 사용하십시오 .

# DOESN'T WORK: git subtree push --prefix=public/shared project-shared master --onto=project-shared/master

[편집 : 안타깝게도 기본으로 subtree push전달되지 않으므로 두 명령으로 작업을 수행해야합니다! 이렇게하면 내 명령이 다른 답변 중 하나의 명령과 동일하다는 것을 알 수 있지만 설명이 다르므로 어쨌든 여기에 남겨 두겠습니다.]--ontosplit

git push project-shared $(git subtree split --prefix=public/shared --onto=project-shared/master):master

또는 bash를 사용하지 않는 경우 :

git subtree split --prefix=public/shared --onto=project-shared/master
# This will print an ID, say 0123456789abcdef0123456789abcdef,
# which you then use as follows:
git push project-shared 01234567:master

나는 이것을 이해하기 위해 git-subtree 소스를 조사하는 데 몇 시간을 보냈으므로 감사하게 생각합니다.)

subtree push를 실행하여 시작 subtree split하면 커밋 내역을 푸시 할 준비가 된 형식으로 다시 작성됩니다. 이 작업을 수행하는 방식은 public/shared/경로가있는 모든 경로의 앞부분을 제거하고 해당 경로가없는 파일에 대한 정보를 제거하는 것입니다. 즉, 스쿼시되지 않은 상태로 가져 오더라도 모든 업스트림 하위 저장소 커밋은 베어 경로로 파일 이름을 지정하므로 무시됩니다. (아래의 파일을 건드리지 않는 커밋public/shared/또는 부모와 동일한 병합 커밋도 축소됩니다. [편집 : 또한, 나는 스쿼시 감지를 발견 한 이후로 비 스쿼시를 당긴 다음 다른 답변에 설명 된 단순한 병합 커밋 축소가 비 스쿼시 경로를 선택하고 관리하는 경우에만 해당한다고 생각합니다. 스쿼시 된 경로를 버립니다.]) 결론은 푸시하려는 항목에 사용자가 푸시하려는 현재 호스트 저장소에 커밋 된 작업이 포함되지만 하위 저장소에 직접 커밋되거나 다른 호스트를 통해 커밋되지 않은 작업이 포함된다는 것입니다. 저장소.

그러나를 사용 --onto하면 모든 업스트림 커밋이 그대로 사용 가능으로 기록되므로 재 작성 프로세스가 병합의 부모 중 하나로 발견되면 재 작성을 시도하는 대신 유지합니다. 일반적인 방식으로.


답변

gh-pages 분기에 “dist”하위 트리를 배포하는 “GitHub 페이지”유형 앱의 경우 솔루션은 다음과 같을 수 있습니다.

git push origin `git subtree split --prefix dist master`:gh-pages --force

위에서 언급 한 heroku 예제와 약간 다르게 보이기 때문에 이것을 언급합니다. 내 “dist”폴더가 내 repo의 마스터 브랜치에 존재하는 것을 볼 수 있으며,이 폴더를 원본에있는 gh-pages 브랜치에 하위 트리로 푸시합니다.


답변

이전에도이 문제가 발생했으며 여기에 해결 방법이 있습니다.

내가 알아 낸 것은 로컬 마스터 브랜치에 연결되지 않은 브랜치를 가지고 있다는 것입니다. 이 가지가 존재하고 공허 속에 매달려 있습니다. 귀하의 경우 아마도 project-shared. 이것이 사실이라고 가정하고 a git branch를 할 때 로컬 project-shared브랜치를 볼 수 있으며 다음을 수행 하여 기존 브랜치에 새 커밋을 ‘ 추가 ‘ 할 수 있습니다 project-shared.


git subtree split --prefix=public/shared --onto public-shared --branch public-shared

내가 이해 한 방식은 git subtree에서 새 분기를 만들기 시작하는 것 --onto입니다.이 경우 로컬 public-shared분기입니다. 그런 다음 분기는 이전 public-shared분기를 대체하는 분기를 만드는 것을 의미합니다 .

이렇게하면 public-shared분기 의 모든 이전 SHA가 유지됩니다 . 마지막으로


git push project-shared project-shared:master

project-shared리모컨도 있다고 가정합니다 . 이것은 void project-shared 브랜치 있는 로컬 매달린master 것을 리모트 브랜치로 푸시 할 것 project-shared입니다.


답변

이는 원래 알고리즘의 한계 때문입니다. 병합 커밋을 처리 할 때 원래 알고리즘은 관련없는 상위를 잘라 내기 위해 단순화 된 기준을 사용합니다. 특히 동일한 트리를 가진 부모가 있는지 확인합니다. 그러한 부모가 발견되면 다른 부모가 하위 트리와 관련되지 않은 변경 사항이 있다고 가정하여 병합 커밋을 축소하고 대신 부모 커밋을 사용합니다. 어떤 경우에는 이로 인해 히스토리의 일부가 삭제되어 하위 트리가 실제로 변경됩니다. 특히 하위 트리를 건드리지 만 동일한 하위 트리 값을 가져 오는 커밋 시퀀스를 삭제합니다.

이것이 어떻게 작동하는지 더 잘 이해하기 위해 예제 (쉽게 재현 할 수 있음)를 보겠습니다. 다음 내역을 고려하십시오 (행 형식은 commit [tree] subject).

% git log --graph --decorate --pretty=oneline --pretty="%h [%t] %s"
*   E [z] Merge branch 'master' into side-branch
|\
| * D [z] add dir/file2.txt
* | C [y] Revert "change dir/file1.txt"
* | B [x] change dir/file1.txt
|/
*   A [w] add dir/file1.txt

이 예에서는 dir. 커밋 DE같은 나무가 z우리가 커밋 때문에, C취소 커밋하는, B그래서 B-C순서를 위해 아무것도하지 않습니다 dir그것을 변경에도 불구하고 있습니다.

이제 분할을 해보겠습니다. 먼저 커밋으로 나눕니다 C.

% git log `git subtree split -P dir C` ...
* C' [y'] Revert "change dir/file1.txt"
* B' [x'] change dir/file1.txt
* A' [w'] add dir/file1.txt

다음으로 커밋으로 나눕니다 E.

% git log `git subtree split -P dir E` ...
* D' [z'] add dir/file2.txt
* A' [w'] add dir/file1.txt

예, 두 개의 커밋을 잃었습니다. 두 번째 분할을 푸시하려고 할 때 이미 오리진에 들어간 두 커밋이 없기 때문에 오류가 발생합니다.

push --force삭제 된 커밋에는 일반적으로 중요한 정보가 없기 때문에 일반적으로을 사용하여이 오류를 허용 할 수 있습니다. 장기적으로 버그를 수정해야하므로 분할 기록에는 실제로 dir예상대로 를 터치하는 모든 커밋이 포함 됩니다. 숨겨진 종속성에 대한 상위 커밋에 대한 심층 분석이 수정에 포함될 것으로 예상합니다.

참고로 다음은 동작을 담당하는 원본 코드의 일부입니다.

copy_or_skip()
  ...
  for parent in $newparents; do
      ptree=$(toptree_for_commit $parent) || exit $?
      [ -z "$ptree" ] && continue
      if [ "$ptree" = "$tree" ]; then
          # an identical parent could be used in place of this rev.
          identical="$parent"
      else
          nonidentical="$parent"
      fi
  ...
  if [ -n "$identical" ]; then
      echo $identical
  else
      copy_commit $rev $tree "$p" || exit $?
  fi


답변

Eric Woodruff의 답변은 도움이되지 않았지만 다음은 도움이되었습니다.

저는 보통 ‘–squash’옵션과 함께 ‘git subtree pull’을 사용합니다. 이로 인해 조정하기가 더 어려워 졌기 때문에 이번에는 스쿼시없이 하위 트리 풀을 수행하고 일부 충돌을 해결 한 다음 푸시해야했습니다.

나는 스쿼시 풀이 어떤 충돌도 드러내지 않았고 모든 것이 괜찮다고 말했습니다.