ย้ายการกระทำล่าสุดไปยังสาขาใหม่ด้วย Git


4983

ฉันต้องการย้ายหลายคอมมิชชันล่าสุดที่ฉันตกลงไว้ที่จะเชี่ยวชาญในสาขาใหม่และนำมาสเตอร์กลับไปก่อนที่จะทำการคอมมิชชันเหล่านั้น น่าเสียดาย Git-fu ของฉันยังไม่แข็งแรงพอความช่วยเหลือใด ๆ

เช่นฉันจะไปจากนี้

master A - B - C - D - E

สำหรับสิ่งนี้?

newbranch     C - D - E
             /
master A - B 

114
หมายเหตุ: ฉันถามคำถามตรงข้ามที่นี่
Benjol

3
eddmann.com/posts/…อันนี้ใช้ได้
Sagar Naliyapara

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

คำตอบ:


6450

ย้ายไปสาขาที่มีอยู่

หากคุณต้องการย้ายการกระทำของคุณไปยังสาขาที่มีอยู่มันจะมีลักษณะเช่นนี้:

git checkout existingbranch
git merge master         # Bring the commits here
git checkout master
git reset --keep HEAD~3  # Move master back by 3 commits.
git checkout existingbranch

--keepตัวเลือกที่จะเก็บรักษาการเปลี่ยนแปลงปราศจากข้อผูกมัดใด ๆ ที่คุณอาจมีในไฟล์ที่ไม่เกี่ยวข้องหรือยกเลิกถ้าการเปลี่ยนแปลงเหล่านั้นจะต้องมีการเขียนทับ - คล้ายกับสิ่งที่git checkoutทำ ถ้าเป็นการยกเลิกgit stashการเปลี่ยนแปลงและลองใหม่ของคุณหรือใช้--hardเพื่อทำให้การเปลี่ยนแปลงนั้นหายไป (แม้จะมาจากไฟล์ที่ไม่ได้เปลี่ยนแปลงระหว่างคอมมิท!)

ย้ายไปที่สาขาใหม่

วิธีนี้ใช้ได้โดยการสร้างสาขาใหม่ด้วยคำสั่งแรก ( git branch newbranch) แต่ไม่เปลี่ยนไปใช้ จากนั้นเราจะย้อนกลับสาขาปัจจุบัน (หลัก) และเปลี่ยนเป็นสาขาใหม่เพื่อทำงานต่อไป

git branch newbranch      # Create a new branch, containing all current commits
git reset --keep HEAD~3   # Move master back by 3 commits (Make sure you know how many commits you need to go back)
git checkout newbranch    # Go to the new branch that still has the desired commits
# Warning: after this it's not safe to do a rebase in newbranch without extra care.

แต่ให้แน่ใจว่ามีจำนวนคนที่ต้องกลับไป อีกทางเลือกหนึ่งแทนHEAD~3คุณสามารถเพียงแค่ให้กัญชาของการกระทำ (หรือการอ้างอิงเช่นorigin/master) คุณต้องการที่จะกลับไปเช่น:

git reset --keep a1b2c3d4

คำเตือน:ด้วย Git เวอร์ชัน 2.0 และใหม่กว่าหากคุณภายหลังgit rebaseสาขาใหม่ตามสาขาเดิม ( master) คุณอาจจำเป็นต้องมี--no-fork-pointตัวเลือกที่ชัดเจนในระหว่างการรีบูตเพื่อหลีกเลี่ยงการสูญเสียคอมมิทที่คุณย้ายจากสาขาหลัก การมีbranch.autosetuprebase alwaysชุดทำให้สิ่งนี้มีโอกาสมากขึ้น ดูคำตอบของ John Mellorสำหรับรายละเอียด


249
และโดยเฉพาะอย่างยิ่งอย่าพยายามย้อนกลับไปไกลกว่าจุดที่คุณผลักดันไปยังที่เก็บข้อมูลล่าสุดซึ่งคนอื่นอาจดึง
เกร็ก Hewgill

