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


88

몇 가지 기본 코드를 공유하기 위해 작업중인 몇 가지 프로젝트와 함께 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.

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


왜 git이 당신에게 말하는 것을하지 않습니까 : git pull?
AlexWien

8
힘내 풀은 내 질문과 관련이 없습니다. 그러나 예, git pull은 원본 저장소에 새로운 것이 없으므로 최신 메시지를 제공합니다.
마테우스

1
예를 들어, 원격 마스터 HEAD가 C에 있지만 현재 분기가 A에있는 ...-> A-> B-> C의 히스토리가 있습니다. (또는 C에 연결되지 않은 A의 일부 자손). 나는 확실히 내 자식 역사를 볼 것입니다.
Mark Leighton Fisher

나는 같은 문제가 있습니다. 나는 --rejoin하위 트리 푸시 속도를 높이기 위해 사용해 왔습니다 . 수동으로 하위 트리 푸시를 단계별로 실행 subtree split --rejoin하여 실행 하면 분할 하위 트리 분기에는 일반적으로 전체 하위 트리 기록이 포함 된 마지막 재결합까지의 기록 만 있습니다. 저에게는 이것이 빠른 전달이 아닌 오류의 즉각적인 원인입니다. 하위 트리 분할이 잘린 기록을 생성하는 이유를 여전히 잘 모르겠습니다.
hurrymaplelad dec.

답변:


98

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

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

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

2
날 도와 줬어! 감사.
Michael Forrest

8
Windows에서이 명령을 실행하는 방법은 무엇입니까? 나는 받고있다error: unknown option 'prefix'
pilau

3
놀라운,이 그냥 내 일을 :) 저장
bpipat

2
사실이지만 아마도 이런 방식으로 하위 트리를 사용하는 것은 heroku에 대한 단방향 푸시입니다. 일반적으로 heroku는 여러 사용자가 푸시하고 가져올 수있는 사실상의 git 저장소로 간주되지 않습니다.
Eric Woodruff

11
나는 어떤 관계 herokupythonapp가지고 있고 그 답을 찾기가 어려웠습니다 . 이것을 Heroku 특정 언어로 번역 :git push <your subtree's origin> `git subtree split --prefix=Path/to/subtree master`:master --force
aednichols

39

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

이것은 훨씬 더 깨끗한 솔루션입니다. 나를 위해 작동합니다!
Infinity

git subtree split --prefix subtreedirectoryname master를 실행하면 치명적인 모호한 인수 'master'가 표시됩니다. 알 수없는 개정 또는 경로가 작업 트리에 없습니다
Demodave

이것은 훌륭한 솔루션입니다. 방금 계속해서 얻은 git subtree명령은 이전에 명령이 아닙니다. 솔루션을 사용한 후 나는 모든 대신 Heroku가 내 REPO 폴더를 배포 할 수 있었다
pakman198

훌륭한 솔루션 및 시간 절약
Nisharg Shah

29

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


2
나는 이미 두 번 도움이 된이 답변을 추천합니다. 그리고 설명은 분할 및 올바른 위치로의 밀기가 어떻게 작동하는지 이해하는 데 필요한 것입니다. 감사합니다.
Kamil Wozniak

2
"프로젝트 공유"란 무엇입니까?
Colin D

1
@ColinD "project-shared"는 리모컨의 이름입니다. 가져오고 푸시 할 장소를 구성 할 때 선택한 이름입니다. 을 사용하여 리모컨 목록을 가져올 수 있습니다 git remote -v.
entheh

@entheh 당신이 이것에 보낸 시간은 jsut가 내 하루를 만들었습니다. 감사!
jun

1
이 버그는 하위 저장소에 이름이 접두사와 동일한 디렉토리가있을 때 항상 발생한다는 것을 발견했습니다. 예를 들어 하위 저장소를 주 저장소에 "libxxx"로 추가하고 하위 저장소 자체에도 "libxxx라는 하위 디렉토리가 있습니다. ". 이 경우 git은 하위 저장소의 커밋을 주 저장소의 커밋으로 잘못 처리하고 분할을 시도합니다. 이 --onto옵션은 git이 이미 하위 저장소에있는 커밋을 식별하는 데 도움이됩니다.
Cosyn

14

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

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

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


3

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

내가 알아 낸 것은 로컬 마스터 브랜치에 연결되지 않은 브랜치를 가지고 있다는 것입니다. 이 가지가 존재하고 공허 속에 매달려 있습니다. 귀하의 경우 아마도 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입니다.


3

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

이것이 어떻게 작동하는지 더 잘 이해하기 위해 예제 (쉽게 재현 할 수 있음)를 보겠습니다. 다음 내역을 고려하십시오 (행 형식은 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

2020-05-09 (토) 현재 버그가 수정 되었나요? @klimkin
halfmoonhalf

버그가 수정되었습니다. 참조 : 기여 / 하위 트리 : "하위 트리 분할"건너 뛴 병합 버그 수정. github.com/git/git/commit/…
halfmoonhalf

0

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

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

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


0

또 다른 [간단한] 해결책은 가능한 한 다른 커밋을 만들어 리모컨의 헤드를 전진시키는 것입니다. 이 고급 헤드를 로컬 하위 트리로 가져온 후 다시 푸시 할 수 있습니다.


1
이것이 바로 로컬 하위 트리와 동기화되지 않은 원격 저장소와 동일한 문제를 해결 한 방법입니다.
Aidas Bendoraitis

0

로컬 변경 사항을 원격 하위 트리 저장소에 강제로 푸시 할 수 있습니다.

git push subtree_remote_address.git `git subtree split --prefix=subtree_folder`:refs/heads/branch --force

0

Chris Jordan의 솔루션을 기반으로하는 Windows 사용자를위한 빠른 powershell

$id = git subtree split --prefix pythonapp master
Write-Host "Id is: $id"
Invoke-Expression "git push heroku $id`:master --force"

0

그래서 이것은 @entheh가 말한 것을 기반으로 작성한 것입니다.

for /f "delims=" %%b in ('git subtree split --prefix [name-of-your-directory-on-the-file-system-you-setup] -b [name-of-your-subtree-branch]') do @set token=%%b
git push [alias-for-your-subtree] %token%:[name-of-your-subtree-branch] --force

pause

0

이 문제도있었습니다. 상위 답변을 기반으로 한 작업은 다음과 같습니다.

주어진:

#! 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.

이것을 입력하십시오 :

git push <origin> 72a6157733c4e0bf22f72b443e4ad3be0bc555ce:<branch> --force

'git subtree push'에 의해 출력되는 토큰은 'git push'에서 사용됩니다.

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