เปลี่ยนสาขา Git โดยไม่ต้องชำระเงินไฟล์


100

เป็นไปได้ไหมใน Git ที่จะเปลี่ยนไปใช้สาขาอื่นโดยไม่ตรวจสอบไฟล์ทั้งหมด

หลังจากเปลี่ยนสาขาฉันต้องลบไฟล์ทั้งหมดสร้างใหม่คอมมิตและเปลี่ยนกลับ ดังนั้นการตรวจสอบไฟล์จึงเสียเวลา (และมีไฟล์ประมาณ 14,000 ไฟล์ซึ่งใช้งานได้ยาวนาน)

เพื่อให้ทุกอย่างชัดเจน:

ฉันต้องการทั้งหมดนี้เพื่ออัปโหลดเอกสารไปยัง GitHub

ฉันมีที่เก็บที่มีสาขาgh-pages เมื่อฉันสร้างเอกสารภายในเครื่องใหม่ฉันคัดลอกไปยังไดเร็กทอรีที่เก็บคอมมิตและพุชไปที่ GitHub แต่ฉันไม่มีความสุขเพราะฉันมีเอกสารสองชุดในเครื่อง และฉันตัดสินใจที่จะสร้างสาขาที่ว่างเปล่าและหลังจากตกลงแล้วให้เปลี่ยนเป็นว่างและลบไฟล์ แต่การเปลี่ยนกลับเป็นการดำเนินการที่ยาวนานฉันจึงถามคำถามนี้

ฉันรู้ว่าฉันสามารถออกจากสาขาgh-pagesและลบไฟล์ได้ แต่ฉันไม่ชอบต้นไม้ที่ทำงานสกปรก


"นาน" สำหรับคุณนานแค่ไหน? คุณกำลังทำงานบนแพลตฟอร์มใด คุณทำงานผ่านเครือข่ายเช่นกับ NFS หรือการแชร์ไฟล์อื่น ๆ หรือไม่?
Greg Hewgill

จุดประสงค์ของแบบฝึกหัดนี้คืออะไร? คุณต้องการมีสองสาขาโดยสาขาที่มีรายละเอียดการบันทึกครั้งที่สองจะมีการเปลี่ยนแปลงที่สำคัญเท่านั้น (แบบหยาบ) หรือไม่?
Jakub Narębski

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

คำตอบ:


106

ใช่คุณสามารถทำได้

git symbolic-ref HEAD refs/heads/otherbranch

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

git reset

1
ใช้echo "ebff34ffb665de0694872dceabe0edeaf50ec5a9" > .git/HEADตามด้วยgit resetเพื่อชี้ไปที่การอ้างอิงแทนสาขา
cadorn

1
การเขียนไปยังไฟล์ HEAD โดยตรงนั้นเชื่อถือได้น้อยกว่า เกิดอะไรขึ้นถ้าคุณอยู่ใน subdir? สำหรับหัวแยก (หัวที่ชี้ไปที่ SHA1 โดยตรง) ให้ลองทำดังนี้ git update-ref HEAD refs/heads/otherbranch
Alexander Bird

2
หากคุณต้องการเช็คเอาท์ไปยังสาขาใหม่จากสาขาปัจจุบันวิธีอื่นในการทำเช่นนี้คือ 1. git stash2. git checkout -b otherBranch3.git stash pop
วินนี่

@AlexanderBird: git update-refมีประโยชน์ แต่ก็ย้ายส่วนปลายของสาขาปัจจุบันด้วย
tomekwi

47

ใช้คำสั่ง git พื้นฐานเท่านั้น:

คำตอบนี้ยาวกว่าของ Charles เล็กน้อย แต่ประกอบด้วยคำสั่ง git พื้นฐานเท่านั้นที่ฉันเข้าใจและจำไว้ดังนั้นจึงไม่จำเป็นต้องค้นหาต่อไป

ทำเครื่องหมายตำแหน่งปัจจุบันของคุณ (ยอมรับก่อนหากจำเป็น):

git checkout -b temp

รีเซ็ต (ย้าย) เครื่องหมายไปยังสาขาอื่นโดยไม่ต้องเปลี่ยน dir ทำงาน:

git reset <branch where you want to go>

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

git checkout <branch where you want to go>

เนื่องจาก HEAD ของคุณชี้ไปที่คอมมิตเดียวกันอยู่แล้วจึงไม่แตะต้องผบ

git branch -d temp

โปรดทราบว่าคำสั่งเหล่านี้ยังพร้อมใช้งานจากไคลเอ็นต์กราฟิกใด ๆ


7
ฉันต้องการgit reset --soft <branch where you want to go>หลีกเลี่ยงการอัปเดตดัชนี
JoelFan

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

26