107
สงสัยว่าถ้าคุณสามารถอธิบายได้ว่าทำไมงานนี้ สำหรับฉันคุณกำลังสร้างสาขาใหม่ให้ลบ 3 คอมมิชชันจากสาขาเก่าที่คุณยังอยู่จากนั้นตรวจสอบสาขาที่คุณสร้าง ดังนั้นความมุ่งมั่นที่คุณลบออกไปอย่างน่าอัศจรรย์ปรากฏขึ้นในสาขาใหม่ได้อย่างไร
Jonathan Dumaine

142
@Janathan Dumaine: เพราะฉันสร้างสาขาใหม่ก่อนที่จะลบคอมมิชชันจากสาขาเก่า พวกเขายังคงอยู่ในสาขาใหม่
sykora

86
สาขาในคอมไพล์เป็นเพียงเครื่องหมายจุดที่จะกระทำในประวัติศาสตร์มีอะไรที่ถูกโคลนสร้างหรือลบ (ยกเว้นเครื่องหมาย)
knittl

218
หมายเหตุเพิ่มเติม: อย่าทำเช่นนี้กับการเปลี่ยนแปลงที่ไม่มีข้อผูกมัดในสำเนาการทำงานของคุณ! เพียงแค่นี้ฉัน! :(
Adam Tuttle

1038

สำหรับคนที่สงสัยว่าทำไมมันถึงได้ผล (เหมือนตอนแรก):

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

A-B-C-D-E (HEAD)
        ↑
      master

หลังgit branch newBranch:

    newBranch
        ↓
A-B-C-D-E (HEAD)
        ↑
      master

หลังgit reset --hard HEAD~2:

    newBranch
        ↓
A-B-C-D-E (HEAD)
    ↑
  master

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


56
ฉันยังต้องทำการgit push origin master --forceเปลี่ยนแปลงเพื่อแสดงในที่เก็บหลัก
Dženan

9
คำตอบนี้ทำให้เกิดการกระทำที่จะหายไป:ครั้งต่อไปคุณgit rebaseที่ 3 กระทำจะถูกยกเลิกอย่างเงียบ ๆ newbranchจาก ดูคำตอบของฉันสำหรับรายละเอียดและทางเลือกที่ปลอดภัยกว่า
John Mellor

14
@ John นั่นไร้สาระ การรีบูตโดยไม่ทราบว่าสิ่งที่คุณกำลังทำอยู่ทำให้คุณหลงทาง หากคุณทำคอมมิชชันฉันเสียใจสำหรับคุณ แต่คำตอบนี้ไม่ได้ทำให้คุณเสียไป โปรดทราบว่าorigin/masterไม่ปรากฏในแผนภาพด้านบน หากคุณผลักดันให้origin/masterทำการเปลี่ยนแปลงข้างต้นแน่นอนว่าสิ่งต่าง ๆ จะเป็นเรื่องตลก แต่นั่นเป็น "หมอมันเจ็บเมื่อฉันทำแบบนี้" ปัญหา และมันก็เกินขอบเขตสำหรับคำถามเดิมที่ถาม ฉันขอแนะนำให้คุณเขียนคำถามของคุณเองเพื่อสำรวจสถานการณ์ของคุณแทนการหักหลังคำถามนี้
ไรอันลุน

1
@John ในคำตอบของคุณคุณพูดว่า "อย่าทำอย่างนี้! git branch -t newbranch" ย้อนกลับไปและอ่านคำตอบอีกครั้ง ไม่มีใครแนะนำให้ทำเช่นนั้น
Ryan Lundy

1
@Kyralessa แน่นอน แต่ถ้าคุณดูแผนภาพในคำถามมันชัดเจนว่าพวกเขาต้องการnewbranchที่จะยึดmasterสาขาท้องถิ่นที่มีอยู่ หลังจากดำเนินการคำตอบที่ได้รับการยอมรับเมื่อผู้ใช้ที่ได้รับไปรอบ ๆ เพื่อทำงานgit rebaseในnewbranch, คอมไพล์จะเตือนพวกเขาว่าพวกเขาลืมที่จะตั้งสาขาต้นน้ำดังนั้นพวกเขาจะใช้git branch --set-upstream-to=masterแล้วgit rebaseและมีปัญหาเดียวกัน พวกเขาอาจใช้git branch -t newbranchเป็นครั้งแรกเช่นกัน
John Mellor

455

โดยทั่วไป ...

วิธีการเปิดเผยโดย sykora เป็นตัวเลือกที่ดีที่สุดในกรณีนี้ แต่บางครั้งก็ไม่ใช่วิธีที่ง่ายที่สุดและไม่ใช่วิธีทั่วไป สำหรับวิธีทั่วไปให้ใช้git cherry-pick :

เพื่อให้บรรลุถึงสิ่งที่ OP ต้องการกระบวนการ 2 ขั้นตอน:

ขั้นตอนที่ 1 - หมายเหตุซึ่งกระทำจากต้นแบบที่คุณต้องการใน newbranch

ปฏิบัติ

git checkout master
git log

หมายเหตุ hashes ของ (กล่าว 3) newbranchมุ่งมั่นที่คุณต้องการใน ที่นี่ฉันจะใช้:
C กระทำ: 9aa1233
D กระทำ: 453ac3d
E กระทำ:612ecb3

หมายเหตุ:คุณสามารถใช้อักขระเจ็ดตัวแรกหรือแฮชคอมมิททั้งหมด

ขั้นตอนที่ 2 - ใส่พวกเขาใน newbranch

git checkout newbranch
git cherry-pick 612ecb3
git cherry-pick 453ac3d
git cherry-pick 9aa1233

หรือ (ใน Git 1.7.2+ ใช้ช่วง)

git checkout newbranch
git cherry-pick 612ecb3~1..9aa1233

git cherry-pickใช้ทั้งสามข้อตกลงกับ newbranch


13
OP พยายามที่จะย้ายจากต้นแบบไปยังสาขาใหม่หรือไม่? หากคุณเลือกเชอร์รี่ขณะที่อยู่ในระดับปริญญาโทคุณจะต้องเพิ่มสาขาหลัก - เพิ่มการยอมรับว่ามันมีอยู่แล้วในความเป็นจริง และไม่ขยับกลับไปที่หัวสาขาทั้งสองไปที่ B. หรือมีบางสิ่งที่บอบบางและเท่มาก
RaveTheTadpole

6
วิธีนี้ใช้งานได้ดีมากถ้าคุณตั้งใจที่จะทำผิดสาขาที่ไม่ได้อยู่ในหลักเมื่อคุณควรสร้างสาขาฟีเจอร์ใหม่
julianc

5
+1 สำหรับแนวทางที่มีประโยชน์ในบางสถานการณ์ นี่เป็นสิ่งที่ดีถ้าคุณต้องการดึงความมุ่งมั่นของคุณเอง (ซึ่งสลับกับผู้อื่น) เข้าสู่สาขาใหม่
Tyler V.

7
มันเป็นคำตอบที่ดีกว่า วิธีนี้คุณสามารถย้ายคอมมิชชันไปยังสาขาใดก็ได้
skywinder

8
คำสั่งของการเก็บเชอร์รี่สำคัญหรือไม่?
kon Psych

328

อีกวิธีหนึ่งในการทำเช่นนี้โดยใช้เพียง 2 คำสั่ง ยังทำให้ต้นไม้ทำงานปัจจุบันของคุณไม่บุบสลาย

git checkout -b newbranch # switch to a new branch
git branch -f master HEAD~3 # make master point to some older commit

รุ่นเก่า - ก่อนที่ฉันจะเรียนรู้git branch -f

git checkout -b newbranch # switch to a new branch
git push . +HEAD~3:master # make master point to some older commit 

ความสามารถในpushการ.เป็นเคล็ดลับที่ดีที่จะรู้


1
ไดเรกทอรีปัจจุบัน ฉันเดาว่านี่จะใช้ได้เฉพาะเมื่อคุณอยู่ในไดเรกทอรีอันดับต้น ๆ
aragaer

1
แรงผลักดันในท้องถิ่นนั้นเกิดจากรอยยิ้ม แต่ด้วยความไตร่ตรองแล้วgit branch -fที่นี่แตกต่างอย่างไร?
jthill

2
@ GerardSexton .เป็นผู้อำนวยการคนปัจจุบัน git สามารถส่งไปที่ REMOTES หรือ GIT URLs path to local directoryได้รับการสนับสนุนไวยากรณ์ Git URL ดูส่วน GIT URL git help cloneที่ใน
อ่อนตัว

31
ฉันไม่รู้ว่าทำไมไม่ให้คะแนนสูงกว่านี้ ตายง่ายและไม่มีอันตรายเล็ก ๆ แต่อาจเกิดขึ้นจากการรีเซ็ตคอมไพล์ - ฮาร์ด
Godsmith

4
@Godsmith การเดาของฉันคือคนชอบสามคำสั่งง่ายๆกับสองคำสั่งที่คลุมเครือเล็กน้อย นอกจากนี้คำตอบที่ได้รับการโหวตสูงสุดจะได้รับการโหวตมากขึ้นตามลักษณะของการแสดงก่อน
JS_Riddler

322

คำตอบก่อนหน้านี้ส่วนใหญ่ผิดไปอย่างอันตราย!

อย่าทำอย่างนี้:

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch

ในครั้งต่อไปที่คุณเรียกใช้git rebase(หรือgit pull --rebase) 3 การกระทำเหล่านั้นจะถูกยกเลิกอย่างเงียบ ๆnewbranch! (ดูคำอธิบายด้านล่าง)

ทำสิ่งนี้แทน:

git reset --keep HEAD~3
git checkout -t -b newbranch
git cherry-pick ..HEAD@{2}
  • ก่อนอื่นจะละทิ้ง 3 การกระทำล่าสุด ( --keepเหมือน--hardแต่ปลอดภัยกว่าล้มเหลวแทนที่จะทิ้งการเปลี่ยนแปลงที่ไม่มีข้อผูกมัด)
  • newbranchจากนั้นก็จะปิดส้อม
  • จากนั้นมันจะทำการหยิบเชอร์รี่ทั้ง 3 ตัวกลับเข้าnewbranchมา เนื่องจากพวกมันไม่ได้ถูกอ้างถึงโดยสาขาอีกต่อไปมันก็ทำเช่นนั้นโดยใช้reflogของ git : HEAD@{2}เป็นความมุ่งมั่นที่HEADเคยอ้างถึง 2 การทำงานที่ผ่านมานั่นคือก่อนที่เราจะ 1 ออกnewbranchและ 2 ใช้git resetเพื่อทิ้ง 3 คอมมิชชัน

คำเตือน: reflog ถูกเปิดใช้งานโดยค่าเริ่มต้น แต่ถ้าคุณได้ปิดใช้งานด้วยตนเอง (เช่นโดยการใช้ "เปลือย" เก็บคอมไพล์) คุณจะไม่สามารถที่จะได้รับ 3 git reset --keep HEAD~3กระทำกลับมาหลังจากที่ทำงาน

ทางเลือกที่ไม่พึ่งพาการอ้างอิงคือ:

# newbranch will omit the 3 most recent commits.
git checkout -b newbranch HEAD~3
git branch --set-upstream-to=oldbranch
# Cherry-picks the extra commits from oldbranch.
git cherry-pick ..oldbranch
# Discards the 3 most recent commits from oldbranch.
git branch --force oldbranch oldbranch~3

(ถ้าคุณต้องการคุณสามารถเขียน@{-1}- สาขาที่เช็คเอาท์ก่อนหน้า - แทนoldbranch)


คำอธิบายทางเทคนิค

ทำไมถึงgit rebaseทิ้ง 3 คอมมิชชันหลังจากตัวอย่างแรก? เป็นเพราะgit rebaseไม่มีอาร์กิวเมนต์เปิดใช้งาน--fork-pointตัวเลือกโดยค่าเริ่มต้นซึ่งใช้ reflog ท้องถิ่นเพื่อพยายามที่จะแข็งแกร่งกับสาขาต้นน้ำที่ถูกผลักดัน

สมมติว่าคุณแยกต้นกำเนิด / ปรมาจารย์เมื่อมีการยอมรับ M1, M2, M3 จากนั้นให้สามการกระทำ:

M1--M2--M3  <-- origin/master
         \
          T1--T2--T3  <-- topic

แต่มีบางคนเขียนประวัติโดยใช้ต้นกำเนิด / แรงผลักดันหลักเพื่อลบ M2:

M1--M3'  <-- origin/master
 \
  M2--M3--T1--T2--T3  <-- topic

ใช้ reflog ในพื้นที่ของคุณgit rebaseจะเห็นว่าคุณแยกจากชาติกำเนิดก่อนหน้าของสาขาต้นกำเนิด / หลักและด้วยเหตุนี้การมอบหมาย M2 และ M3 จึงไม่ได้เป็นส่วนหนึ่งของสาขาหัวข้อของคุณ ดังนั้นจึงสันนิษฐานว่ามีเหตุผลตั้งแต่ M2 ถูกลบออกจากสาขาต้นน้ำคุณไม่ต้องการมันในสาขาหัวข้อของคุณอีกต่อไปเมื่อสาขาหัวข้อถูก rebased:

M1--M3'  <-- origin/master
     \
      T1'--T2'--T3'  <-- topic (rebased)

พฤติกรรมนี้เหมาะสมและโดยทั่วไปเป็นสิ่งที่ถูกต้องเมื่อรีบูต

ดังนั้นเหตุผลที่คำสั่งต่อไปนี้ล้มเหลว:

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch

เป็นเพราะพวกเขาออกจาก reflog ในสถานะที่ไม่ถูกต้อง Git มองnewbranchว่ามีการแยกสาขาต้นน้ำในการแก้ไขที่รวม 3 คอมมิตจากนั้นการreset --hardเขียนประวัติต้นน้ำใหม่เพื่อลบการคอมมิชชันและในครั้งถัดไปที่คุณรันgit rebaseมันจะลบทิ้งเหมือนกับการคอมมิชชันอื่นที่ถูกลบออกจากอัปสตรีม

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

สำหรับรายละเอียดเพิ่มเติมโปรดดูที่ความหมายของ--fork-pointในrebase คอมไพล์และคอมไพล์ผสานฐานเอกสาร


14
คำตอบนี้บอกว่า "อย่าทำเช่นนี้!" เหนือสิ่งที่ไม่มีใครแนะนำให้ทำ
Ryan Lundy

3
คนส่วนใหญ่ไม่ได้เขียนประวัติศาสตร์ที่ตีพิมพ์masterซ้ำโดยเฉพาะใน ไม่เลยพวกเขาไม่ผิดอย่างอันตราย
Walf

4
@Kyralessa -tคุณหมายถึงgit branchเกิดขึ้นโดยปริยายหากคุณgit config --global branch.autosetuprebase alwaysตั้งค่าไว้ แม้ว่าคุณจะไม่ทำเช่นนั้นฉันก็อธิบายให้คุณทราบแล้วว่าปัญหาเดียวกันนี้เกิดขึ้นหากคุณติดตั้งการติดตามหลังจากดำเนินการคำสั่งเหล่านี้เนื่องจาก OP มีแนวโน้มที่จะตอบคำถามของพวกเขา
John Mellor

2
@RockLee ใช่วิธีทั่วไปในการแก้ไขสถานการณ์ดังกล่าวคือการสร้างสาขาใหม่ (newbranch2) จากจุดเริ่มต้นที่ปลอดภัยจากนั้นเลือกเชอร์รี่ที่คุณต้องการเก็บไว้ (จาก badnewbranch ถึง newbranch2) การเก็บเชอร์รี่จะมอบแฮชใหม่ให้ดังนั้นคุณจะสามารถรีบูท newbranch2 ได้อย่างปลอดภัย (และสามารถลบแบรนต์ใหม่)
John Mellor

2
@ วอลล์คุณเข้าใจผิด: การรีคอมไพล์ gitได้รับการออกแบบให้มีความทนทานเมื่อมีการเขียนประวัติขึ้นใหม่ น่าเสียดายที่ผลข้างเคียงของความแข็งแกร่งนั้นส่งผลกระทบต่อทุกคนแม้ว่าพวกเขาและพวกเขาจะไม่เคยเขียนประวัติศาสตร์
John Mellor

150

วิธีที่ง่ายกว่ามากโดยใช้ git stash

นี่เป็นวิธีที่ง่ายกว่าสำหรับการผูกสาขาผิด เริ่มต้นจากสาขาmasterที่มีการกระทำที่ผิดสามประการ:

git reset HEAD~3
git stash
git checkout newbranch
git stash pop

จะใช้เมื่อไหร่?

  • หากวัตถุประสงค์หลักของคุณคือการย้อนกลับ master
  • คุณต้องการเปลี่ยนแปลงไฟล์
  • คุณไม่สนใจเกี่ยวกับข้อความที่กระทำผิดพลาด
  • คุณยังไม่ได้ผลัก
  • คุณต้องการให้ง่ายต่อการจดจำ
  • คุณไม่ต้องการให้เกิดปัญหาแทรกซ้อนเช่นสาขาชั่วคราว / สาขาใหม่การค้นหาและการคัดลอกแฮชและปวดหัวอื่น ๆ

สิ่งนี้ทำโดยหมายเลขบรรทัด

  1. เลิกทำสามคอมมิชชันล่าสุด (และข้อความ) ถึงmasterแต่จะทำให้ไฟล์ทำงานทั้งหมดไม่เปลี่ยนแปลง
  2. หยุดการเปลี่ยนแปลงไฟล์การทำงานทั้งหมดทำให้แผนผังการทำงานmasterเท่ากับสถานะ HEAD ~ 3
  3. สลับไปยังสาขาที่มีอยู่ newbranch
  4. ใช้การเปลี่ยนแปลงที่เก็บในไดเร็กทอรีการทำงานของคุณและล้างค่าที่เก็บ

ตอนนี้คุณสามารถใช้git addและgit commitได้ตามปกติ newbranchใหม่กระทำทั้งหมดจะถูกเพิ่มเข้าไป

สิ่งนี้ไม่ได้ทำ

  • มันไม่ได้ทิ้งกิ่งก้านที่สุ่มชั่วคราวไว้บนต้นไม้ของคุณ
  • มันไม่ได้รักษาข้อความคอมมิทที่ผิดพลาดดังนั้นคุณจะต้องเพิ่มข้อความการส่งข้อความใหม่ในการส่งข้อความใหม่
  • Update! ใช้ลูกศรขึ้นเพื่อเลื่อนดูบัฟเฟอร์คำสั่งของคุณเพื่อนำการกระทำก่อนหน้านี้ไปใช้อีกครั้งกับข้อความการส่งมอบ (ขอบคุณ @ARK)

เป้าหมาย

OP ระบุว่าเป้าหมายคือ "นำมาสเตอร์กลับไปก่อนที่จะทำการคอมมิทเหล่านั้น" โดยไม่สูญเสียการเปลี่ยนแปลงและวิธีการแก้ปัญหานี้ก็ทำเช่นนั้น

ฉันทำเช่นนี้อย่างน้อยสัปดาห์ละครั้งเมื่อฉันไม่ได้ตั้งใจจะทำให้ใหม่กระทำการแทนmaster developโดยปกติฉันมีเพียงหนึ่งข้อตกลงที่จะย้อนกลับซึ่งในกรณีที่ใช้git reset HEAD^ในบรรทัดที่ 1 เป็นวิธีที่ง่ายกว่าในการย้อนกลับเพียงคนเดียวที่มอบหมาย

อย่าทำอย่างนี้ถ้าคุณผลักการเปลี่ยนแปลงของต้นน้ำ

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


4
ขอบคุณฉันดีใจที่ฉันอ่านอดีต / ผ่านมากที่จะไปที่นี่ทำให้มันเป็นกรณีการใช้งานที่ค่อนข้างธรรมดาสำหรับฉันเช่นกัน พวกเราเป็นคนผิดปกติเหรอ?
Jim Mack

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

9
นี่ควรเป็นคำตอบที่ยอมรับได้ มันตรงไปตรงมาง่ายต่อการเข้าใจและจดจำได้ง่าย
Sina Madani

1
ฉันไม่คิดว่าการหยุดชะงักเป็นสิ่งจำเป็น ฉันทำได้โดยไม่ต้องใช้และทำงานได้ดี
A Campos

1
คุณสามารถเรียกคืนข้อความคอมมิชชันของคุณได้เช่นกันหากคุณมีข้อความเหล่านั้นในประวัติ CLI (บรรทัดคำสั่ง) ของคุณ ฉันเกิดมีทั้งคำสั่งgit addและgit commitคำสั่งที่ฉันใช้ดังนั้นสิ่งที่ฉันต้องทำก็คือกดลูกศรแล้วป้อนสองสามครั้งและบูม! ทุกอย่างกลับมา แต่ตอนนี้อยู่ในสาขาที่เหมาะสม
Luke Gedeon

30

สิ่งนี้ไม่ได้ "เคลื่อนย้าย" พวกมันในแง่เทคนิค แต่มีผลเหมือนกัน:

A--B--C  (branch-foo)
 \    ^-- I wanted them here!
  \
   D--E--F--G  (branch-bar)
      ^--^--^-- Opps wrong branch!

While on branch-bar:
$ git reset --hard D # remember the SHAs for E, F, G (or E and G for a range)

A--B--C  (branch-foo)
 \
  \
   D-(E--F--G) detached
   ^-- (branch-bar)

Switch to branch-foo
$ git cherry-pick E..G

A--B--C--E'--F'--G' (branch-foo)
 \   E--F--G detached (This can be ignored)
  \ /
   D--H--I (branch-bar)

Now you won't need to worry about the detached branch because it is basically
like they are in the trash can waiting for the day it gets garbage collected.
Eventually some time in the far future it will look like:

A--B--C--E'--F'--G'--L--M--N--... (branch-foo)
 \
  \
   D--H--I--J--K--.... (branch-bar)

1
คุณไม่สามารถใช้rebaseสิ่งเดียวกันได้หรือ
Bergi

ใช่คุณสามารถใช้rebaseในสาขาแยกในสถานการณ์ข้างต้น
Sukima

24

ในการดำเนินการนี้โดยไม่ต้องเขียนประวัติศาสตร์ใหม่ (เช่นหากคุณผลักสัญญาแล้ว):

git checkout master
git revert <commitID(s)>
git checkout -b new-branch
git cherry-pick <commitID(s)>

กิ่งไม้ทั้งสองสามารถถูกผลักโดยไม่มีแรง!


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

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

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

5
ฉันไม่ทำตามการโต้เถียงของคุณ - จุดประสงค์ของคำตอบนี้คือคุณไม่ได้เปลี่ยนประวัติศาสตร์เพียงเพิ่มคอมมิทใหม่ ความมุ่งมั่นใหม่เหล่านี้สามารถผลักดันและผสานได้ตามปกติ
teh_senaus

13

มีเพียงสถานการณ์นี้:

Branch one: A B C D E F     J   L M  
                       \ (Merge)
Branch two:             G I   K     N

ฉันแสดง:

git branch newbranch 
git reset --hard HEAD~8 
git checkout newbranch

ฉันคาดหวังว่าฉันจะเป็นหัวหน้า แต่จะส่ง L เป็นตอนนี้ ...

เพื่อให้แน่ใจว่าจะลงจอดในจุดที่ถูกต้องในประวัติศาสตร์มันง่ายกว่าที่จะทำงานกับแฮชของการกระทำ

git branch newbranch 
git reset --hard #########
git checkout newbranch

7

ฉันจะไปจากนี้ได้อย่างไร

A - B - C - D - E 
                |
                master

สำหรับสิ่งนี้?

A - B - C - D - E 
    |           |
    master      newbranch

ด้วยสองคำสั่ง

  • git branch -m newbranch ต้นแบบ

ให้

A - B - C - D - E 
                |
                newbranch

และ

  • git branch master B

ให้

A - B - C - D - E
    |           |
    master      newbranch

ใช่งานนี้และค่อนข้างง่าย Sourcetree GUI สับสนเล็กน้อยเกี่ยวกับการเปลี่ยนแปลงที่เกิดขึ้นใน git shell แต่หลังจากดึงข้อมูลแล้วมันก็กลับมาใช้งานได้อีกครั้ง
ลาร์สเค

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

4

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

  1. สร้างสาขาใหม่จากหนึ่งในปัจจุบัน:git branch new-branch-name

  2. ผลักดันสาขาใหม่ของคุณ:git push origin new-branch-name

  3. เปลี่ยนสาขาเก่า (ปัจจุบัน)ของคุณกลับไปเป็นสถานะผลักดัน / เสถียรล่าสุด:git reset --hard origin/old-branch-name

บางคนก็มีคนอื่นupstreamsมากกว่าoriginพวกเขาควรใช้อย่างเหมาะสมupstream


3

1) สร้างสาขาใหม่ซึ่งย้ายการเปลี่ยนแปลงทั้งหมดของคุณไปยัง new_branch

