ฉันจะเดินหน้าและถอยหลังระหว่าง commits in git ได้อย่างไร?


110

ฉันกำลังทำgit bisectและหลังจากมาถึงข้อตกลงที่มีปัญหาตอนนี้ฉันกำลังพยายามก้าวไปข้างหน้า / ถอยหลังเพื่อให้แน่ใจว่าฉันอยู่ในสิ่งที่ถูกต้อง

ฉันรู้ที่HEAD^จะย้อนกลับไปในประวัติศาสตร์ แต่มีทางลัดอื่นที่จะพาฉันไปข้างหน้า (ไปสู่การกระทำที่เฉพาะเจาะจงในอนาคต) ดังนี้:

A - B - C(HEAD) - D - E - F

ฉันรู้ว่าเป้าหมายของฉันคือFและฉันต้องการที่จะย้ายจากCไปD


หมายเหตุ: นี่ไม่ใช่Git ที่ซ้ำกัน: วิธีการเลื่อนไปมาระหว่างการกระทำคำถามของฉันแตกต่างกันเล็กน้อยและไม่มีคำตอบที่นั่น


1
stackoverflow.com/questions/2263674/…ช่วยด้วยครับ.
VonC

git checkout -b new_branch HEAD~4เพื่อย้อนกลับ 4 คอมมิตจาก HEAD ในstackoverflow.com/a/4940090/911945
Anton Tarasenko

คำตอบ:


61

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

git checkout $(git rev-list --topo-order HEAD..towards | tail -1)

ที่towardsเป็น SHA1 ของการกระทำหรือแท็ก

คำอธิบาย:

  • คำสั่งภายใน$()หมายถึง: รับคอมมิตทั้งหมดระหว่างกระแสHEADและคอมtowardsมิต (ไม่รวมHEAD) และเรียงลำดับตามลำดับความสำคัญ (เหมือนgit logตามค่าเริ่มต้น - แทนลำดับเวลาซึ่งเป็นค่าเริ่มต้นอย่างแปลกสำหรับrev-list) จากนั้นใช้ค่าสุดท้าย ( tail) คือคนที่เราอยากไป
  • สิ่งนี้ได้รับการประเมินใน subshell และส่งไปgit checkoutยังเพื่อดำเนินการชำระเงิน

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

# Go forward in Git commit hierarchy, towards particular commit 
# Usage:
#  gofwd v1.2.7
# Does nothing when the parameter is not specified.
gofwd() {
  git checkout $(git rev-list --topo-order HEAD.."$*" | tail -1)
}

# Go back in Git commit hierarchy
# Usage: 
#  goback
alias goback='git checkout HEAD~'

2
การก้าวไปข้างหน้าทำงานได้ดีกับส่วนตรงของประวัติศาสตร์ แต่จะเข้าสู่ลูปเมื่อพบการผสาน
Kostas

ใช่ฉันยังไม่ได้ทดสอบเกี่ยวกับการผสาน ฉันจะพยายามดูเวลาว่าง แต่ฉันมีแรงจูงใจเล็กน้อยชั่วคราวเนื่องจากเราตกลงที่จะมีประวัติเชิงเส้นอย่างเคร่งครัดในโครงการของเรา;)
jakub.g

3
ตอบโจทย์มาก! แก้ไขเพื่อระบุสาขาปัจจุบันโดยอัตโนมัติ: stackoverflow.com/a/23172256/480608
Raine Revere

สิ่งนี้ไม่ถูกต้อง git logแสดงการคอมมิตตามลำดับเวลาโดยค่าเริ่มต้นจะเหมือนกับrev-listยกเว้นเมื่อใช้--graphแฟล็ก
papiro

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

55

ฉันเชื่อว่าคุณทำได้:

git reset HEAD@{1}

เพื่อก้าวไปข้างหน้าอย่างรวดเร็ว หากต้องการส่งต่อคอมมิตหลายรายการให้ใช้ HEAD @ {2}, HEAD @ {3} เป็นต้น


51

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

git reset HEAD@{1}

5
หรือgit reset "HEAD@{1}"ในเปลือกหอยบางชนิดเช่น fish และ powershell .. git reflogยังมีประโยชน์ในการค้นหาการกระทำที่ถูกต้อง
สตีฟคุก

1
ใช้เครื่องหมายคำพูดเดี่ยวในคำสั่งเชลล์เสมอเว้นแต่คุณต้องการให้เชลล์พยายามตีความ / ขยายเนื้อหาอย่างชัดเจน นี่เป็นสิ่งสำคัญอย่างยิ่งในกรณีเช่นนี้ซึ่งเป้าหมายคือเพื่อป้องกันไม่ให้เชลล์ตีความอักขระพิเศษ ด้วยวิธีนี้คุณไม่จำเป็นต้องรู้ว่าสตริงมีสิ่งที่เป็นปัญหาหรือไม่
Chris Page

ฉันทำแบบนั้นเพื่อย้อนเวลากลับไปจากนั้นฉันก็git reset HEADกลับไปยังจุดที่ฉันอยู่ ... ตอนนี้ฉันไม่รู้ว่าที่เก็บของฉันอยู่ในสถานะใดและทุกอย่างก็น่ากลัว ตอนนี้ฉันควรทำอะไรดี?
theonlygusti

20