ใน v2.24 เป็นสิ่งที่ต้องการความปลอดภัยgit switch ดังนั้นผมเปลี่ยนชื่อนามแฝงด้านล่างเพื่อสำหรับ "ฮอปสาขาโดยไม่ต้องเปลี่ยน worktree"git checkout
git hop

เพื่อประโยชน์ของผู้อ่าน:

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

git checkout --detach
git reset --soft commitish
git checkout commitish

อธิบาย:

  • git checkout --detachเหมือนกับgit checkout HEAD^{}ที่ทิ้งสาขาปัจจุบันไว้ข้างหลังและเข้าสู่ "สถานะหัวแยก" ดังนั้นการปรับเปลี่ยนครั้งต่อไปจะHEADไม่มีผลกับสาขาใด ๆ อีกต่อไป การถอดHEADไม่ส่งผลกระทบต่อแผนผังงานหรือดัชนี
  • git reset --soft commitishแล้วย้ายHEADไป SHA commitishของที่กำหนด หากคุณต้องการอัปเดตดัชนีเช่นกันให้ออก--softไป แต่ฉันไม่แนะนำให้ทำเช่นนั้น นี่เป็นอีกครั้งที่ไม่ได้สัมผัสกับผังงานและ ( --soft) ไม่ใช่ดัชนี
  • git checkout commitishจากนั้นแนบHEADกับcommitish(สาขา) ที่กำหนดอีกครั้ง (ถ้าcommitishเป็น SHA จะไม่มีอะไรเกิดขึ้น) สิ่งนี้ก็ไม่ส่งผลกระทบต่อดัชนีหรือแผนผังงาน

โซลูชันนี้ยอมรับทุกสิ่งที่อ้างถึงคอมมิตดังนั้นจึงเหมาะสำหรับgitนามแฝงบางตัว rev-parseด้านล่างเป็นเพียงการทดสอบเพื่อให้แน่ใจว่าไม่มีอะไรแบ่งในห่วงโซ่เช่นว่าความผิดพลาดไม่ได้ตั้งใจเปลี่ยนเป็นรัฐหัวเดี่ยว (กู้คืนข้อผิดพลาดจะเป็นวิธีที่ซับซ้อนมากขึ้น)

สิ่งนี้นำไปสู่git hop treeishนามแฝงต่อไปนี้:

git config --global alias.hop '!f() { git rev-parse --verify "$*" && git checkout "HEAD^{}" && git reset --soft "$*" && git checkout "$*"; }; f'

FYI คุณสามารถพบได้ในรายการgitนามแฝงของฉัน


คุณไม่ต้องการใช้$@มากกว่า$*หรือ? ความแตกต่างคือ $ @ ที่ไม่มีการขยายอาร์กิวเมนต์ที่ยกมาซึ่งมีช่องว่างอยู่ภายใน
kyb

