Tôi có kho lưu trữ git với khoảng 3500 cam kết và 30.000 tệp riêng biệt trong bản sửa đổi mới nhất. Nó đại diện cho khoảng 3 năm làm việc từ nhiều người và chúng tôi đã nhận được sự cho phép để làm cho nó tất cả nguồn mở. Tôi đang cố gắng giải phóng toàn bộ lịch sử, thay vì chỉ là phiên bản mới nhất. Để thực hiện điều này, tôi quan tâm đến việc "quay ngược thời gian" và chèn tiêu đề giấy phép ở đầu tệp khi chúng được tạo. Tôi thực sự đã làm việc này, nhưng nó mất khoảng 3 ngày chạy hoàn toàn ra khỏi một đĩa RAM, và vẫn yêu cầu một chút can thiệp thủ công. Tôi biết nó có thể nhanh hơn rất nhiều, nhưng git-fu của tôi không hoàn toàn phụ thuộc vào nhiệm vụ.ghi lại hiệu quả (rebase -i) rất nhiều lịch sử với git
Câu hỏi: làm thế nào tôi có thể thực hiện điều tương tự nhanh hơn rất nhiều?
Những gì tôi đang làm (tự động trong một kịch bản, nhưng xin vui lòng chịu với tôi ...):
Xác định tất cả các cam kết, nơi một tập tin mới được bổ sung vào kho lưu trữ (có chỉ nhút nhát 500 trong số này, fwiw):
git whatchanged --diff-filter=A --format=oneline
Xác định GIT_EDITOR biến môi trường là kịch bản của riêng tôi mà thay thế
pick
vớiedit
chỉ một lần duy nhất trên dòng đầu tiên của file (bạn sẽ thấy lý do tại sao trong thời gian ngắn). Đây là cốt lõi của hoạt động:perl -pi -e 's/pick/edit/ if $. == 1' $1
Đối với mỗi cam kết từ đầu ra của
git whatchanged
trên, gọi một rebase tương tác bắt đầu ngay trước khi cam kết rằng thêm các tập tin:git rebase -i decafbad001badc0da0000~1
Tùy chỉnh GIT_EDITOR của tôi (mà perl một lớp lót) thay đổi pick
đến edit
và chúng tôi bị loại bỏ vào trình bao để thực hiện thay đổi đối với tệp mới. Một kịch bản đơn giản header-inserter
tìm kiếm một mẫu duy nhất đã biết trong tiêu đề mà tôi đang cố gắng chèn (chỉ trong các loại tệp đã biết (*. [ChS] cho tôi)). Nếu nó không có, nó chèn nó, và git add
của tập tin. Kỹ thuật ngây thơ này không có kiến thức về các tệp thực sự được thêm vào trong cam kết hiện tại, nhưng nó kết thúc làm điều đúng và là idempotent (an toàn để chạy nhiều lần với cùng một tệp), và không phải là toàn bộ quá trình này bị tắc nghẽn. .
Tại thời điểm này chúng tôi hạnh phúc vì chúng tôi đã cập nhật các hiện cam kết, và gọi:
git commit --amend
git rebase --continue
Các rebase --continue
là phần tốn kém. Vì chúng tôi gọi một git rebase -i
một lần cho mỗi sửa đổi ở đầu ra của whatchanged
, đó là rất nhiều việc rebasing. Hầu như tất cả các thời gian trong đó kịch bản này chạy là chi tiêu xem "Rebasing (2345/2733)" tăng truy cập.
Nó cũng không chỉ chậm. Có những xung đột định kỳ phải được giải quyết. Điều này có thể xảy ra trong ít nhất các trường hợp này (nhưng có thể nhiều hơn): (1) khi tệp "mới" thực sự là bản sao của tệp hiện có với một số thay đổi được thực hiện cho các dòng đầu tiên của nó (ví dụ: #include
). Đây là một cuộc xung đột thực sự nhưng có thể được giải quyết tự động trong hầu hết các trường hợp (vâng, có một kịch bản liên quan đến điều đó). (2) khi một tập tin bị xóa. Điều này có thể phân giải bằng cách xác nhận rằng chúng tôi muốn xóa nó với git rm
. (3) có một số nơi có vẻ như là diff
chỉ hoạt động kém, ví dụ: nơi thay đổi chỉ là bổ sung một dòng trống.Các xung đột hợp pháp khác yêu cầu can thiệp thủ công nhưng trên toàn bộ chúng không phải là nút cổ chai lớn nhất. Các nút cổ chai lớn nhất là hoàn toàn chỉ ngồi đó nhìn chằm chằm vào "Rebasing (xxxx/yyyy)".
Hiện tại, các lần rebases cá nhân được bắt đầu từ các cam kết mới hơn đến các commit cũ hơn, tức là, bắt đầu từ đầu ra của git whatchanged
. Điều này có nghĩa là đợt rebase đầu tiên ảnh hưởng đến các cam kết của ngày hôm qua, và cuối cùng chúng ta sẽ rebasing cam kết từ 3 năm trước. Đi từ "mới hơn" sang "cũ hơn" có vẻ phản trực giác, nhưng cho đến nay tôi không tin rằng nó quan trọng trừ khi chúng tôi thay đổi nhiều hơn một pick
thành một số edit
khi gọi lệnh rebase. Tôi sợ làm điều này bởi vì xung đột sẽ đến, và tôi không muốn đối phó với một làn sóng thủy triều xung đột gợn sóng từ cố gắng để rebase tất cả mọi thứ trong một đi. Có lẽ ai đó biết cách để tránh điều đó? Tôi đã không thể đến với một.
Tôi bắt đầu xem xét các hoạt động bên trong của các đối tượng git 1! Nó có vẻ như có một cách hiệu quả hơn để đi bộ đồ thị đối tượng và chỉ cần thực hiện những thay đổi mà tôi muốn thực hiện. Xin lưu ý rằng kho lưu trữ này đến từ một kho lưu trữ SVN nơi chúng tôi đã không sử dụng thẻ hoặc các chi nhánh một cách hiệu quả (tôi đã cắt git filter-branch
), vì vậy chúng tôi có sự tiện lợi của một lịch sử đường thẳng. Không có chi nhánh git hoặc sáp nhập.
Tôi chắc chắn tôi đã bỏ sót một số thông tin quan trọng, nhưng bài đăng này dường như quá dài. Tôi sẽ cố hết sức để cung cấp thêm thông tin theo yêu cầu. Cuối cùng tôi có thể cần phải xuất bản các kịch bản khác nhau của tôi, đó là một khả năng. Đó là mục tiêu của tôi để tìm ra cách viết lại lịch sử như vậy trong một kho git; không tranh luận các phương pháp cấp phép và phát hành mã khác khả thi.
Cảm ơn!
Cập nhật 2012-06-17: Blog post với tất cả các chi tiết đẫm máu.
Nó khá mơ hồ đối với tôi, tôi không bao giờ cần thiết để làm một viết lại lịch sử đồ sộ, nhưng tôi biết rằng công cụ chính xác để làm điều đó là ['git filter-branch'] (http://www.kernel.org/pub/software/scm/git/docs/v1.7.3/git-filter-branch.html) . Tôi xin lỗi tôi không thể hữu ích hơn, tôi hy vọng nó giúp bạn đi đúng hướng. – KurzedMetal
@KurzedMetal: Tôi đã sử dụng 'filter-branch' trước khi bắt đầu tất cả việc rebasing này để loại bỏ các đường dẫn (filesystem) không liên quan đến bản phát hành này. (Kho lưu trữ SVN mà kho git này được tạo ra thậm chí còn lớn hơn và khó sử dụng hơn). Tuy nhiên, bạn có thể có một điểm mà các thay đổi kịch bản được thực hiện trong một 'bộ lọc' có thể hiệu quả hơn làm tất cả việc rebasing này. Tôi sẽ điều tra. – jonny0x5
'Nó đại diện cho khoảng 3 năm làm việc từ nhiều người và chúng tôi đã nhận được giấy phép để làm cho tất cả nguồn mở', tôi biết đó là chủ đề, nhưng tôi tò mò: P, tên dự án/trang chủ là gì? – KurzedMetal