แทรกการกระทำก่อนที่จะคอมมิทใน Git?


231

ฉันถามก่อนเกี่ยวกับวิธีการสควอชสองคอมมิชชันในที่เก็บคอมไพล์

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

ดังนั้นฉันควรจะผ่านความเจ็บปวดเพียงครั้งเดียวและจากนั้นสามารถใช้การปฏิเสธแบบโต้ตอบมาตรฐานได้ตลอดไป

สิ่งที่ฉันต้องการจะทำคือการมีความมุ่งมั่นเริ่มต้นว่างเปล่าที่มีอยู่เพียงเพื่อวัตถุประสงค์ในการเป็นคนแรก ไม่มีรหัสไม่มีอะไรเลย เพียงแค่สละพื้นที่เพื่อให้สามารถเป็นฐานสำหรับการรีบูต

คำถามของฉันคือการมีที่เก็บข้อมูลที่มีอยู่ฉันจะดำเนินการเกี่ยวกับการใส่ข้อมูลคอมมิชชันใหม่ที่ว่างเปล่าไว้ก่อนหน้าแรกและเปลี่ยนคนอื่นให้ส่งต่อได้อย่างไร


3
;) ฉันคิดว่ามันรับประกันคำตอบต่อไป ฉันกำลังสำรวจหลาย ๆ วิธีที่อาจทำให้เสียสติได้โดยการแก้ไขประวัติศาสตร์อย่างหมกมุ่น ไม่ต้องกังวลไม่ใช่ที่เก็บข้อมูลที่แชร์
kch

11
จากเครื่องมือแก้ไขประวัติผู้คลั่งไคล้คนหนึ่งสู่อีกคนหนึ่งขอบคุณที่โพสต์คำถาม! ; D
Marco

10
ในการป้องกันของ @ kch เหตุผลหนึ่งที่ถูกต้องตามกฎหมายคือเหตุผลที่ฉันพบว่าตัวเองใน: การเพิ่มสแนปช็อตของเวอร์ชันประวัติศาสตร์ที่ไม่เคยถูกจับใน repo
McStopher เก่า

