Tại sao tôi không thể đẩy cây con Git cập nhật này?


88

Tôi đang sử dụng cây con Git với một vài dự án mà tôi đang thực hiện, để chia sẻ một số mã cơ sở giữa chúng. Mã cơ sở được cập nhật thường xuyên và việc nâng cấp có thể xảy ra ở bất kỳ ai trong các dự án, cuối cùng thì tất cả chúng đều được cập nhật.

Tôi đã gặp sự cố trong đó git báo cáo rằng cây con của tôi đã được cập nhật, nhưng việc đẩy bị từ chối. Ví dụ:

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

Nếu tôi đẩy, tôi sẽ nhận được một thông báo rằng không có gì để đẩy ... Đúng không? ĐÚNG? :(

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

Điều gì có thể là lý do cho điều này? Tại sao đẩy không thành công?


Tại sao bạn không làm những gì git nói với bạn: git pull?
AlexWien

8
Git pull không liên quan gì đến câu hỏi của tôi. Nhưng có, git pull cung cấp cho tôi một thông báo cập nhật, vì không có gì mới trong repo gốc.
mateusz 7/12/12

1
Có vẻ như chi nhánh bạn đang ở phía sau HEAD chính - ví dụ: lịch sử của ... -> A -> B -> C nơi HEAD chính ở xa ở C nhưng chi nhánh hiện tại của bạn ở A (hoặc một số con cháu của A không kết nối với C). Tôi sẽ xem lịch sử git của mình để chắc chắn.
Mark Leighton Fisher

Im có cùng một vấn đề. Tôi đã sử dụng --rejoinđể tăng tốc độ đẩy cây con. Khi tôi bước qua lần đẩy cây con theo cách thủ công, đang chạy subtree split --rejoin, nhánh cây con đã tách chỉ có lịch sử trở lại lần tham gia lại cuối cùng, khi nó thường chứa toàn bộ lịch sử cây con. Đối với tôi, đó là nguyên nhân ngay lập tức gây ra lỗi không tua nhanh. Tôi vẫn không rõ tại sao chia cây con lại tạo ra lịch sử bị cắt ngắn.
quickmaplelad

Câu trả lời:


98

Tôi đã tìm thấy câu trả lời trên bình luận blog này https://coderwall.com/p/ssxp5q

Nếu bạn gặp phải vấn đề "Cập nhật bị từ chối vì phần đầu của nhánh hiện tại của bạn đang ở phía sau. Hợp nhất các thay đổi từ xa (ví dụ: 'git pull')" khi bạn đang đẩy (vì bất kỳ lý do gì, đặc biệt là vấn đề với lịch sử git) thì bạn sẽ cần lồng các lệnh git để có thể thực hiện việc đẩy đến heroku. ví dụ, với ví dụ trên:

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

2
Đã giúp tôi! Cảm ơn.
Michael Forrest

8
Làm thế nào để chạy lệnh này trên Windows? Tôi đang nhậnerror: unknown option 'prefix'
pilau

3
Tuyệt vời, điều này chỉ lưu ngày của tôi :)
bpipat

2
Đúng nhưng có lẽ hầu hết việc sử dụng cây con theo cách này là một cách để thúc đẩy heroku. Nói chung, heroku không được coi là repo defacto git cho nhiều người dùng đẩy và kéo.
Eric Woodruff

11
Tôi đã có một thời gian khó khăn để xác định mối quan hệ herokupythonappcó trong câu trả lời. Dịch điều này ra ngôn ngữ dành riêng cho Heroku:git push <your subtree's origin> `git subtree split --prefix=Path/to/subtree master`:master --force
aednichols

39

Trên windows, lệnh lồng nhau không hoạt động:

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

Bạn chỉ có thể chạy bit lồng nhau trước:

git subtree split --prefix pythonapp master

Điều này sẽ (sau nhiều số) trả về một mã thông báo, ví dụ:

157a66d050d7a6188f243243264c765f18bc85fb956

Sử dụng điều này trong lệnh chứa, ví dụ:

git push heroku 157a66d050d7a6188f243243264c765f18bc85fb956:master --force

Đây là một giải pháp sạch hơn nhiều. Làm việc cho tôi!
Infinity

Khi tôi chạy git chia cây con --prefix subtreedirectoryname thạc sĩ, tôi nhận lập luận mơ hồ gây tử vong 'thầy': sửa đổi chưa được biết hoặc đường dẫn không trong cây làm việc
Demodave

Đây là một giải pháp tuyệt vời, tôi chỉ nhận git subtreeđược không phải là một lệnh trước đây. Sau khi sử dụng giải pháp của bạn tôi đã có thể triển khai một thư mục để Heroku thay vì tất cả repo của tôi
pakman198

Giải pháp tuyệt vời và tiết kiệm thời gian
Nisharg Shah

29

Sử dụng --ontocờ:

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

