2012-06-06 19 views
6

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 ...):

  1. 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 
    
  2. 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ới edit 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 
    
  3. Đố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.

+0

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

+0

@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

+0

'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

Trả lời

4

Sử dụng

git filter-branch -f --tree-filter '[[ -f README ]] && echo "---FOOTER---" >> README' HEAD 

về cơ bản có thêm một dòng chân đến tập tin README, và lịch sử sẽ trông giống như nó đã có từ những sáng tạo tập tin, tôi không chắc chắn nếu nó sẽ có đủ hiệu quả cho bạn nhưng đó là cách chính xác để làm điều đó.

Tạo kịch bản tùy chỉnh và có thể bạn sẽ kết thúc với lịch sử dự án tốt, làm quá nhiều "ma thuật" (rebase, perl, biên tập kịch bản, v.v.) có thể bị mất hoặc thay đổi lịch sử dự án theo cách không mong muốn.

jon (OP) đã sử dụng mẫu cơ bản này để đạt được mục tiêu với việc đơn giản hóa đáng kể và tăng tốc.

git filter-branch -d /dev/shm/git --tree-filter \ 
'perl /path/to/find-add-license.pl' --prune-empty HEAD 

Một vài quan sát hiệu suất quan trọng.

  • Sử dụng -d <directory> tham số trỏ đến một thư mục ramdisk (như /dev/shm/foo) sẽ cải thiện tốc độ đáng kể.

  • Thực hiện tất cả thay đổi từ một tập lệnh, sử dụng các tính năng ngôn ngữ được tích hợp sẵn, khi sử dụng các tiện ích nhỏ (như find), sẽ làm chậm quá trình nhiều lần. Tránh tình trạng này:

    git filter-branch -d /dev/shm/git --tree-filter \ 
    'find . -name "*.[chS]" -exec perl /path/to/just-add-license.pl \{\} \;' \ 
    --prune-empty HEAD 
    

Đây là một phiên bản làm vệ sinh của kịch bản perl OP sử dụng:

#!/usr/bin/perl -w 
use File::Slurp; 
use File::Find; 

my @dirs = qw(aDir anotherDir nested/DIR); 
my $header = "Please put me at the top of each file."; 

foreach my $dir(@dirs) { 
    if (-d $dir) { 
    find(\&Wanted, $dir); 
    } 
} 

sub Wanted { 
    /\.c$|\.h$|\.S$/ or return; # *.[chS] 
    my $file = $_; 
    my $contents = read_file($file); 
    $contents =~ s/\r\n?/\n/g; # convert DOS or old-Mac line endings to Unix 
    unless($contents =~ /Please put me at the top of each file\./) { 
    write_file($file, {atomic => 1}, $header, $contents); 
    } 
} 
+0

Cảm ơn. Điều này có vẻ như nó đang di chuyển đúng hướng. Một điều không rõ ràng với tôi là tại sao điều này không giới thiệu '--- FOOTER ---' một lần cho mọi cam kết sau khi tệp README tồn tại. Trường hợp trong "khéo léo" của '--tree-filter' không git tìm ra để chỉ chạy lệnh đó một lần? Ví dụ: 'git filter-branch -f --tree-filter 'echo" i ran ">>/tmp/ran.log' HEAD' in" i ran "một lần cho mọi cam kết vào tệp tạm thời đó. – jonny0x5

+1

Đó là cách 'filter-branch' hoạt động, nó" loops "trough tất cả các commit của nhánh bạn đã chỉ định. Và bạn thực sự có cơ hội tất cả các cam kết, bạn không thể thoát khỏi điều đó, 'git' không lưu trữ' diff ', nó lưu trữ nội dung tập tin, vì vậy nếu bạn có một lịch sử' A - B - C', thêm "footer" để commit 'A' nhưng không có' B', nó giống như bạn đã xóa nó trong 'B', nó không lan rộng, vì vậy nếu bạn muốn giữ" footer "của bạn thông qua tất cả lịch sử, bạn sẽ phải thêm nó để commit 'A',' B' và 'C', giống như' filter-branch' (chạy cho mỗi commit). – KurzedMetal

+0

Cảm ơn rất nhiều vì lời giải thích này. Tôi đã có thể hoàn thành mục tiêu của mình bằng cách sử dụng 'git filter-branch --tree-filter' và tạo ra một đoạn mã ngắn (nhỏ hơn 20 dòng) perl để thực hiện các thay đổi mong muốn. Sự hiểu biết của tôi là thực hành tốt nhất của stackoverflow là chỉnh sửa câu trả lời của bạn để làm rõ cách giải quyết câu hỏi cụ thể của tôi, upvote và đánh dấu là đã được giải quyết. – jonny0x5

-1

Các đốm màu là địa chỉ nội dung. Bạn không thể sửa đổi một tệp đơn lẻ mà không thay đổi mã băm của nó, điều này làm thay đổi blob thư mục được tham chiếu bởi bất kỳ cam kết nào bao gồm nó, và do đó bất kỳ cam kết nào bắt nguồn từ nó. Về cơ bản bạn phải viết lại thế giới, khi tôi hiểu được vấn đề. Tôi đoán tôi có thể tưởng tượng một thuật toán đã làm tất cả điều này làm việc theo thứ tự DAG ngược lại, với một bảng băm lớn của băm đối tượng gốc để sửa đổi, mà chỉ viết lại mỗi đối tượng một lần.

Nếu bạn đã có giải pháp đúng (thậm chí một trong ba ngày), có thực sự đáng giá để thử tối ưu hóa điều này không? Tôi không thể tưởng tượng thực sự nhận được mã này được gỡ lỗi và hoạt động chính xác, đủ để giải phóng kết quả trong vòng chưa đầy ba ngày mà giải pháp ngây thơ sẽ mất.

+0

Trong 3 ngày bạn có thể làm nhiều việc hơn là viết lại lịch sử của repo, nó không phải là kỷ nguyên máy tính của Cray. Vì vậy, có nó là tối ưu INMHO – CharlesB

+0

Tôi có nghĩa là nó sẽ mất hơn ba ngày ** phát triển ** trước khi kết quả đã sẵn sàng để phát hành. –

+0

Xin lỗi, những điều chưa đọc, bạn có thể chỉnh sửa bài đăng của mình để tôi có thể xóa bỏ phiếu giảm giá của mình không? – CharlesB

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