4
ฉันมีอีกเหตุผลที่ถูกต้อง! การเพิ่มการกระทำที่ว่างเปล่าก่อนที่จะเป็นครั้งแรกเพื่อให้สามารถ rebase การกระทำแรกและลบไบนารี bloat ที่เพิ่มในการกระทำเริ่มต้นของพื้นที่เก็บข้อมูล (:
pospi

คำตอบ:


314

มี 2 ​​ขั้นตอนในการบรรลุเป้าหมายนี้:

  1. สร้างการมอบหมายที่ว่างเปล่าใหม่
  2. เขียนประวัติใหม่เพื่อเริ่มจากการกระทำที่ว่างเปล่านี้

เราจะใส่ค่าคอมมิชชันว่างเปล่าใหม่ไว้ที่สาขาชั่วคราวnewrootเพื่อความสะดวก

1. สร้างการมอบหมายที่ว่างเปล่าใหม่

มีหลายวิธีที่คุณสามารถทำได้

ใช้เพียงแค่การประปา

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

  1. สร้างวัตถุต้นไม้สำหรับไดเรกทอรีว่าง:

    tree=`git hash-object -wt tree --stdin < /dev/null`
    
  2. ล้อมรอบกระทำ:

    commit=`git commit-tree -m 'root commit' $tree`
    
  3. สร้างการอ้างอิงถึงมัน:

    git branch newroot $commit
    

แน่นอนคุณสามารถจัดเรียงขั้นตอนทั้งหมดเป็นหนึ่งซับถ้าคุณรู้ว่าเปลือกของคุณดีพอ

โดยไม่ต้องประปา

ด้วยคำสั่งเครื่องเคลือบปกติคุณจะไม่สามารถสร้างการมอบหมายที่ว่างเปล่าโดยไม่ตรวจสอบจากnewrootสาขาและอัปเดตดัชนีและสำเนาการทำงานซ้ำ ๆ ได้โดยไม่มีเหตุผล แต่บางคนอาจเข้าใจง่ายกว่านี้:

git checkout --orphan newroot
git rm -rf .
git clean -fd
git commit --allow-empty -m 'root commit'

โปรดทราบว่าใน Git เวอร์ชั่นเก่าที่ไม่มี--orphanสวิตช์checkoutคุณต้องเปลี่ยนบรรทัดแรกด้วย:

git symbolic-ref HEAD refs/heads/newroot

2. เขียนซ้ำประวัติศาสตร์เพื่อเริ่มต้นจากการกระทำที่ว่างเปล่านี้

คุณมีสองตัวเลือกที่นี่: การรีบูทหรือเขียนประวัติใหม่ทั้งหมด

rebasing

git rebase --onto newroot --root master

สิ่งนี้มีข้อดีของความเรียบง่าย อย่างไรก็ตามมันจะอัปเดตชื่อผู้เดินทางและวันที่ในทุกการกระทำล่าสุดในสาขา

นอกจากนี้ด้วยประวัติกรณีขอบบางอย่างก็อาจล้มเหลวเนื่องจากการรวมความขัดแย้ง - แม้ว่าคุณจะ reboots ในการกระทำที่มีอะไร

เขียนประวัติศาสตร์ใหม่

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

git replace <currentroot> --graft newroot
git filter-branch master

การเขียนใหม่เกิดขึ้นในขั้นตอนที่สอง มันเป็นขั้นตอนแรกที่ต้องการคำอธิบาย สิ่งที่git replaceไม่สามารถจะบอก Git ว่าเมื่อใดก็ตามที่เห็นว่ามีการอ้างอิงไปยังวัตถุที่คุณต้องการแทนที่, Git แทนควรดูที่การเปลี่ยนของวัตถุว่า

เมื่อใช้--graftสวิตช์คุณกำลังบอกบางสิ่งที่แตกต่างจากปกติเล็กน้อย คุณกำลังพูดว่ายังไม่มีวัตถุทดแทน แต่คุณต้องการแทนที่<currentroot>วัตถุยืนยันด้วยสำเนาที่แน่นอนของตัวเองยกเว้นการกระทำหลักของการแทนที่ควรเป็นวัตถุที่คุณระบุไว้ (เช่นการnewrootกระทำ ) จากนั้นgit replaceดำเนินการต่อและสร้างการกระทำนี้ให้คุณแล้วประกาศว่าการกระทำนั้นเป็นการแทนที่การกระทำดั้งเดิมของคุณ

ตอนนี้ถ้าคุณทำgit logคุณจะเห็นว่าสิ่งที่มีอยู่แล้วมีลักษณะตามที่คุณต้องการให้พวกเขา: newrootสาขาเริ่มต้น

อย่างไรก็ตามโปรดทราบgit replace ว่าไม่ได้แก้ไขประวัติจริง - และไม่เผยแพร่ออกจากที่เก็บของคุณ มันเพิ่มการเปลี่ยนเส้นทางในเครื่องไปยังที่เก็บข้อมูลของคุณจากวัตถุหนึ่งไปยังอีกวัตถุหนึ่ง สิ่งนี้หมายความว่าไม่มีใครเห็นผลของการเปลี่ยนนี้ - เพียงคุณเท่านั้น

นั่นเป็นเหตุผลที่filter-branchจำเป็นต้องมีขั้นตอน เมื่อgit replaceคุณสร้างสำเนาที่แน่นอนพร้อมกับพาเรนต์ที่ปรับแล้วให้คอมมิตสำหรับรูทคอมมิท git filter-branchจากนั้นทำซ้ำกระบวนการนี้สำหรับการกระทำทั้งหมดต่อไปนี้เช่นกัน นี่คือที่ที่ประวัติศาสตร์ได้รับการเขียนใหม่เพื่อให้คุณสามารถแบ่งปันได้


1
ว่า--onto newrootเป็นตัวเลือกที่ซ้ำซ้อน; คุณสามารถทำได้โดยไม่ได้เพราะการโต้แย้งคุณผ่านมันnewrootเป็นเช่นเดียวกับการโต้แย้งต้นน้ำ newroot-
wilhelmtell

7
ทำไมไม่ใช้พอร์ซเลนแทนคำสั่งประปา? ฉันจะแทนที่git symbol-ref HEAD refs / heads / newrootด้วยgit checkout --orphan newroot
albfan

4
@nenopera: เพราะคำตอบนี้ถูกเขียนก่อนที่จะgit-checkoutมีสวิตช์นั้น ฉันได้อัปเดตเป็นวิธีที่กล่าวถึงก่อนแล้วขอขอบคุณสำหรับตัวชี้
อริสโตเติล Pagaltzis

1
หาก newroot ของคุณไม่ว่างให้ใช้git rebase --merge -s recursive -X theirs --onto newroot --root masterเพื่อแก้ไขข้อขัดแย้งทั้งหมดโดยอัตโนมัติ (ดูคำตอบนี้ ) @AlexanderKuzin
ผู้ใช้

1
@Geremia คุณสามารถแก้ไขการคอมมิชชันล่าสุดเท่านั้นดังนั้นหากที่เก็บของคุณมีการคอมมิชชันรูทเท่านั้นมันอาจใช้งานได้มิฉะนั้นคุณต้องรีบูตคอมมิชชันอื่นทั้งหมดใน repo ด้านบนสุดของคอมมิชชันที่แก้ไข แต่ถึงตอนนั้นหัวข้อก็บอกเป็นนัยว่าคุณไม่ต้องการเปลี่ยนการคอมมิทรูท แต่ต้องการแทรกรูทอีกอันก่อนรูทที่มีอยู่แทน
ผู้ใช้

30

ผสานคำตอบของอริสโตเติล Pagaltzis และ Uwe Kleine-Königและความคิดเห็นของ Richard Bronosky

git symbolic-ref HEAD refs/heads/newroot
git rm --cached -r .
git clean -f -d
# touch .gitignore && git add .gitignore # if necessary
git commit --allow-empty -m 'initial'
git rebase --onto newroot --root master
git branch -d newroot

(เพียงใส่ทุกอย่างไว้ในที่เดียว)


มันยอดเยี่ยมมาก มันจะดีถ้านี่อาจเป็นสิ่งที่ rebit คอมไพล์ -i - รูตทำภายใน
aredridel

ใช่ฉันประหลาดใจที่พบว่ามันไม่
Antony Hatchkins

ฉันต้องเปลี่ยนคำสั่ง rebase เป็นgit rebase newroot masterเพราะข้อผิดพลาด
marbel82

@ antony-hatchkins ขอบคุณสำหรับสิ่งนี้ ฉันมี repo คอมไพล์ที่มีอยู่และ (ด้วยเหตุผลต่าง ๆ ที่ฉันจะไม่เข้าไปที่นี่) ฉันพยายามที่จะผนวกคอมไพล์ที่ไม่ว่างเปล่ากระทำเป็นครั้งแรกของฉัน ดังนั้นฉันจึงแทนที่คอมมิทคอมอนุญาต --allow-empty -m 'initial' ด้วย git add.; คอมไพล์กระทำ -m "laravel เริ่มต้นกระทำ"; git push; จากนั้นขั้นตอนการรีบูตนี้: git rebase --onto newroot - มาสเตอร์รูทล้มเหลวโดยมีความขัดแย้งในการรวม TON จำนวนมาก คำแนะนำใด ๆ? : ((
kp123

@ kp123 ลองคอมมิชชันที่ว่างเปล่า :)
Antony Hatchkins

12

ฉันชอบคำตอบของอริสโตเติล แต่พบว่าสำหรับที่เก็บขนาดใหญ่ (> 5000 การกระทำ) ตัวกรองสาขาทำงานได้ดีกว่าการรีบูตด้วยเหตุผลหลายประการ 1) มันเร็วกว่า 2) มันไม่ต้องการการแทรกแซงจากมนุษย์เมื่อเกิดข้อขัดแย้งในการรวม 3) มันสามารถเขียนแท็ก - รักษาพวกเขา โปรดทราบว่าตัวกรองสาขาใช้งานได้เนื่องจากไม่มีคำถามเกี่ยวกับเนื้อหาของการส่งแต่ละครั้ง - มันเหมือนกันกับ 'rebase' ก่อนหน้านี้