1
@kyb เคล็ดลับฟังก์ชันที่ถูกขโมยมาจากคำตอบ SO อีก และ$@ไม่ได้หมายถึงที่นี่อย่างแน่นอน $*ใช้แทน$1เช่นว่าgit switch -f bจะกลายเป็นเช่นเดียวกับgit switch '-f b'ที่ควรจะเป็นข้อผิดพลาด ด้วยวิธีนี้ฉันสามารถทำให้นามแฝงสั้นลงโดยทิ้งการจัดการข้อผิดพลาดบางอย่างเช่น!f() { [ 1 = $# ] || { echo 'WTF!'; return 1; }; ..
Tino

ทางออกที่ดีมาก โดยเฉพาะอย่างยิ่งที่สามารถใช้สำหรับสาขาที่ห่างไกลได้!
Nils-o-mat

14

คงไม่ใช่ทางออกที่ดีกว่าหากมีไดเรกทอรีการทำงานสองรายการ (พื้นที่ทำงานสองพื้นที่) พร้อมที่เก็บเดียวหรือแม้แต่ที่เก็บสองที่

มีเครื่องมือgit-new-workdirในcontrib/ส่วนที่จะช่วยคุณในเรื่องนี้


git-new-workdir เหมือนกับคำสั่ง worktree ของ git หรือไม่ ฉันใช้ worktree เมื่อต้องการชำระเงินสาขาไปยังโฟลเดอร์อื่น (โดยไม่ต้องโคลน repo ทั้งหมด)
Ryuu

git-new-worktreeถือกำเนิดสคริปต์git worktreesubcommand; คำสั่งนี้ใช้ไม่ได้เมื่อเขียนคำตอบ ตัวอย่างเช่นสคริปต์ต้องการการสนับสนุน symlink IMHO ควรใช้การสนับสนุนแบบเนทีฟ
Jakub Narębski

8

git read-treeผมคิดว่าคุณกำลังมองหาคำสั่งประปา การดำเนินการนี้จะอัปเดตดัชนี แต่จะไม่อัปเดตไฟล์ใด ๆ ในไดเรกทอรีการทำงานของคุณ ตัวอย่างเช่นสมมติว่าbranchเป็นชื่อของสาขาที่จะอ่าน:

git read-tree branch

หากคุณต้องการตกลงกับสาขาที่คุณเพิ่งอ่านคุณจะต้อง:

git symbolic-ref HEAD อ้างอิง / หัว / สาขา

ไม่มีฉันต้องสาขาสวิทช์เท่านั้นไม่มีการเปลี่ยนแปลงอื่น ๆ - ดังนั้นสัญลักษณ์-เตะเป็นที่ดีพอ
TIG

read-treeสร้างข้อผิดพลาด: fatal: Not a valid object name branchถ้าไม่มีใดgit switch branch
Andry

7

คุณสามารถเขียนทับไฟล์ HEAD ของคุณด้วยชื่อสาขาอื่น:

echo "ref: refs / head / MyOtherBranch"> .git / HEAD


13
น่าจะดีกว่าถ้าใช้คำสั่ง symbolic-ref เพื่อทำสิ่งนี้ให้คุณ: git symbolic-ref HEAD refs/heads/MyOtherBranch kernel.org/pub/software/scm/git/docs/git-symbolic-ref.html
Greg Hewgill

@GregHewgill นี่เป็นวิธีเดียวที่ฉันรู้ในการย้าย HEAD ไปยังคอมมิตแฮช คุณสามารถทำได้ด้วยgit symbolic-ref?
tomekwi

0

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


คุณสามารถใช้git-new-worktreeแทนได้ (ในcontrib/)
Jakub Narębski

ฉันมักจะทำอะไรคล้าย ๆ กันนั่นคือการคัดลอกไดเร็กทอรีในเครื่องของฉันก่อนที่จะทำ "คอมไพล์ที่น่ากลัว" ในฐานะมือใหม่ (เช่นเปลี่ยนสาขา ฯลฯ ) ฉันขอแนะนำให้ผู้คนไปเส้นทางนั้นจนกว่าคุณจะรู้สึกมั่นใจใน git-fu ของคุณ แต่ให้ถอยห่างจากเส้นทางนั้นเมื่อเป็นไปได้ การเก็บสอง repos ที่แตกต่างกันเป็นเรื่องปกติ แต่จะเพิ่มชั้นของความซับซ้อนและไม่อนุญาตให้คุณใช้ประโยชน์จากคุณสมบัติที่มีประโยชน์มากมายของ git (การรวมการเก็บเชอร์รี่ ฯลฯ )
David

0

หากคุณเพียงแค่พยายามเปลี่ยนตำแหน่งที่จุดสาขาระยะไกลคุณสามารถทำได้ด้วย "git push" โดยไม่ต้องแตะสำเนาในเครื่อง

http://kernel.org/pub/software/scm/git/docs/git-push.html

รูปแบบของพารามิเตอร์ <refspec> คือ plus + ที่เป็นทางเลือกตามด้วย source ref <src> ตามด้วยเครื่องหมายทวิภาค: ตามด้วยปลายทาง ref <dst> ใช้เพื่อระบุสิ่งที่ <src> อ็อบเจ็กต์ที่ <dst> ref ในที่เก็บแบบรีโมตจะถูกอัพเดต

เช่นในการอัพเดต foo เพื่อคอมมิต c5f7eba ให้ทำดังต่อไปนี้:

git push origin c5f7eba:foo

ไม่แน่ใจว่านั่นคือสิ่งที่คุณเป็นหรือไม่


คำถามมีคำตอบแล้ว: stackoverflow.com/questions/1282639/…
tig

0

คุณสามารถใช้ประโยชน์จากไฟล์

      1. git checkout -f <new-branch>
      2. git cherry-pick -x <previous-branch-commit-id>

previous-branch-commit-id คือการกระทำจากที่ที่คุณต้องการคัดลอกข้อมูลเก่า


0

หรือเพียงแค่ใช้ไฟล์แพทช์เพื่อแก้ไขจากสาขาอื่นของคุณไปยังต้นแบบของคุณ

git diff otherbranch master > ~/tmp/otherbranch.diff
git checkout master
git apply ~/tmp/otherbranch.diff

-1

บอกว่าคุณต้องการอยู่ในสาขา A แต่มีไฟล์จากสาขา B

ค้นหาการอ้างอิงการกระทำปัจจุบันของสาขา A พร้อมบันทึกคอมไพล์เช่น "99ce9a2"

git checkout A
git reset --hard B
git reset 99ce9a2

ตอนนี้คุณควรอยู่ในสาขา A โดยมีโครงสร้างโฟลเดอร์ที่ตรงกับ B ซึ่งแสดงเป็นการเปลี่ยนแปลงที่ไม่ได้จัดเตรียมไว้ (ประวัติไม่ได้เปลี่ยนแปลง)

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