หากฉันมีความมุ่งมั่นในอดีตที่ชี้ไปที่ผู้ปกครองคนหนึ่ง แต่ฉันต้องการเปลี่ยนผู้ปกครองที่ชี้ไปที่ฉันจะทำอย่างไร
หากฉันมีความมุ่งมั่นในอดีตที่ชี้ไปที่ผู้ปกครองคนหนึ่ง แต่ฉันต้องการเปลี่ยนผู้ปกครองที่ชี้ไปที่ฉันจะทำอย่างไร
คำตอบ:
git rebaseการใช้ มันเป็นคำสั่ง "รับมอบทั่วไปและป๋อมัน / พวกเขาในคำสั่งผู้ปกครอง (ฐาน)" ที่แตกต่างกันใน Git
อย่างไรก็ตามสิ่งที่ควรรู้:
เนื่องจากการกระทำ SHA เกี่ยวข้องกับผู้ปกครองของพวกเขาเมื่อคุณเปลี่ยนผู้ปกครองของการกระทำที่กำหนด SHA ของมันจะเปลี่ยนไปเช่นเดียวกับ SHAs ของการกระทำทั้งหมดที่เกิดขึ้นหลังจากนั้น (ล่าสุดกว่านั้น) ในสายการพัฒนา
หากคุณกำลังทำงานกับคนอื่นและคุณได้ผลักดันการกระทำความผิดในคำถามสู่ที่ที่พวกเขาดึงมันไปแล้วการปรับเปลี่ยนการกระทำนั้นน่าจะเป็นความคิดที่แย่ นี่คือสาเหตุที่ # 1 และทำให้เกิดความสับสนที่เก็บข้อมูลของผู้ใช้รายอื่นจะพบเมื่อพยายามหาสิ่งที่เกิดขึ้นเนื่องจาก SHA ของคุณไม่ตรงกับความมุ่งมั่นของพวกเขาสำหรับ "เดียวกัน" (ดูส่วน "การกู้คืนจากการกู้คืนของ UPSTREAM" ของหน้า man ที่เชื่อมโยงเพื่อดูรายละเอียด)
ที่กล่าวว่าหากคุณอยู่ในสาขาที่มีข้อผูกพันบางอย่างที่คุณต้องการย้ายไปที่ผู้ปกครองใหม่ก็จะมีลักษณะเช่นนี้:
git rebase --onto <new-parent> <old-parent>
ที่จะย้ายทุกอย่างหลังจาก <old-parent>ในสาขาปัจจุบันเพื่อนั่งด้านบนของ<new-parent>แทน
--ontoใช้สำหรับ! เอกสารเป็นสิ่งคลุมเครือให้ฉันอย่างสมบูรณ์แม้หลังจากอ่านคำตอบนี้!
git rebase --onto <new-parent> <old-parent>บอกฉันทุกอย่างเกี่ยวกับวิธีใช้rebase --ontoคำถามและเอกสารอื่น ๆ ที่ฉันเคยอ่านมาแล้ว
rebase this commit on that one git rebase --on <new-parent> <commit>การใช้ผู้ปกครองเก่าที่นี่ไม่สมเหตุสมผลสำหรับฉัน
git rebase <new-parent>กรณีที่คุณจะอธิบายจะถูกจัดการโดย
หากปรากฎว่าคุณต้องหลีกเลี่ยงการทำข้อผูกพันใหม่ (เช่นเนื่องจากการเขียนประวัติจะไม่สามารถป้องกันได้) ดังนั้นคุณสามารถใช้การแทนที่ git (มีใน Git 1.6.5 และใหม่กว่า)
# …---o---A---o---o---…
#
# …---o---B---b---b---…
#
# We want to transplant B to be "on top of" A.
# The tree of descendants from B (and A) can be arbitrarily complex.
replace_first_parent() {
old_parent=$(git rev-parse --verify "${1}^1") || return 1
new_parent=$(git rev-parse --verify "${2}^0") || return 2
new_commit=$(
git cat-file commit "$1" |
sed -e '1,/^$/s/^parent '"$old_parent"'$/parent '"$new_parent"'/' |
git hash-object -t commit -w --stdin
) || return 3
git replace "$1" "$new_commit"
}
replace_first_parent B A
# …---o---A---o---o---…
# \
# C---b---b---…
#
# C is the replacement for B.
เมื่อมีการแทนที่ด้านบนแล้วคำขอใด ๆ สำหรับวัตถุ B จะส่งคืนวัตถุ C จริง ๆ เนื้อหาของ C จะเหมือนกับเนื้อหาของ B ทุกประการยกเว้นผู้ปกครองคนแรก (ผู้ปกครองคนเดียวกัน (ยกเว้นผู้แรก) ต้นเดียวกัน กระทำข้อความเดียวกัน)
การเปลี่ยนใช้งานโดยค่าเริ่มต้น แต่สามารถเปลี่ยนได้โดยใช้--no-replace-objectsตัวเลือกเพื่อgit (ก่อนชื่อคำสั่ง) หรือโดยการตั้งค่าGIT_NO_REPLACE_OBJECTSตัวแปรสภาพแวดล้อม สามารถใช้การแทนที่ได้โดยการกดrefs/replace/*(นอกเหนือจากปกติrefs/heads/*)
หากคุณไม่ชอบคอมมิชชัน (ทำกับsedด้านบน) จากนั้นคุณสามารถสร้างการคอมมิททดแทนโดยใช้คำสั่งระดับสูงกว่า:
git checkout B~0
git reset --soft A
git commit -C B
git replace B HEAD
git checkout -
ความแตกต่างใหญ่คือลำดับนี้ไม่แพร่กระจายผู้ปกครองเพิ่มเติมถ้า B เป็นผสานกระทำ
refs/replace/ลำดับชั้นการอ้างอิง หากคุณต้องการทำเพียงครั้งเดียวคุณสามารถทำได้git push yourremote 'refs/replace/*'ในที่เก็บข้อมูลต้นทางและgit fetch yourremote 'refs/replace/*:refs/replace/*'ในที่เก็บข้อมูลปลายทาง หากคุณต้องการทำหลาย ๆ ครั้งคุณสามารถเพิ่ม refspecs เหล่านั้นลงในremote.yourremote.pushตัวแปร config (ในแหล่งเก็บข้อมูลต้นฉบับ) และremote.yourremote.fetchตัวแปร config (ในที่เก็บปลายทาง)
git replace --graftsวิธีที่ง่ายที่จะทำเช่นนี้คือการใช้ ไม่แน่ใจว่าเมื่อสิ่งนี้ถูกเพิ่มเข้ามา แต่มันมีจุดประสงค์ทั้งหมดเพื่อสร้างการแทนที่ที่เหมือนกับการส่งมอบBแต่ใช้กับพาเรนต์ที่ระบุ
โปรดทราบว่าการเปลี่ยนการคอมมิทใน Git นั้นจะต้องเปลี่ยนคอมมิทที่ตามมาทั้งหมด สิ่งนี้จะหมดกำลังใจหากคุณได้เผยแพร่ประวัติส่วนนี้และใครบางคนอาจสร้างงานของพวกเขาในประวัติศาสตร์ที่เคยเป็นมาก่อนการเปลี่ยนแปลง
วิธีการแก้ปัญหาอื่นเพื่อgit rebaseกล่าวถึงในการตอบสนองของแอมเบอร์คือการใช้ตัดกลไก (ดูความหมายของ Git grafts ในGit คำศัพท์และเอกสารของ.git/info/graftsแฟ้มในGit Repository เค้าโครงเอกสาร) ไปยังผู้ปกครองการเปลี่ยนแปลงของการกระทำ, การตรวจสอบว่ามันไม่ได้ทำสิ่งที่ถูกต้องกับโปรแกรมดูประวัติ ( gitk, git log --graphฯลฯ ) จากนั้นใช้git filter-branch(ตามที่อธิบายไว้ในส่วน "ตัวอย่าง" ของ manpage) เพื่อทำให้ถาวร (และจากนั้นลบการรับสินบนและเลือกที่จะลบ refs ดั้งเดิมที่สำรองไว้git filter-branchหรือ reclone repository):
echo "$ commit-id $ graft-id" >> .git / info / grafts git filter-branch $ graft-id..HEAD
บันทึก !!! โซลูชันนี้แตกต่างจากโซลูชัน rebaseซึ่งgit rebaseจะเป็นการลด / เปลี่ยนการปลูกถ่ายในขณะที่โซลูชันที่ใช้กราฟต์จะทำการซ่อมแซมตามปกติโดยไม่คำนึงถึงความแตกต่างระหว่างผู้ปกครองเก่ากับผู้ปกครองใหม่!
git replaceมีการแทนที่ git git (สมมติว่าคุณมี git 1.6.5 หรือใหม่กว่า)
git-replace+ git-filter-branchไม่ได้ ในการทดสอบของฉันfilter-branchดูเหมือนว่าจะเคารพการแทนที่ทำให้ต้นไม้ทั้งหมดใหม่
git filter-branchเขียนประวัติใหม่เคารพ grafts และ replacements (แต่ในการแทนที่ประวัติศาสตร์ rewritten จะเป็น moot); การกดจะส่งผลให้ไม่มีการเปลี่ยนไปข้างหน้า git replaceสร้างทดแทนในสถานที่ถ่ายโอนได้; การส่งข้อมูลจะเป็นการส่งต่อที่รวดเร็ว แต่คุณจะต้องผลักดันrefs/replaceเพื่อโอนการเปลี่ยนเพื่อให้มีการแก้ไขประวัติ HTH
เพื่อชี้แจงคำตอบข้างต้นและเสียบสคริปต์ของตัวเองอย่างไร้ยางอาย:
ขึ้นอยู่กับว่าคุณต้องการ "rebase" หรือ "reparent" rebaseที่แนะนำโดยแอมเบอร์ , ย้ายรอบdiffs การซ่อมแซมตามคำแนะนำของจาคุบและคริสเคลื่อนไปรอบ ๆภาพรวมของต้นไม้ทั้งหมด หากคุณต้องการที่จะซ่อมแซมฉันขอแนะนำให้ใช้git reparentแทนการทำงานด้วยตนเอง
สมมติว่าคุณมีภาพทางด้านซ้ายและคุณต้องการให้มันดูเหมือนภาพด้านขวา:
C'
/
A---B---C A---B---C
ทั้งการรีบูตและการรีเอเจนต์จะสร้างภาพเดียวกัน แต่คำจำกัดความของC'ความแตกต่าง ด้วยgit rebase --onto A B, C'จะไม่มีการเปลี่ยนแปลงใด ๆ Bที่นำโดย ด้วยgit reparent -p A, C'จะเหมือนกับC(ยกเว้นว่าBจะไม่อยู่ในประวัติศาสตร์)
reparentสคริปต์ มันทำงานได้อย่างสวยงาม ขอแนะนำ ฉันขอแนะนำให้สร้างbranchป้ายกำกับใหม่และไปยังcheckoutสาขานั้นก่อนที่จะโทร (เนื่องจากฉลากสาขาจะย้าย)
คำตอบที่แน่นอนของ @Jubub ช่วยฉันเมื่อหลายชั่วโมงก่อนเมื่อฉันลองสิ่งเดียวกันกับ OP
อย่างไรก็ตามgit replace --graftตอนนี้เป็นวิธีแก้ปัญหาที่ง่ายกว่าเกี่ยวกับกราฟ ปัญหาที่สำคัญของการแก้ปัญหานั้นก็คือสาขาตัวกรองทำให้ฉันหลวมทุกสาขาที่ไม่ได้รวมเข้ากับสาขา HEAD จากนั้นgit filter-repoทำงานได้อย่างสมบูรณ์แบบและไร้ที่ติ
$ git replace --graft <commit> <new parent commit>
$ git filter-repo --force
ข้อมูลเพิ่มเติม: ชำระเงินในส่วน "การทำกราฟประวัติ" ในเอกสาร