มีวิธีสควอชจำนวนหนึ่งที่ไม่โต้ตอบหรือไม่?


116

ฉันพยายามที่จะทำลายความมุ่งมั่น - HEAD to HEAD ~ 3 มีวิธีที่รวดเร็วในการทำเช่นนี้หรือฉันต้องใช้ rebase --interactive?


5
คำถามที่คล้ายกัน: stackoverflow.com/questions/5189560/…
koppor

คำตอบ:


148

ตรวจสอบให้แน่ใจว่าต้นไม้ทำงานของคุณสะอาดแล้ว

git reset --soft HEAD~3
git commit -m 'new commit message'

@wilhelmtell: เยี่ยม! ตอนนี้ฉันจะสามารถสร้างชื่อแทน git เช่น "mysquash 3 'some message'" เพื่อตัดสิ่งนี้ให้เหลือหนึ่งบรรทัดได้หรือไม่
Phillip

5
หากเป็นเพียงจำนวนบรรทัด:git reset --soft HEAD~3 && git commit -m "my message"
KingCrunch

7
@Phillip: คุณสามารถฝังฟังก์ชันเชลล์ในนามแฝงของคอมไพล์ git config alias.mysquash '!f(){ git reset --soft HEAD~$1 && git commit ${2:+-m "$2"}; };f'. git mysquash 3 'some message'จะใช้งานได้ แต่ฉันก็ปรับแต่งมันด้วยดังนั้นgit musquash 3จะละเว้นแฟล็ก -m ทั้งหมดดังนั้นคุณจะได้รับgit commitUI แบบโต้ตอบในกรณีนั้น
Lily Ballard

3
เพียงเพื่อให้ชัดเจน: มันไม่เหมือนกับสควอช สควอชจะรวมข้อความคอมมิตด้วย หากคุณทำการซอฟต์รีเซ็ตคุณจะสูญเสียข้อความทั้งหมดของการกระทำ หากคุณต้องการสควอชลองstackoverflow.com/a/27697274/974186
René Link

1
@sebnukem - นั่นคือตอนที่เราพยายามผลักดันสาขาและรีโมตได้รับการกำหนดค่าให้ปฏิเสธแรงผลัก
avmohan

30

โดยส่วนตัวฉันชอบวิธีแก้ปัญหาของ wilhelmtell :

git reset --soft HEAD~3
git commit -m 'new commit message'

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

git squash 3 'my commit message'

ขอแนะนำให้ตั้งค่านามแฝงที่เรียกใช้สคริปต์จริง ๆ เพื่อให้ง่ายต่อการ (a) เขียนโค้ดสคริปต์ของคุณและ (b) ทำงานที่ซับซ้อนมากขึ้นด้วยการตรวจสอบข้อผิดพลาด ด้านล่างนี้เป็นสคริปต์ที่ทำงานของสควอชและด้านล่างเป็นสคริปต์สำหรับตั้งค่านามแฝง git ของคุณ

สคริปต์สำหรับสควอช (squash.sh)

#!/bin/bash
#

#get number of commits to squash
squashCount=$1

#get the commit message
shift
commitMsg=$@

#regular expression to verify that squash number is an integer
regex='^[0-9]+$'

echo "---------------------------------"
echo "Will squash $squashCount commits"
echo "Commit message will be '$commitMsg'"

echo "...validating input"
if ! [[ $squashCount =~ $regex ]]
then
    echo "Squash count must be an integer."
elif [ -z "$commitMsg" ]
then
    echo "Invalid commit message.  Make sure string is not empty"
else
    echo "...input looks good"
    echo "...proceeding to squash"
    git reset --soft HEAD~$squashCount
    git commit -m "$commitMsg"
    echo "...done"
fi

echo
exit 0

จากนั้นหากต้องการเชื่อมต่อสคริปต์squash.shนั้นกับนามแฝง git ให้สร้างสคริปต์อื่นสำหรับการตั้งค่านามแฝง git ของคุณเช่นนั้น ( create_aliases.commandหรือcreate_aliases.sh ):

#!/bin/sh
echo '-----------------------'
echo 'adding git aliases....'
echo '-----------------------'
echo
git config --global alias.squash "!bash -c 'bash <path to scripts directory>/squash.sh \$1 \$2' -"
#add your other git aliases setup here
#and here
#etc.
echo '------------------------------------'
echo 'here is your global gitconfig file:'
echo '------------------------------------'
more ~/.gitconfig
echo 
echo
echo '----------------'
echo 'end of script...'
echo '----------------'

4
นอกจากนี้คุณยังสามารถวางไว้ใน$PATHการตั้งชื่อและมันจะถูกนามโดยอัตโนมัติเป็นgit-squash.sh git squashฉันไม่ได้เปลี่ยนคำตอบของคุณในกรณีที่มีเหตุผลที่ต้องใช้create-aiases.shสคริปต์ที่ฉันไม่ทราบ

