เหตุใดฉันจึงไม่สามารถพุชทรีย่อย Git ที่เป็นปัจจุบันนี้ได้


88

ฉันใช้ทรีย่อย 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 pull?
AlexWien

8
Git pull ไม่มีส่วนเกี่ยวข้องกับคำถามของฉัน แต่ใช่ git pull ช่วยให้ฉันได้รับข้อความล่าสุดเนื่องจากไม่มีอะไรใหม่ใน repo ต้นทาง
mateusz

1
ดูเหมือนว่าสาขาที่คุณอยู่จะอยู่หลังตำแหน่งที่ HEAD หลักอยู่ - ตัวอย่างเช่นประวัติของ ... -> A -> B -> C โดยที่ HEAD หลักระยะไกลอยู่ที่ C แต่สาขาปัจจุบันของคุณอยู่ที่ A (หรือบางส่วนสืบเชื้อสายมาจาก A ที่ไม่เชื่อมต่อกับ C) ฉันจะดูประวัติคอมไพล์ของฉันเพื่อความแน่ใจ
Mark Leighton Fisher

ฉันมีปัญหาเดียวกัน ฉันใช้--rejoinเพื่อเร่งการผลักดันทรีย่อย เมื่อฉันก้าวผ่านการพุชทรีย่อยด้วยตนเองการเรียกใช้subtree split --rejoinสาขาทรีย่อยที่แยกจะมีเพียงประวัติกลับไปที่การเข้าร่วมครั้งล่าสุดเมื่อโดยปกติแล้วจะมีประวัติทรีย่อยทั้งหมด สำหรับฉันนั่นคือสาเหตุของข้อผิดพลาดที่ไม่ใช่การกรอไปข้างหน้าทันที ฉันยังไม่แน่ใจว่าเหตุใดการแยกทรีย่อยจึงสร้างประวัติที่ถูกตัดทอน
rushmaplelad

คำตอบ:


99

ฉันพบคำตอบในความคิดเห็นของบล็อกนี้https://coderwall.com/p/ssxp5q

หากคุณพบปัญหา "การอัปเดตถูกปฏิเสธเนื่องจากส่วนปลายของสาขาปัจจุบันของคุณอยู่ข้างหลังผสานการเปลี่ยนแปลงระยะไกล (เช่น 'git pull')" ปัญหาเมื่อคุณกำลังผลักดัน (ไม่ว่าด้วยเหตุผลใดก็ตามโดยเฉพาะอย่างยิ่งกับประวัติคอมไพล์) จากนั้นคุณจะต้องซ้อนคำสั่ง git เพื่อให้คุณสามารถบังคับให้ส่งไปที่ heroku ยกตัวอย่างข้างต้น:

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

8
จะรันคำสั่งนี้บน Windows ได้อย่างไร? ฉันได้รับerror: unknown option 'prefix'
pilau

3
น่าอัศจรรย์นี้ช่วยวันของฉันได้ :)
bpipat

1
แม้ว่าจะมีวิธีแก้ปัญหา แต่ก็ไม่ได้อธิบายถึงพฤติกรรม
klimkin

2
จริง แต่การใช้ทรีย่อยส่วนใหญ่ในลักษณะนี้เป็นการผลักไปที่ heroku ทางเดียว โดยทั่วไป heroku ไม่ถือว่าเป็น repo git defacto สำหรับผู้ใช้หลายคนในการผลักและดึงจาก
Eric Woodruff

11
ฉันมีช่วงเวลาที่ยากลำบากในการระบุความสัมพันธ์herokuและpythonappมีคำตอบ การแปลสิ่งนี้จากภาษาเฉพาะของ 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ไม่ได้ส่งต่อไป--ontoยังต้นแบบsplitดังนั้นการดำเนินการจึงต้องทำในสองคำสั่ง! เมื่อเสร็จแล้วฉันเห็นว่าคำสั่งของฉันเหมือนกับคำสั่งในคำตอบอื่น ๆ แต่คำอธิบายแตกต่างกันดังนั้นฉันจะปล่อยไว้ที่นี่ต่อไป]

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
"project-shared" คืออะไร?
Colin D

1
@ColinD "project-shared" คือชื่อของรีโมท เป็นชื่อที่คุณจะเลือกเมื่อกำหนดค่าสถานที่ที่จะดึงข้อมูลและกดไปที่ คุณสามารถรับรายการรีโมทด้วยgit remote -v.
59

@ สิบชั่วโมงที่คุณใช้ไปกับสิ่งนี้ทำให้วันนี้เป็นวันของฉัน ขอบคุณ!
มิถุนายน

1
ฉันพบว่าข้อบกพร่องนี้เกิดขึ้นเสมอเมื่อที่เก็บย่อยของคุณมีไดเร็กทอรีที่มีชื่อเหมือนกับคำนำหน้าเช่นคุณเพิ่มที่เก็บย่อยเป็น "libxxx" ลงในที่เก็บหลักและที่เก็บย่อยของคุณเองก็มีไดเร็กทอรีย่อยชื่อ "libxxx ". ในกรณีนี้ git จะถือว่า commits ในที่เก็บย่อยไม่ถูกต้องเหมือนกับ commits ในที่เก็บหลักและพยายามแยกออก --ontoตัวเลือกที่จะช่วยให้คอมไพล์เพื่อแจ้งกระทำที่มีอยู่แล้วในพื้นที่เก็บข้อมูลย่อย
Cosyn