ขั้นตอนของฉันคือ:

# first you need a new empty branch; let's call it `newroot`
git symbolic-ref HEAD refs/heads/newroot
git rm --cached -r .
git clean -f -d

# then you apply the same steps
git commit --allow-empty -m 'root commit'

# then use filter-branch to rebase everything on newroot
git filter-branch --parent-filter 'sed "s/^\$/-p <sha of newroot>/"' --tag-name-filter cat master

โปรดทราบว่าตัวเลือก '--tag-name-filter cat' หมายความว่าแท็กจะถูกเขียนใหม่เพื่อให้ชี้ไปที่คอมมิทที่สร้างขึ้นใหม่


สิ่งนี้ไม่ได้ช่วยในการสร้างการยอมรับที่ไม่ว่างเปล่าซึ่งเป็นกรณีการใช้งานที่น่าสนใจ
ceztko

เมื่อเปรียบเทียบกับโซลูชันอื่น ๆ คุณมีผลข้างเคียงที่ไม่สำคัญเพียงอย่างเดียวนั่นคือการเปลี่ยนแปลงแฮช แต่ประวัติทั้งหมดยังคงไม่ถูกแตะต้อง ขอบคุณ!
Vladyslav Savchenko

5

ฉันใช้คำตอบของอริสโตเติลและเคนต์สำเร็จแล้ว:

# first you need a new empty branch; let's call it `newroot`
git checkout --orphan newroot
git rm -rf .
git commit --allow-empty -m 'root commit'
git filter-branch --parent-filter \
'sed "s/^\$/-p <sha of newroot>/"' --tag-name-filter cat -- --all
# clean up
git checkout master
git branch -D newroot
# make sure your branches are OK first before this...
git for-each-ref --format="%(refname)" refs/original/ | \
xargs -n 1 git update-ref -d

สิ่งนี้จะเขียนทุกสาขา (ไม่ใช่แค่master) นอกเหนือจากแท็ก


บรรทัดสุดท้ายนี้ทำอะไร
Diederick C. Niehorster

มันค้นหาผ่านrefs/original/และลบการอ้างอิงแต่ละครั้ง ผู้อ้างอิงที่ถูกลบควรถูกอ้างอิงโดยสาขาอื่นแล้วดังนั้นพวกเขาจึงไม่หายไปจริงๆเพียงแค่refs/original/ถูกลบออก
ldav1s

สิ่งนี้ใช้ได้สำหรับฉัน นอกจากนี้ฉันเคยtimedatectl set-time '2017-01-01 00:00:00'บอกnewrootเวลาเก่า
chrm

4

git rebase --root --onto $emptyrootcommit

ควรทำเคล็ดลับง่ายๆ


3
$emptyrootcommitตัวแปรเชลล์ที่ขยายออกไปเป็นอะไรแน่นอน?
Flimm

@Flimm: $ emptyrootcommit คือ sha1 ของเปล่าที่กระทำว่าโปสเตอร์ต้นฉบับดูเหมือนว่ามีอยู่แล้ว
Uwe Kleine-König

4

ฉันคิดว่าการใช้git replaceและgit filter-branchเป็นทางออกที่ดีกว่าการใช้git rebase:

  • ประสิทธิภาพที่ดีขึ้น
  • ง่ายขึ้นและมีความเสี่ยงน้อยลง (คุณสามารถยืนยันผลลัพธ์ของคุณในแต่ละขั้นตอนและเลิกทำในสิ่งที่คุณทำ ... )
  • ทำงานได้ดีกับหลายสาขาพร้อมรับประกันผลลัพธ์

แนวคิดเบื้องหลังคือ:

  • สร้างการมอบหมายที่ว่างเปล่าใหม่ในอดีต
  • แทนที่รูทเก่าที่คอมมิชชันด้วยคอมมิทที่คล้ายกันยกเว้นว่าการคอมมิทรูทใหม่จะถูกเพิ่มเป็นพาเรนต์
  • ตรวจสอบว่าทั้งหมดเป็นไปตามที่คาดหวังและเรียกใช้ git filter-branch
  • ตรวจสอบอีกครั้งว่าทั้งหมดเป็นปกติและล้างไฟล์ git ที่ไม่ต้องการ

นี่คือสคริปต์สำหรับ 2 ขั้นตอนแรก:

#!/bin/bash
root_commit_sha=$(git rev-list --max-parents=0 HEAD)
git checkout --force --orphan new-root
find . -path ./.git -prune -o -exec rm -rf {} \; 2> /dev/null
git add -A
GIT_COMMITTER_DATE="2000-01-01T12:00:00" git commit --date==2000-01-01T12:00:00 --allow-empty -m "empty root commit"
new_root_commit_sha=$(git rev-parse HEAD)