นี่คือสิ่งที่ฉันใช้เพื่อนำทางไปมา

ย้ายไปคอมมิตถัดไป

function n() {
    git log --reverse --pretty=%H master | grep -A 1 $(git rev-parse HEAD) | tail -n1 | xargs git checkout
}

ย้ายไปที่การกระทำก่อนหน้านี้

function p() {
    git checkout HEAD^1
}

ขอบคุณ! ฉันกำลังใช้อยู่ตอนนี้! หมายเหตุสำหรับผู้เริ่มต้นคนอื่น ๆ เช่นฉัน : ในการแนบ HEAD อีกครั้งgit checkout <current branch>แนบกับคอมมิตล่าสุด git checkout -b <new-branch-name>จากการกระทำปัจจุบันอนุญาตให้มีการเปลี่ยนแปลงในสาขาใหม่ git rebase -iยังใช้งานได้ นอกจากนี้ฉันตั้งชื่อn()ฟังก์ชันnx()เพื่อหลีกเลี่ยงความขัดแย้งกับตัวจัดการเวอร์ชันโหนด "n" อย่าลืมตรวจสอบนามแฝง!
Steven Choi

function () {... } ใช้สำหรับสร้างไฟล์สคริปต์ทุบตี Unix / Linux ฉันมาจาก Windows ยากเล็กน้อยสำหรับฉันที่จะเข้าใจประการแรก
IcyBrk

9

พูดว่า F เป็นการกระทำล่าสุดtrunk(ใส่ชื่อสาขาของคุณเองที่นี่) ... คุณสามารถอ้างถึงว่าเป็นtrunk~0(หรือเพียงtrunk), E เป็นtrunk~1, D trunk~2เป็นต้น

ดูreflogของคุณเพื่อดูวิธีอื่น ๆ ในการตั้งชื่อคอมมิต


1
~ กลับไปไม่ใช่ฟาวลำ ~ 2 คือ A
EmmanuelMess

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

@theonlygusti คุณย้ายกลับจาก HEAD สองครั้ง
EmmanuelMess

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

4

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

  function git_down
        git checkout HEAD^
  end

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

  function git_up 
        git log --reverse --pretty=%H $argv | grep -A 1 (git rev-parse HEAD) | tail -n1 | xargs git checkout
  end

การใช้งาน: git down,git up <branch-name>


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

2

หากคุณต้องการเห็นข้างหน้าคุณสามารถทำเคล็ดลับนี้ได้เนื่องจาก Git ไม่มีคำสั่งที่เข้มงวดสำหรับมัน

git log --reverse COMMIT_HASH..

ตัวอย่าง

รายการแฮชประวัติบันทึก:

A
B
C -> put this
D

การใช้คำสั่งgit log --reverse C..ในการส่งออกคุณจะเห็นBและ


1

อาจไม่ใช่วิธีที่ดีที่สุด แต่คุณสามารถใช้git logเพื่อดูรายการคอมมิตแล้วใช้git checkout [sha1 of D]เพื่อย้ายไปที่ D


6
ฉันไม่เข้าใจถ้าเขาอยู่ใน C ดังนั้น git log จะแสดงให้เขาเห็น C, B และ A. เท่านั้น
Bilthon

ตกลงเข้าใจแล้ว แต่คุณจะต้องทำ git-log ที่ยุ่งยากเหมือนที่ระบุไว้ในลิงค์ที่ VonC ให้มา
Bilthon

1

ฉันเพิ่งทำการทดสอบเกี่ยวกับเรื่องนี้ พูดเช่นคุณอยู่ในสาขาหลักจากนั้นทำ:

git checkout HEAD@{3}

ดังนั้นหัวจะหลุดออกไปและคุณสามารถลองอีกครั้งเพื่อไปที่การกระทำอื่น ๆ :

git checkout HEAD@{4}

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

git checkout master

หากคุณไม่ต้องการไปสู่สภาพดั้งเดิมและต้องการให้การกระทำอย่างใดอย่างหนึ่งเป็นส่วนหัวของคุณและดำเนินการต่อจากที่นั่นคุณต้องแตกแขนงออกจากที่นั่น ตัวอย่างเช่นหลังจาก "git checkout HEAD @ {4}" คุณสามารถออกได้

git checkout -b MyNewBranch

0

วิธีแก้ปัญหาชั่วคราวคุณสามารถกลับไปที่ HEAD ด้วย

git checkout <branch>

จากนั้นย้ายไปที่การคอมมิตที่คุณต้องการด้วย

git checkout HEAD~<offset>

0

หากคุณใช้ vs code ประวัติ Git เป็นปลั๊กอินที่ยอดเยี่ยมที่คุณสามารถดูการคอมมิตและตรวจสอบเนื้อหาในตัวแก้ไขได้อย่างมีประสิทธิภาพ ตรวจสอบลิงค์


0
branchName=master; commitInOrder=1; git checkout $(git log --pretty=%H "${branchName}" | tac | head -n "${commitInOrder}" | tail -n 1)

ที่ไหน:

branchName เท่ากับชื่อสาขา

commitInOrder เท่ากับการกระทำตามลำดับจากการกระทำแรกสุดในสาขาที่เลือก (ดังนั้น 1 คือการกระทำแรกสุด 2 คือการกระทำที่สองในสาขาเป็นต้น)

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