Có cách nào để loại bỏ một số cam kết không tương tác không?


Câu trả lời:


146

Đảm bảo cây làm việc của bạn sạch sẽ, sau đó

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

@wilhelmtell: Tuyệt vời! Bây giờ tôi có thể tạo một bí danh git, ví dụ: "mysquash 3 'some message'", để cắt nó xuống một dòng không?
Phillip

5
Nếu nó chỉ là số dòng:git reset --soft HEAD~3 && git commit -m "my message"
KingCrunch

7
@Phillip: Bạn có thể nhúng một hàm shell vào bí danh git. git config alias.mysquash '!f(){ git reset --soft HEAD~$1 && git commit ${2:+-m "$2"}; };f'. git mysquash 3 'some message'sẽ hoạt động, nhưng tôi cũng đã chỉnh sửa nó nên git musquash 3sẽ loại bỏ hoàn toàn cờ -m, do đó bạn sẽ nhận được git commitgiao diện người dùng tương tác trong trường hợp đó.
Lily Ballard,

3
Chỉ cần nói rõ rằng: Nó không giống như một quả bí. Một quả bí cũng sẽ hợp nhất các thông điệp cam kết. Nếu bạn thiết lập lại mềm, bạn sẽ mất tất cả các thông báo của các cam kết. Nếu bạn muốn bí, hãy thử stackoverflow.com/a/27697274/974186
René Link

1
@sebnukem - Đó là khi chúng tôi cố gắng đẩy nhánh và điều khiển từ xa được định cấu hình để từ chối lực đẩy.
avmohan 17/02/16

30

Cá nhân tôi thích giải pháp của wilhelmtell :

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

Tuy nhiên, tôi đã tạo một bí danh với một số lỗi kiểm tra để bạn có thể thực hiện việc này:

git squash 3 'my commit message'

Tôi khuyên bạn nên thiết lập bí danh thực sự chạy các tập lệnh để (a) viết mã các tập lệnh của bạn dễ dàng hơn và (b) thực hiện công việc phức tạp hơn với việc kiểm tra lỗi. Dưới đây là một tập lệnh thực hiện công việc của bí và sau đó là tập lệnh để thiết lập bí danh git của bạn.

Script cho squashing (bí.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

Sau đó, để kết nối tập lệnh squash.sh đó với bí danh git, hãy tạo một tập lệnh khác để thiết lập bí danh git của bạn như vậy ( create_aliases.command hoặc 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
Bạn cũng có thể đặt nó trong $PATHtên git-squash.shvà nó sẽ được tự động bí danh như git squash. Tôi đã không thay đổi câu trả lời của bạn, đề phòng có lý do để sử dụng create-aiases.shtập lệnh mà tôi không biết.

Về cơ bản, tôi sử dụng script create_aliases.command để ngay cả các PM và nhà thiết kế của chúng tôi cũng có thể dễ dàng thiết lập. Chỉ cần nhấp đúp vào tập lệnh và tất cả chúng đã được thiết lập (đặc biệt là vì tôi có tập lệnh thiết lập trong repo của chúng tôi và đường dẫn tương đối đã biết). Sau đó, họ thậm chí không cần phải khởi động lại thiết bị đầu cuối.
n8tr

1
Tôi đã thử điều này và nó đọc thông báo cam kết của tôi dưới dạng số bí mật và không thành công vì nó không phải là số nguyên.
Minthos

1
Giải pháp là nối thêm - sau /squash.sh \ $ 1 \ $ 2'
Minthos

1
Tôi thích ý tưởng của giải pháp, nhưng nhận xét ở trên vẫn chưa được tính đến trong giải pháp. Cần có một dấu trừ giữa dấu nháy đơn và dấu nháy kép.
thể chất hoạt động

10

Để thêm vào câu trả lời của wilhelmtell, tôi thấy thuận tiện khi đặt lại mềm HEAD~2và sau đó sửa đổi cam kết của HEAD~3:

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

Điều này sẽ hợp nhất tất cả các cam kết với HEAD~3cam kết và sử dụng thông điệp cam kết của nó. Đảm bảo bắt đầu từ một cây làm việc sạch sẽ.


2
Đây là câu trả lời chính xác vì nó gạch chéo một loạt các cam kết dẫn đến đối đầu và nó sử dụng thông điệp của cam kết đầu tiên. Nó không tương tác.
Landon Kuhn

9

Tôi đã sử dụng:

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

Làm việc khá tốt. Chỉ cần không cố gắng có một bản ghi cam kết với một dòng bắt đầu bằng "pick" :)


Đáng tiếc là điều này không hoạt động đối với tôi trên Windows. Vẫn nhận được trình chỉnh sửa tương tác.
abergmeier

3

Sử dụng lệnh sau để loại bỏ 4 lần cam kết cuối cùng trong lần cam kết cuối cùng:

git squash 4

Với bí danh:

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 

Từ https://github.com/brauliobo/gitconfig/blob/master/configs/.gitconfig


Bạn không chỉ định hệ điều hành / shell nào đang được sử dụng ở đây. Giải pháp này có thể sẽ không hoạt động đối với tất cả các máy tính để bàn khác mà mọi người sử dụng.
Rick-777,

vâng, bạn nên sử dụng Linux / bash | zsh cho điều này
brauliobo

2

Đây là một lớp lót để xóa 2 cam kết cuối cùng. Trong ví dụ này, thông báo của lần cam kết cuối cùng thứ hai sẽ được giữ lại. Bạn có thể thay đổi thông báo như bạn muốn.

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

Lệnh này sẽ rất hữu ích nếu bạn tạo bí danh cho lệnh này và sử dụng bí danh thay thế.


1

Để thu gọn tất cả mọi thứ kể từ khi nhánh được chia từ chủ nhân:

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

--reedit-message=HEADsẽ sử dụng thông báo của lần cam kết cuối cùng mà không phải là một phần của quá trình xử lý . Đây có thể không phải là cái bạn muốn. Để thay thế thông điệp của cam kết đầu tiên được đưa vào , hãy (1) thay thế HEADbằng băm của cam kết mà bạn muốn thông báo hoặc (2) chuyển đến cam kết đầu tiên được đưa vào và git commit --amend --reedit-message=HEAD. Đây là những gì câu trả lời hài hòa 'làm.
Maëlan

-1

Bạn có thể đến khá gần với

git rebase --onto HEAD ~ 4 HEAD ~ master

Điều này giả định rằng bạn đang nắm vững một lịch sử tuyến tính. Nó không hoàn toàn bí vì nó loại bỏ các cam kết trung gian. Bạn cần phải sửa đổi HEAD mới để sửa đổi thông báo cam kết.


Cảm ơn Greg; bằng cách 'loại bỏ' bạn có nghĩa là các cam kết trung gian đó có thể bị xóa bởi git gc không?
Phillip

@Phillip Có, các cam kết trung gian cũng trở thành rác cũng như HEAD cũ vì nó được viết lại để có HEAD~4làm cha mẹ của nó.
Greg Bacon
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.