echo "The commit '$new_root_commit_sha' will be added before existing root commit '$root_commit_sha'..."

parent="parent $new_root_commit_sha"
replacement_commit=$(
 git cat-file commit $root_commit_sha | sed "s/author/$parent\nauthor/" |
 git hash-object -t commit -w --stdin
) || return 3
git replace "$root_commit_sha" "$replacement_commit"

คุณสามารถเรียกใช้สคริปต์นี้โดยไม่มีความเสี่ยง (แม้ว่าการทำสำรองข้อมูลก่อนที่จะทำการกระทำที่คุณไม่เคยทำมาก่อนเป็นความคิดที่ดี;)) และถ้าผลที่ได้คือไม่ได้เป็นคนที่คาดหวังเพียงแค่ลบไฟล์ที่สร้างในโฟลเดอร์.git/refs/replaceและลองอีกครั้ง; )

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

git filter-branch -- --all

ตอนนี้คุณต้องดูประวัติ 2 อันเก่าและใหม่ (ดูความช่วยเหลือfilter-branchสำหรับข้อมูลเพิ่มเติม) คุณสามารถเปรียบเทียบ 2 และตรวจสอบอีกครั้งว่าทุกอย่างเรียบร้อย หากคุณพอใจให้ลบไฟล์ที่ไม่ต้องการเพิ่มเติม:

rm -rf ./.git/refs/original
rm -rf ./.git/refs/replace

คุณสามารถกลับไปที่masterสาขาของคุณและลบสาขาชั่วคราว:

git checkout master
git branch -D new-root

ตอนนี้สิ่งที่ควรทำ;)


3

ฉันตื่นเต้นและเขียนสคริปต์ที่ดีเวอร์ชัน 'idempotent' นี้ ... มันจะแทรกค่าคอมมิชชันเดียวกันเสมอและถ้าคุณเรียกใช้สองครั้งมันจะไม่เปลี่ยนแฮชการกระทำของคุณในแต่ละครั้ง ดังนั้นนี่คือฉันใช้ในgit-insert-empty-root :

#!/bin/sh -ev
# idempotence achieved!
tmp_branch=__tmp_empty_root
git symbolic-ref HEAD refs/heads/$tmp_branch
git rm --cached -r . || true
git clean -f -d
touch -d '1970-01-01 UTC' .
GIT_COMMITTER_DATE='1970-01-01T00:00:00 +0000' git commit \
  --date='1970-01-01T00:00:00 +0000' --allow-empty -m 'initial'
git rebase --committer-date-is-author-date --onto $tmp_branch --root master
git branch -d $tmp_branch

มันคุ้มค่ากับความซับซ้อนที่เพิ่มขึ้นไหม? อาจจะไม่ แต่ฉันจะใช้อันนี้

SHOULD นี้ยังอนุญาตให้ดำเนินการนี้กับสำเนาที่ลอกเลียนแบบของ repo หลายชุดและลงเอยด้วยผลลัพธ์เดียวกันดังนั้นพวกเขาจึงยังคงใช้งานร่วมกันได้ ... การทดสอบ ... ใช่มันทำงานได้ แต่ต้องลบและเพิ่ม รีโมตอีกครั้งเช่น:

git remote rm origin
git remote add --track master user@host:path/to/repo

3

หากต้องการเพิ่มการมอบหมายที่ว่างเปล่าที่จุดเริ่มต้นของที่เก็บหากคุณลืมที่จะสร้างการมอบหมายที่ว่างเปล่าทันทีหลังจาก "git init":

git rebase --root --onto $(git commit-tree -m 'Initial commit (empty)' 4b825dc642cb6eb9a060e54bf8d69288fbee4904)

1
4b825dc ... เป็นแฮชของต้นไม้ที่ว่างเปล่า: stackoverflow.com/questions/9765453/ …
mrks

2

นี่คือสิ่งที่ฉันคิดขึ้นมา:

# Just setting variables on top for clarity.
# Set this to the path to your original repository.
ORIGINAL_REPO=/path/to/original/repository

# Create a new repository…
mkdir fun
cd fun
git init
# …and add an initial empty commit to it
git commit --allow-empty -m "The first evil."