14

สำหรับแอปประเภท "หน้า GitHub" ที่คุณปรับใช้ทรีย่อย "dist" กับสาขา gh-pages โซลูชันอาจมีลักษณะดังนี้

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

ฉันพูดถึงสิ่งนี้เนื่องจากมันดูแตกต่างจากตัวอย่าง heroku ที่ให้ไว้ข้างต้นเล็กน้อย คุณจะเห็นว่าโฟลเดอร์ "dist" ของฉันมีอยู่ในสาขาหลักของ repo ของฉันจากนั้นฉันก็พุชเป็นทรีย่อยไปยังสาขา gh-pages ซึ่งเป็นต้นกำเนิดด้วย


3

ฉันเคยพบปัญหานี้มาก่อนเช่นกันและนี่คือวิธีที่ฉันแก้ไข

สิ่งที่ฉันพบคือฉันมีสาขาที่ไม่ได้ติดอยู่กับสาขาต้นแบบในท้องถิ่น กิ่งก้านนี้มีอยู่จริงและแขวนอยู่ในความว่างเปล่า project-sharedในกรณีของคุณมันอาจจะเรียกว่า สมมติว่าเป็นกรณีนี้และเมื่อคุณทำ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สาขาเก่า

สิ่งนี้จะเก็บ SHA ก่อนหน้าทั้งหมดของpublic-sharedสาขา สุดท้ายคุณสามารถทำไฟล์

git push project-shared project-shared:master

สมมติว่าคุณมีproject-sharedรีโมทด้วย นี้จะผลักดันให้ท้องถิ่นที่แขวนอยู่ในโมฆะ project-sharedสาขาไปยังสาขาของระยะไกลmasterproject-shared


3

นี่เป็นเพราะข้อ จำกัด ของอัลกอริทึมดั้งเดิม เมื่อจัดการผสานคอมมิตอัลกอริทึมดั้งเดิมจะใช้เกณฑ์ที่เรียบง่ายในการตัดพาเรนต์ที่ไม่เกี่ยวข้องออกไป โดยเฉพาะอย่างยิ่งจะตรวจสอบว่ามีต้นแม่ที่มีต้นไม้เหมือนกันหรือไม่ หากพบพาเรนต์ดังกล่าวจะยุบคอมมิตการผสานและใช้พาเรนต์คอมมิตแทนโดยสมมติว่าพาเรนต์อื่นมีการเปลี่ยนแปลงที่ไม่เกี่ยวข้องกับซับทรี ในบางกรณีอาจส่งผลให้บางส่วนของประวัติลดลงซึ่งมีการเปลี่ยนแปลงโครงสร้างย่อยจริง โดยเฉพาะอย่างยิ่งมันจะทิ้งลำดับของการคอมมิตซึ่งจะแตะต้นไม้ย่อย แต่ส่งผลให้มีค่าต้นไม้ย่อยเหมือนกัน

มาดูตัวอย่าง (ซึ่งคุณสามารถทำซ้ำได้อย่างง่ายดาย) เพื่อทำความเข้าใจวิธีการทำงานให้ดียิ่งขึ้น พิจารณาประวัติต่อไปนี้ (รูปแบบบรรทัดคือ: กระทำ [ต้นไม้] เรื่อง):

% 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ส่วน ยอมรับDและEมีทรีเดียวกัน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

ข้อบกพร่องได้รับการแก้ไข ดู: Contrib / ทรีย่อย: แก้ไขข้อผิดพลาด "แยกทรีย่อย" ที่ข้ามการผสาน github.com/git/git/commit/…
halfmoonhalf

0

คำตอบของ Eric Woodruff ไม่ได้ช่วยฉัน แต่สิ่งต่อไปนี้:

ฉันมักจะ 'git subtree pull' ด้วยตัวเลือก '--squash' ดูเหมือนว่าสิ่งนี้จะทำให้การคืนดีกันยากขึ้นดังนั้นฉันจึงต้องดึงทรีย่อยโดยไม่ต้องทะเลาะกันในครั้งนี้แก้ไขความขัดแย้งบางอย่างแล้วผลักดัน

ฉันต้องเพิ่มว่าการดึงที่ถูกบีบไม่ได้เปิดเผยความขัดแย้งใด ๆ บอกฉันว่าทุกอย่างเรียบร้อยดี


0

อีกวิธีหนึ่งที่ [ง่ายกว่า] คือการเลื่อนส่วนหัวของรีโมตโดยทำการคอมมิตใหม่หากทำได้ หลังจากที่คุณดึงส่วนหัวขั้นสูงนี้ไปที่ทรีย่อยภายในเครื่องแล้วคุณจะสามารถผลักดันจากมันได้อีกครั้ง


1
นี่เป็นวิธีที่ฉันแก้ไขปัญหาเดียวกันกับที่เก็บระยะไกลไม่ซิงค์กับทรีย่อยในเครื่อง
Aidas Bendoraitis

0

คุณสามารถบังคับให้พุชการเปลี่ยนแปลงในเครื่องไปยัง repo ทรีย่อยระยะไกลได้

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

0

พาวเวอร์เชลล์อย่างรวดเร็วสำหรับผู้ใช้ windows โดยใช้โซลูชันของ Chris Jordan

$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.