[EDIT: rất tiếc là subtree pushkhông chuyển tiếp --ontođến bên dưới split, vì vậy hoạt động phải được thực hiện trong hai lệnh! Sau khi hoàn thành, tôi thấy rằng các lệnh của tôi giống với các lệnh trong một trong các câu trả lời khác, nhưng lời giải thích khác nhau nên tôi vẫn để nó ở đây.]

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

Hoặc nếu bạn không sử dụng 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

Tôi đã dành hàng giờ để nghiền ngẫm nguồn git-subtree để tìm ra nguồn này, vì vậy tôi hy vọng bạn đánh giá cao nó;)

subtree pushbắt đầu bằng cách chạy subtree split, ghi lại lịch sử cam kết của bạn thành một định dạng sẵn sàng để đẩy. Cách nó thực hiện là, nó loại public/shared/bỏ phía trước của bất kỳ đường dẫn nào có nó và xóa mọi thông tin về các tệp không có nó. Điều đó có nghĩa là ngay cả khi bạn kéo không bị bóp méo, tất cả các cam kết của kho lưu trữ con ngược dòng sẽ bị bỏ qua vì chúng đặt tên tệp theo đường dẫn trần của chúng. (Cam kết không đụng hàng với bất kỳ tệp nào dướipublic/shared/hoặc hợp nhất các cam kết giống hệt với cha mẹ, cũng bị thu gọn. [CHỈNH SỬA: Ngoài ra, tôi đã tìm thấy một số phát hiện bí, vì vậy bây giờ tôi nghĩ chỉ khi bạn kéo không bị bóp méo và sau đó cam kết hợp nhất đơn giản thu gọn được mô tả trong một câu trả lời khác quản lý để chọn đường dẫn không bị bóp méo và loại bỏ đường dẫn bị bóp méo.]) Kết quả là, thứ mà nó cố gắng đẩy kết thúc chứa bất kỳ công việc nào mà ai đó đã cam kết với kho lưu trữ máy chủ hiện tại mà bạn đang đẩy, nhưng không phải công việc mà mọi người đã cam kết trực tiếp với kho lưu trữ phụ hoặc thông qua một máy chủ lưu trữ khác kho.

Tuy nhiên, nếu bạn sử dụng --onto, thì tất cả các cam kết ngược dòng được ghi lại là OK để sử dụng nguyên văn, vì vậy khi quá trình viết lại bắt gặp chúng với tư cách là một trong những cha mẹ của hợp nhất mà nó muốn viết lại, nó sẽ giữ chúng thay vì cố gắng viết lại. theo cách thông thường.


2
Tôi đang giới thiệu câu trả lời này như một câu trả lời đã giúp tôi hai lần. Và mô tả là những gì tôi cần để hiểu cách hoạt động của việc tách và đẩy đến đúng vị trí. Cảm ơn bạn.
Kamil Wozniak

2
"Dự án được chia sẻ" là gì?
Colin D

1
@ColinD "project-shared" là tên của điều khiển từ xa. Đó là tên bạn sẽ chọn khi định cấu hình nơi để tìm nạp và đẩy đến. Bạn có thể nhận được danh sách các điều khiển từ xa với git remote -v.
entheh

@entheh những giờ bạn dành cho việc này có jsut làm nên ngày của tôi. Cảm ơn!
tháng

1
Tôi thấy lỗi này luôn xảy ra khi kho lưu trữ phụ của bạn có một thư mục có tên giống với tiền tố, ví dụ: bạn thêm kho lưu trữ phụ là "libxxx" vào kho lưu trữ chính và bản thân kho lưu trữ phụ của bạn cũng có một thư mục con có tên "libxxx ". Trong trường hợp này, git sẽ xử lý không chính xác các cam kết trong kho lưu trữ phụ như các cam kết trong kho lưu trữ chính và cố gắng chia nhỏ chúng. Các --ontotùy chọn giúp git để xác định các cam kết đã có trong kho phụ.
Cosyn

14

Đối với ứng dụng loại "GitHub pages", nơi bạn triển khai cây con "dist" cho nhánh gh-pages, giải pháp có thể trông giống như thế này

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

Tôi đề cập đến điều này vì nó trông hơi khác so với các ví dụ về heroku được đưa ra ở trên. Bạn có thể thấy rằng thư mục "dist" của tôi tồn tại trên nhánh chính của repo của tôi, và sau đó tôi đẩy nó dưới dạng cây con đến nhánh gh-pages cũng có nguồn gốc.


3

Tôi cũng đã gặp sự cố này trước đây, và đây là cách tôi giải quyết nó.

Những gì tôi phát hiện ra là tôi có một chi nhánh không thuộc chi nhánh chủ địa phương. Nhánh này tồn tại và nó chỉ treo lơ lửng trong khoảng không. Trong trường hợp của bạn, nó có thể được gọi project-shared. Giả sử trường hợp này xảy ra và khi thực hiện, git branchbạn có thể thấy một project-sharedchi nhánh cục bộ , sau đó bạn có thể ' nối ' các cam kết mới vào project-sharedchi nhánh hiện tại của mình bằng cách:

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