# Add the original repository as a remote
git remote add previous $ORIGINAL_REPO
git fetch previous

# Get the hash for the first commit in the original repository
FIRST=`git log previous/master --pretty=format:%H  --reverse | head -1`
# Cherry-pick it
git cherry-pick $FIRST
# Then rebase the remainder of the original branch on top of the newly 
# cherry-picked, previously first commit, which is happily the second 
# on this branch, right after the empty one.
git rebase --onto master master previous/master

# rebase --onto leaves your head detached, I don't really know why)
# So now you overwrite your master branch with the newly rebased tree.
# You're now kinda done.
git branch -f master
git checkout master
# But do clean up: remove the remote, you don't need it anymore
git remote rm previous

2

นี่คือbashสคริปต์ของฉันตามคำตอบของKentพร้อมการปรับปรุง:

  • มันตรวจสอบสาขาดั้งเดิมไม่ใช่เพียง แต่masterเมื่อเสร็จแล้ว
  • ฉันพยายามหลีกเลี่ยงสาขาชั่วคราว แต่git checkout --orphanทำงานได้กับสาขาเท่านั้นไม่ใช่สถานะส่วนหัวดังนั้นจึงตรวจสอบได้นานพอที่จะทำให้รูทใหม่ยอมรับและลบออกแล้ว
  • มันใช้แฮชของรูทใหม่ที่คอมมิทในช่วงfilter-branch(เคนต์ทิ้งตัวยึดไว้ที่นั่นเพื่อทดแทนด้วยตนเอง);
  • การfilter-branchดำเนินการเขียนเฉพาะสาขาในพื้นที่เท่านั้น
  • ผู้เขียนและข้อมูลเมตาของผู้ส่งข้อมูลเป็นมาตรฐานเพื่อให้การคอมมิทรูทเหมือนกันข้ามที่เก็บ

#!/bin/bash

# Save the current branch so we can check it out again later
INITIAL_BRANCH=`git symbolic-ref --short HEAD`
TEMP_BRANCH='newroot'

# Create a new temporary branch at a new root, and remove everything from the tree
git checkout --orphan "$TEMP_BRANCH"
git rm -rf .

# Commit this empty state with generic metadata that will not change - this should result in the same commit hash every time
export GIT_AUTHOR_NAME='nobody'
export GIT_AUTHOR_EMAIL='nobody@example.org'
export GIT_AUTHOR_DATE='2000-01-01T00:00:00+0000'
export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
git commit --allow-empty -m 'empty root'
NEWROOT=`git rev-parse HEAD`

# Check out the commit we just made and delete the temporary branch
git checkout --detach "$NEWROOT"
git branch -D "$TEMP_BRANCH"

# Rewrite all the local branches to insert the new root commit, delete the 
# original/* branches left behind, and check out the rewritten initial branch
git filter-branch --parent-filter "sed \"s/^\$/-p $NEWROOT/\"" --tag-name-filter cat -- --branches
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
git checkout "$INITIAL_BRANCH"

2

ในการสลับรูทการส่งมอบ:

ขั้นแรกให้สร้างความมุ่งมั่นที่คุณต้องการเป็นคนแรก

ประการที่สองสลับลำดับของการกระทำโดยใช้:

git rebase -i - รูท

ตัวแก้ไขจะปรากฏพร้อมคอมมิตจนกว่ารูทจะยอมรับเช่น:

เลือกข้อความรูทเก่า 1234

เลือก 0294 ความมุ่งมั่นที่อยู่ตรงกลาง

เลือก 5678 ยอมรับว่าคุณต้องการใส่ที่รูท

จากนั้นคุณสามารถใส่คำสั่งที่คุณต้องการก่อนโดยวางมันลงในบรรทัดแรก ในตัวอย่าง:

เลือก 5678 ยอมรับว่าคุณต้องการใส่ที่รูท

เลือกข้อความรูทเก่า 1234

เลือก 0294 ความมุ่งมั่นที่อยู่ตรงกลาง

ออกจากตัวแก้ไขคำสั่งการส่งจะมีการเปลี่ยนแปลง

PS: หากต้องการเปลี่ยนโปรแกรมแก้ไข git ให้เรียกใช้:

git config - global core.editor name_of_the_editor_program_you_want_to_use