โดยพื้นฐานแล้วฉันใช้สคริปต์ create_aliases.command เพื่อให้แม้แต่ PMs และนักออกแบบของเราก็สามารถตั้งค่าได้อย่างง่ายดาย เพียงดับเบิลคลิกที่สคริปต์และทุกอย่างพร้อมแล้ว (โดยเฉพาะอย่างยิ่งเมื่อฉันมีสคริปต์การตั้งค่าใน repo ของเราและรู้จักเส้นทางสัมพัทธ์) จากนั้นพวกเขาไม่จำเป็นต้องรีสตาร์ทเทอร์มินัล
n8tr

1
ฉันลองสิ่งนี้และมันอ่านข้อความคอมมิตของฉันเป็นจำนวนสควอชและล้มเหลวเนื่องจากไม่ใช่จำนวนเต็ม
Minthos

1
วิธีแก้คือต่อท้าย - หลัง /squash.sh \ $ 1 \ $ 2 '
Minthos

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

10

ในการเพิ่มคำตอบโดยwilhelmtellฉันพบว่าสะดวกในการรีเซ็ตแบบซอฟต์HEAD~2และแก้ไขการกระทำของHEAD~3:

git reset --soft HEAD~2
git commit --all --amend --no-edit    

สิ่งนี้จะรวมคอมHEAD~3มิตทั้งหมดเข้ากับคอมมิตและใช้ข้อความคอมมิต อย่าลืมเริ่มต้นจากโครงสร้างการทำงานที่สะอาด


2
นี่เป็นคำตอบที่ถูกต้องเพราะมันตัดชุดของการกระทำที่นำไปสู่ส่วนหัวและใช้ข้อความของการกระทำครั้งแรก มันไม่โต้ตอบ
Landon Kuhn

9

ฉันใช้:

EDITOR="sed -i '2,/^$/s/^pick\b/s/'" git rebase -i <ref>

ทำงานได้ดีทีเดียว อย่าพยายามบันทึกคอมมิตด้วยบรรทัดที่ขึ้นต้นด้วย "pick" :)


น่าเศร้าที่สิ่งนี้ใช้ไม่ได้กับฉันบน Windows ยังคงได้รับตัวแก้ไขแบบโต้ตอบ
abergmeier

3

ใช้คำสั่งต่อไปนี้เพื่อสควอช 4 คอมมิตสุดท้ายภายในคอมมิตสุดท้าย:

git squash 4

ด้วยนามแฝง:

squash = !"f() { NL=$1; GIT_EDITOR=\"sed -i '2,$NL s/pick/squash/;/# This is the 2nd commit message:/,$ {d}'\"; git rebase -i HEAD~$NL; }; f"
sq = !git squash $1
sqpsf = !git squash $1 && git psf 

จากhttps://github.com/brauliobo/gitconfig/blob/master/configs/.gitconfig


คุณไม่ได้ระบุว่าจะใช้ OS / เชลล์อะไรที่นี่ วิธีนี้อาจใช้ไม่ได้กับเดสก์ท็อปอื่น ๆ ทั้งหมดที่คนอื่นใช้
Rick-777

ใช่คุณควรใช้ linux / bash | zsh สำหรับสิ่งนี้
brauliobo

2

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

git commit -am "$(git log -1 --skip=1 --pretty=%B | xargs && git reset --soft HEAD~2)"

คำสั่งนี้จะมีประโยชน์มากหากคุณสร้างนามแฝงสำหรับคำสั่งนี้และใช้นามแฝงแทน


1

ในการสควอชทุกอย่างเนื่องจากสาขาถูกแยกออกจากมาสเตอร์:

git reset --soft $(git merge-base --fork-point master) \
  && git commit --verbose --reedit-message=HEAD --reset-author

--reedit-message=HEADจะใช้ข้อความของสุดท้ายกระทำซึ่งไม่ได้เป็นส่วนหนึ่งของการบีบ นี่อาจไม่ใช่สิ่งที่คุณต้องการ ค่อนข้างได้รับข้อความของครั้งแรกที่มุ่งมั่นที่จะถูกรวมทั้ง(1)แทนที่HEADด้วยกัญชาของการกระทำที่คุณต้องการให้ข้อความของหรือ(2)git commit --amend --reedit-message=HEADข้ามไปยังคนแรกที่มุ่งมั่นที่จะถูกรวมและ นี่คือคำตอบที่กลมกลืนกัน
Maëlan

-1

คุณสามารถเข้าใกล้

git rebase - จาก HEAD ~ 4 HEAD ~ master

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


ขอบคุณ Greg; โดย 'ละทิ้ง' คุณหมายความว่าการกระทำระดับกลางเหล่านั้นอาจถูกล้างโดย git gc หรือไม่?
Phillip

@Phillip ใช่การคอมมิชชันระดับกลางกลายเป็นขยะเช่นเดียวกับ HEAD แบบเก่าเนื่องจากถูกเขียนขึ้นใหม่เพื่อให้มีHEAD~4เป็นพาเรนต์
Greg Bacon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.