git checkout -b new_branch

2) จากนั้นกลับไปที่สาขาเก่า

git checkout master

3) ทำการ rebit คอมไพล์

git rebase -i <short-hash-of-B-commit>

4) จากนั้นเอดิเตอร์ที่เปิดอยู่มีข้อมูลการคอมมิตล่าสุด 3 ครั้ง

...
pick <C's hash> C
pick <D's hash> D
pick <E's hash> E
...

5) เปลี่ยนpickเป็นdrop3 ข้อตกลงทั้งหมด จากนั้นบันทึกและปิดตัวแก้ไข

...
drop <C's hash> C
drop <D's hash> D
drop <E's hash> E
...

6) ตอนนี้ 3 คอมมิทล่าสุดจะถูกลบออกจากสาขาปัจจุบัน ( master) ตอนนี้ดันสาขาอย่างแรงพร้อม+ลงชื่อก่อนชื่อสาขา

git push origin +master

2

คุณสามารถทำได้เพียง 3 ขั้นตอนง่ายๆที่ฉันใช้

1) สร้างสาขาใหม่ที่คุณต้องการส่งมอบการอัปเดตล่าสุดให้คุณ

git branch <branch name>

2) ค้นหารหัส Commit ล่าสุดสำหรับการกระทำในสาขาใหม่

git log

3) คัดลอกที่ส่งมอบข้อความประจำตัวที่บันทึกรายการกระทำล่าสุดเกิดขึ้นที่ด้านบน เพื่อให้คุณสามารถค้นหาความมุ่งมั่นของคุณ คุณยังพบสิ่งนี้ผ่านทางข้อความ

git cherry-pick d34bcef232f6c...

นอกจากนี้คุณยังสามารถให้รหัสกระทำบางอย่าง

git cherry-pick d34bcef...86d2aec

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

ตอนนี้คุณสามารถผลักดันรหัสของคุณ

git push


0

อีกวิธีในการทำสิ่งนี้:

[1]เปลี่ยนชื่อmasterสาขาเป็นของคุณnewbranch(สมมติว่าคุณอยู่ในmasterสาขา):

git branch -m newbranch

[2]สร้างmasterสาขาจากความมุ่งมั่นที่คุณต้องการ:

git checkout -b master <seven_char_commit_id>

เช่น git checkout -b master a34bc22

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