Adakah cara untuk menekan sejumlah komitmen non-interaktif?

Jawaban:

146

Pastikan pohon kerja Anda bersih, lalu

git reset --soft HEAD~3
git commit -m 'new commit message'
wilhelmtell.dll
sumber
@wilhelmtell: Bagus! Sekarang, apakah saya bisa membuat alias git, misalnya "mysquash 3 'some message'", untuk memotongnya menjadi satu baris?
Phillip
5
Jika hanya jumlah baris:git reset --soft HEAD~3 && git commit -m "my message"
KingCrunch
7
@Phillip: Anda dapat menyematkan fungsi shell di alias git. git config alias.mysquash '!f(){ git reset --soft HEAD~$1 && git commit ${2:+-m "$2"}; };f'. git mysquash 3 'some message'akan berfungsi, tetapi saya juga mengubahnya sehingga git musquash 3akan menghilangkan -m sepenuhnya sehingga Anda akan mendapatkan git commitUI interaktif dalam kasus itu.
Lily Ballard
3
Hanya untuk memperjelas: Ini tidak sama dengan squash. Squash juga akan menggabungkan pesan komit. Jika Anda melakukan soft reset, Anda akan kehilangan semua pesan komit. Jika Anda ingin squash coba stackoverflow.com/a/27697274/974186
René Link
1
@sebnukem - Saat itulah kami mencoba untuk mendorong cabang dan remote dikonfigurasi untuk menolak dorongan paksa.
avmohan
30

Saya pribadi menyukai solusi wilhelmtell :

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

Namun, saya membuat alias dengan beberapa pengecekan kesalahan sehingga Anda bisa melakukan ini:

git squash 3 'my commit message'

Saya sarankan menyiapkan alias yang benar-benar menjalankan skrip sehingga lebih mudah untuk (a) membuat kode skrip Anda dan (b) melakukan pekerjaan yang lebih kompleks dengan pemeriksaan kesalahan. Di bawah ini adalah skrip yang melakukan pekerjaan squash dan di bawahnya adalah skrip untuk menyiapkan alias git Anda.

Script untuk squash (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

Kemudian untuk menghubungkan skrip squash.sh itu ke alias git, buat skrip lain untuk menyiapkan alias git Anda seperti itu ( create_aliases.command atau 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 '----------------'
n8tr
sumber
4
Anda juga dapat memasukkannya ke dalam $PATHnama git-squash.shdan secara otomatis akan diberi nama alias git squash. Saya tidak mengubah jawaban Anda, kalau-kalau ada alasan untuk menggunakan create-aiases.shskrip yang tidak saya ketahui.
Saya pada dasarnya menggunakan skrip create_aliases.command sehingga bahkan PM dan desainer kami dapat dengan mudah menyiapkannya. Cukup klik dua kali pada skrip dan semuanya sudah siap (terutama karena saya memiliki skrip pengaturan di repo kami dan jalur relatif diketahui). Kemudian mereka bahkan tidak perlu memulai ulang terminal.
n8tr
1
Saya mencoba ini dan membaca pesan komit saya sebagai hitungan squash dan gagal karena itu bukan integer.
Minthos
1
Solusinya adalah menambahkan - setelah /squash.sh \ $ 1 \ $ 2 '
Minthos
1
Saya suka ide solusinya, tetapi komentar di atas belum diperhitungkan dalam solusi. Harus ada tanda minus antara petik tunggal dan ganda.
fisik
10

Untuk menambah jawaban oleh wilhelmtell, saya merasa nyaman untuk melakukan soft reset HEAD~2dan kemudian mengubah komit HEAD~3:

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

Ini akan menggabungkan semua komit ke HEAD~3komit dan menggunakan pesan komitnya. Pastikan untuk memulai dari pohon kerja yang bersih.

harmonis
sumber
2
Ini adalah jawaban yang benar karena ini menekan serangkaian komit yang mengarah ke head, dan menggunakan pesan komit pertama. Ini non-interaktif.
Landon Kuhn
9

Saya menggunakan:

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

Bekerja dengan baik. Hanya saja, jangan mencoba memiliki log komit dengan baris yang dimulai dengan "pilih" :)

Julien Wajsberg
sumber
Sayangnya ini tidak berhasil untuk saya di Windows. Tetap dapatkan editor interaktif.
abergmeier
3

Gunakan perintah berikut untuk menghentikan 4 komit terakhir dalam komit terakhir:

git squash 4

Dengan alias:

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 

Dari https://github.com/brauliobo/gitconfig/blob/master/configs/.gitconfig

brauliobo.dll
sumber
Anda tidak menentukan OS / shell apa yang digunakan di sini. Solusi ini mungkin tidak akan berfungsi untuk semua desktop lain yang digunakan orang.
Rick-777
ya, Anda harus menggunakan linux / bash | zsh untuk ini
brauliobo
2

Berikut ini adalah satu baris untuk menghentikan 2 komit terakhir. Dalam contoh ini, pesan dari komit terakhir kedua akan disimpan. Anda dapat mengubah pesan sesuai keinginan Anda.

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

Perintah ini akan sangat berguna jika Anda membuat alias untuk perintah ini dan menggunakan alias sebagai gantinya.

Jasir
sumber
1

Untuk menghancurkan segalanya sejak cabang itu bercabang dari master:

git reset --soft $(git merge-base --fork-point master) \
  && git commit --verbose --reedit-message=HEAD --reset-author
Andy
sumber
--reedit-message=HEADakan menggunakan pesan dari komit terakhir yang bukan bagian dari squashing . Ini mungkin bukan yang Anda inginkan. Untuk mendapatkan pesan dari komit pertama yang akan disertakan , baik (1) ganti HEADdengan hash komit yang Anda inginkan pesannya, atau (2) lompat ke komit pertama untuk disertakan dan git commit --amend --reedit-message=HEAD. Inilah yang dilakukan oleh jawaban yang harmonis.
Maëlan
-1

Anda bisa cukup dekat dengan

git rebase --untuk HEAD ~ 4 HEAD ~ master

Ini mengasumsikan Anda menguasai dengan sejarah linier. Ini bukan squash karena membuang komitmen perantara. Anda perlu mengubah HEAD baru untuk mengubah pesan komit.

Greg Bacon
sumber
Terima kasih Greg; dengan 'discards' maksud Anda bahwa komit perantara tersebut tunduk pada pembersihan oleh git gc?
Phillip
@Phillip Ya, komit perantara menjadi sampah serta HEAD lama karena ditulis ulang untuk HEAD~4dijadikan induknya.
Greg Bacon