1
ตอนนี้การรีบูตมี - รูทนี่คือทางออกที่เรียบร้อยที่สุด
Ross Burton

1

รวมล่าสุดและยิ่งใหญ่ที่สุด ไม่มีผลข้างเคียงไม่มีข้อขัดแย้งการเก็บแท็ก

git log --reverse

tree=`git hash-object -wt tree --stdin < /dev/null`
commit=`git commit-tree -m 'Initialize empty repository' $tree`
echo $commit # copy below, interpolation didn't work for me

git filter-branch --parent-filter 'sed "s/^\$/-p <commit>/"' --tag-name-filter cat master

git log --reverse

โปรดทราบว่าใน GitHub คุณจะสูญเสียข้อมูลในการเรียกใช้ CI และการประชาสัมพันธ์อาจทำให้เกิดความสับสนเว้นแต่สาขาอื่นจะได้รับการแก้ไขเช่นกัน


0

ทำตามคำตอบอริสโตเติล Pagaltzis และอื่น ๆ แต่ใช้คำสั่งง่ายขึ้น

zsh% git checkout --orphan empty     
Switched to a new branch 'empty'
zsh% git rm --cached -r .
zsh% git clean -fdx
zsh% git commit --allow-empty -m 'initial empty commit'
[empty (root-commit) 64ea894] initial empty commit
zsh% git checkout master
Switched to branch 'master'
zsh% git rebase empty
First, rewinding head to replay your work on top of it...
zsh% git branch -d empty 
Deleted branch empty (was 64ea894).

โปรดทราบว่า repo ของคุณไม่ควรมีการปรับเปลี่ยนในท้องถิ่นรอส่ง
หมายเหตุgit checkout --orphanจะใช้งานได้กับ git เวอร์ชันใหม่ฉันเดา
โปรดทราบว่าส่วนใหญ่git statusให้คำแนะนำที่เป็นประโยชน์


-6

เริ่มพื้นที่เก็บข้อมูลใหม่

ตั้งวันที่ของคุณกลับเป็นวันที่เริ่มต้นที่คุณต้องการ

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

เมื่อคุณมาถึงวันนี้ให้สลับที่เก็บและทำเสร็จแล้ว

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

นั่นจะทำให้มันดีขึ้นเมื่อคุณตัดสินใจว่าคุณต้องการให้อดีตเกิดขึ้นอีกทางหนึ่งสัปดาห์นับจากนี้


ฉันมีความรู้สึกที่ไม่ดีเกี่ยวกับวิธีแก้ปัญหาที่ต้องการให้คุณยุ่งกับวันที่ของระบบ แต่คุณให้ความคิดกับฉันซึ่งฉันพัฒนาขึ้นมาเล็กน้อยและมันก็ใช้ได้ ดังนั้นขอบคุณ
kch

-7

ฉันรู้ว่าโพสต์นี้เก่า แต่หน้านี้เป็นหน้าแรกเมื่อ Googling "แทรกการคอมไพล์คอมไพล์"

ทำไมการทำสิ่งเรียบง่ายจึงซับซ้อน

คุณมี ABC และคุณต้องการ ABZC

  1. git rebase -i trunk (หรืออะไรก่อน B)
  2. เปลี่ยนตัวเลือกเพื่อแก้ไขในบรรทัด B
  3. ทำการเปลี่ยนแปลงของคุณ: git add ..
  4. git commit( git commit --amendซึ่งจะแก้ไข B และไม่สร้าง Z)

[คุณสามารถสร้างได้มากgit commitเท่าที่คุณต้องการที่นี่เพื่อแทรกการกระทำที่มากขึ้น แน่นอนคุณอาจมีปัญหากับขั้นตอนที่ 5 แต่การแก้ไขข้อขัดแย้งการรวมกับ git เป็นทักษะที่คุณควรมี ถ้าไม่ฝึก!]

  1. git rebase --continue

ง่ายใช่มั้ย

หากคุณเข้าใจgit rebaseการเพิ่มการ 'รูท' ไม่ควรเป็นปัญหา

ขอให้สนุกกับคอมไพล์!


5
คำถามที่ถามเพื่อแทรกการกระทำแรก : จาก ABC ที่คุณต้องการ ZABC ตรงไปตรงมาgit rebaseไม่สามารถทำได้
Petr Viktorin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.