2010-12-29 25 views
7

Version đơn giản:Sử dụng git filter-branch để loại bỏ các cam kết bằng tin nhắn cam kết của họ

Nếu tôi có một chi nhánh "foo-555", với một loạt các cam kết với những thông điệp như:

  • foo 555: blah
  • foo 123: blah blah
  • foo 555: blah blah blah
  • foo 321: blahblah

và tôi muốn xóa tất cả các cam kết không bắt đầu bằng "foo 555:", có cách nào để thực hiện điều đó bằng cách sử dụng git filter-branch (hoặc bất kỳ công cụ nào khác cho vấn đề đó) không?

Version gốc (chi tiết):

Trong kho của chúng tôi, chúng tôi có một quy ước, nơi mọi cam kết thông báo bắt đầu với một khuôn mẫu nhất định:

Redmine # 555: SOME_MESSAGE

Chúng tôi cũng làm một chút rebasing để mang lại những thay đổi của chi nhánh phát hành tiềm năng cho một chi nhánh của vấn đề cụ thể. Nói cách khác, tôi có thể có nhánh "foo-555", nhưng trước khi tôi kết hợp nó vào nhánh "pre-release", tôi cần có bất kỳ cam kết nào trước khi phát hành có foo-555 không (vì vậy mà foo- 555 có thể nhanh chóng chuyển tiếp hợp nhất vào trước khi phát hành). Tuy nhiên, vì đôi khi phát hành đôi khi thay đổi, đôi khi chúng tôi sẽ đưa ra các tình huống mà bạn mang theo cam kết từ bản phát hành trước, nhưng sau đó cam kết đó sẽ bị xóa khỏi bản phát hành trước. Thật dễ dàng để xác định các cam kết đến từ bản phát hành trước, vì số từ thông điệp cam kết của họ sẽ không khớp với số chi nhánh; ví dụ: nếu tôi thấy "Redmine # 123: ..." trong chi nhánh foo-555 của mình, tôi biết rằng đó không phải là cam kết từ chi nhánh của tôi.

Vì vậy, bây giờ là câu hỏi: Tôi muốn xóa tất cả các cam kết "không thuộc về" đối với chi nhánh; nói cách khác, bất kỳ cam kết rằng:

  • là trong ngành foo-555 của tôi, nhưng không phải trong ngành trước khi phát hành (pre-release..foo-555)
  • Có một thông điệp rằng doesn' cam kết t bắt đầu bằng "Redmine # 555"

nhưng tất nhiên "555" sẽ thay đổi từ chi nhánh này sang chi nhánh khác. Có cách nào để sử dụng bộ lọc-chi nhánh (hoặc bất kỳ công cụ khác) để thực hiện điều này? Hiện tại cách duy nhất tôi có thể nhìn thấy để làm điều đó là làm một rebase tương tác ("git rebase -i") và tự xóa tất cả các commit "xấu".

+0

thể bạn không cherry chọn các cam kết mà bạn muốn vào ngành có liên quan? –

+0

Chúng tôi * có thể *, nhưng giả sử tôi có 10 555 commit và 10 commit khác; Tôi sẽ phải thiết lập lại và sau đó làm 10 lựa chọn anh đào (so với một lệnh lọc-chi nhánh ... nếu một điều như vậy là có thể). – machineghost

Trả lời

4

Viết một kịch bản để loại bỏ các nếp với Redmine #555:

#!/bin/sh 

mv $1 $1.$$ 
grep -v 'Redmine #555' < $1.$$ > $1 
rm -f $1.$$ 

Tất nhiên bạn có thể làm điều đó tuy nhiên bạn muốn (ví dụ echo một kịch bản các lệnh để ed).

Sau đó khởi động rebase bạn với EDITOR thiết lập để kịch bản của bạn:

EDITOR=/path/to/script git rebase -i REVISION 

Tất nhiên nó vẫn sẽ không được đảm bảo để hoàn thành - có thể có những sai sót trong quá rebase gây ra bằng cách để lại các bản chỉnh sửa. Bạn vẫn có thể sửa chúng và git rebase --continue theo cách thủ công.

+0

Điều này có vẻ như nó sẽ làm việc, nhưng ... không có cách nào để chỉ sử dụng Git (không có kịch bản shell) và làm cho quá trình tự động thông qua một "git filter-branch --commit-filter"? – machineghost

+1

Trợ giúp cho '--commit-filter' đặc biệt đề cập rằng' skip_commit' bỏ qua cam kết nhưng ** không thay đổi ** và nói để sử dụng 'git-rebase' thay thế. 'filter-branch' xem xét các phiên bản của bạn như là một chuỗi các trạng thái và cho phép bạn hoán đổi từng cam kết, nhưng những thay đổi không lan truyền cho các con. 'rebase' xem xét các bản sửa đổi của bạn như một chồng các bản vá và bất kỳ sửa đổi nào giữa luồng * làm * truyền đến các bản sửa đổi trong tương lai. Nhưng điều đó có thể gây ra lỗi là lý do tại sao nó không thể hoàn toàn tự động. –

+0

Ahhhhhhh. Cảm ơn! – machineghost

8

Đây là giải pháp nhanh sử dụng filter-branch thay vì rebasing. Không có sự tương tác hoặc cần giải quyết xung đột.

git filter-branch --commit-filter ' 
    if [ `git rev-list --all --grep "<log-pattern>" | grep -c "$GIT_COMMIT"` -gt 0 ] 
    then 
     skip_commit "[email protected]"; 
    else 
     git commit-tree "[email protected]"; 
    fi' HEAD 

Bạn có lẽ sẽ muốn sau đó dọn dẹp với:

git reflog expire --expire=now 
git gc --prune=now 
+1

Đối với các kho lưu trữ lớn với rất nhiều cam kết, điều này có thể mất một thời gian (ví dụ, một vài giây trên mỗi bộ lọc cam kết trên FreeBSD '-CURRENT', trong đó có hàng trăm nghìn cam kết). Một lựa chọn thay thế nhanh hơn cho điều kiện 'git rev-list --all [...]' là 'git show $ GIT_COMMIT | grep -c "" '. – trombonehero

Các vấn đề liên quan