Cách tôi hiểu là git subtreesẽ bắt đầu tạo nhánh mới --onto, trong trường hợp này là public-sharednhánh cục bộ . Sau đó, branch có nghĩa là tạo một nhánh, nó chỉ thay thế public-sharednhánh cũ .

Điều này sẽ giữ tất cả SHA trước đó của public-sharedchi nhánh. Cuối cùng, bạn có thể làm một

git push project-shared project-shared:master

Giả sử rằng bạn cũng có một project-sharedđiều khiển từ xa; điều này sẽ đẩy cục bộ treo trong project-shared nhánh void đến masternhánh của điều khiển từ xa project-shared.


3

Điều này là do giới hạn của thuật toán ban đầu. Khi xử lý các cam kết hợp nhất, thuật toán ban đầu sử dụng tiêu chí đơn giản hóa để cắt bỏ các cha mẹ không liên quan. Đặc biệt, nó kiểm tra, nếu có cha mẹ, có cùng một cây. Nếu tìm thấy cha mẹ như vậy, nó sẽ thu gọn cam kết hợp nhất và thay vào đó sử dụng cam kết cha, giả sử rằng các cha mẹ khác có những thay đổi không liên quan đến cây con. Trong một số trường hợp, điều này sẽ dẫn đến việc loại bỏ các phần của lịch sử, có những thay đổi thực tế đối với cây con. Cụ thể, nó sẽ thả các chuỗi cam kết, điều này sẽ chạm vào một cây con, nhưng dẫn đến cùng một giá trị cây con.

Hãy xem một ví dụ (mà bạn có thể dễ dàng tái tạo) để hiểu rõ hơn về cách hoạt động của điều này. Hãy xem xét lịch sử sau (định dạng dòng là: chủ đề commit [tree]):

% 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

Trong ví dụ này, chúng tôi đang chia nhỏ dir. Các cam kết DEcó cùng một cây z, bởi vì chúng tôi có cam kết C, mà hoàn tác cam kết B, vì vậy B-Ctrình tự không làm gì dircả mặc dù nó có những thay đổi đối với nó.

Bây giờ chúng ta hãy thực hiện tách. Đầu tiên, chúng tôi phân chia về cam kết 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

Tiếp theo, chúng tôi chia về cam kết E.

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

Vâng, chúng tôi đã mất hai cam kết. Điều này dẫn đến lỗi khi cố gắng đẩy phần tách thứ hai, vì nó không có hai cam kết đó, mà đã có nguồn gốc.

Thông thường, bạn có thể chịu đựng lỗi này bằng cách sử dụng push --force, vì các cam kết bị bỏ thường sẽ không có thông tin quan trọng trong đó. Về lâu dài, lỗi cần được sửa, vì vậy lịch sử phân tách sẽ thực sự có tất cả các cam kết dir, như mong đợi. Tôi hy vọng bản sửa lỗi sẽ bao gồm phân tích sâu hơn về các cam kết của phụ huynh đối với các phần phụ thuộc ẩn.

Để tham khảo, đây là phần của mã gốc, chịu trách nhiệm về hành vi.

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

lỗi có được sửa kể từ 2020-05-09 (Sat) không? @klimkin
halfmoonhalf

lỗi đã được sửa. see: Contrib / cây con: sửa lỗi hợp nhất bỏ qua "tách cây con". github.com/git/git/commit/…
halfmoonhalf

0

Câu trả lời của Eric Woodruff không giúp ích được gì cho tôi, nhưng câu trả lời sau đã làm được:

Tôi thường 'git cây con kéo' với tùy chọn '--squash'. Có vẻ như điều này đã làm cho mọi thứ khó hòa giải hơn, vì vậy tôi cần phải thực hiện kéo cây con mà không làm hỏng lần này, giải quyết một số xung đột và sau đó thúc đẩy.

Tôi phải nói thêm rằng lực kéo bị bóp méo không tiết lộ bất kỳ xung đột nào, cho tôi biết mọi thứ đều ổn.


0

Một giải pháp khác [đơn giản hơn] là nâng cấp người đứng đầu điều khiển từ xa bằng cách thực hiện một cam kết khác nếu bạn có thể. Sau khi bạn kéo đầu nâng cao này vào cây con cục bộ thì bạn sẽ có thể đẩy lại từ nó.


1
Đây chính xác là cách tôi giải quyết vấn đề tương tự với kho lưu trữ từ xa không đồng bộ với cây con cục bộ.
Aidas Bendoraitis

0

Bạn có thể buộc đẩy các thay đổi cục bộ vào repo cây con từ xa

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

0

Một bản quyền nhanh cho người dùng windows dựa trên giải pháp của Chris Jordan

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

0

Vì vậy, đây là những gì tôi đã viết dựa trên những gì @entheh đã nói.

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

Cũng có vấn đề này. Đây là những gì tôi đã làm, dựa trên các câu trả lời hàng đầu:

Được:

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

Nhập cái này:

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

Lưu ý rằng mã thông báo được xuất bởi 'git cây con push' được sử dụng trong 'git push'.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.