Thu gọn lịch sử của kho lưu trữ git


85

Chúng tôi có một dự án git có lịch sử khá lớn.

Cụ thể, trong giai đoạn đầu của dự án, có khá nhiều tệp tài nguyên nhị phân trong dự án, chúng hiện đã bị xóa vì chúng là tài nguyên bên ngoài hiệu quả.

Tuy nhiên, kích thước của kho lưu trữ của chúng tôi là> 200MB (tổng số lần kiểm tra hiện tại là ~ 20MB) do các tệp này đã được cam kết trước đó.

Những gì chúng tôi muốn làm là "thu gọn" lịch sử để kho lưu trữ dường như đã được tạo từ một bản sửa đổi muộn hơn ban đầu. Ví dụ

1-----2-----3-----4-----+---+---+
                   \       /
                    +-----+---+---+
  1. Đã tạo kho lưu trữ
  2. Đã thêm tập hợp lớn các tệp nhị phân
  3. Đã xóa tập hợp lớn các tệp nhị phân
  4. Dự định 'bắt đầu' mới của kho lưu trữ

Vì vậy, hiệu quả là chúng tôi muốn mất lịch sử dự án trước một thời điểm nhất định. Tại thời điểm này chỉ có một nhánh, vì vậy không có gì phức tạp khi cố gắng xử lý nhiều điểm bắt đầu, v.v. Tuy nhiên, chúng tôi không muốn mất tất cả lịch sử và bắt đầu một kho lưu trữ mới với phiên bản hiện tại.

Điều này có khả thi không, hay chúng ta phải chịu đựng một kho lưu trữ quá tải mãi mãi?

Câu trả lời:


89

Bạn có thể xóa mã nhị phân và giữ lại phần còn lại của lịch sử. Git cho phép bạn sắp xếp lại và 'xóa' các cam kết trước đó, vì vậy bạn có thể chỉ kết hợp các cam kết thêm và xóa các tệp nhị phân lớn của mình. Nếu tất cả các lần thêm được thực hiện trong một cam kết và xóa trong một cam kết khác, điều này sẽ dễ dàng hơn nhiều so với việc xử lý từng tệp.

$ git log --stat       # list all commits and commit messages 

Tìm kiếm điều này cho các cam kết thêm và xóa các tệp nhị phân của bạn và ghi chú SHA1 của chúng, chẳng hạn như 2bcdef3cdef3.

Sau đó, để chỉnh sửa lịch sử của repo, hãy sử dụng rebase -ilệnh với tùy chọn tương tác của nó, bắt đầu với cấp độ gốc của cam kết nơi bạn đã thêm các tệp nhị phân của mình. Nó sẽ khởi chạy $ EDITOR của bạn và bạn sẽ thấy danh sách các cam kết bắt đầu bằng 2bcdef:

$ git rebase -i 2bcdef^    # generate a pick list of all commits starting with 2bcdef
# Rebasing zzzzzz onto yyyyyyy 
# 
# Commands: 
#  pick = use commit 
#  edit = use commit, but stop for amending 
#  squash = use commit, but meld into previous commit 
# 
# If you remove a line here THAT COMMIT WILL BE LOST.
#
pick 2bcdef   Add binary files and other edits
pick xxxxxx   Another change
  .
  .
pick 3cdef3   Remove binary files; link to them as external resources
  .
  .

Chèn squash 3cdef3làm dòng thứ hai và xóa dòng có nội dung pick 3cdef3khỏi danh sách. Bây giờ bạn có một danh sách các hành động cho tương tác rebasesẽ kết hợp các cam kết thêm và xóa các tệp nhị phân của bạn thành một cam kết có sự khác biệt chỉ là bất kỳ thay đổi nào khác trong các cam kết đó. Sau đó, nó sẽ áp dụng lại tất cả các cam kết tiếp theo theo thứ tự, khi bạn yêu cầu nó hoàn thành:

$ git rebase --continue

Quá trình này sẽ mất một hoặc hai phút.
Bây giờ bạn có một repo mà không còn các tệp nhị phân đến hoặc đi. Nhưng chúng vẫn sẽ chiếm dung lượng vì theo mặc định, Git giữ các thay đổi trong vòng 30 ngày trước khi chúng có thể được thu gom, vì vậy bạn có thể thay đổi quyết định của mình. Nếu bạn muốn xóa chúng ngay bây giờ:

$ git reflog expire --expire=1.minute refs/heads/master
      #all deletions up to 1 minute  ago available to be garbage-collected
$ git fsck --unreachable      # lists all the blobs(files) that will be garbage-collected
$ git prune
$ git gc                      

Bây giờ bạn đã loại bỏ bloat nhưng vẫn giữ phần còn lại của lịch sử của bạn.


7
Bạn chỉ cần nhớ nếu những người khác đã lấy từ kho đó, việc viết lại lịch sử sẽ gây nhầm lẫn cho việc kéo của họ. Hướng dẫn sử dụng git-rebase giải thích cách khôi phục các repo khác đó. kernel.org/pub/software/scm/git/docs/git-rebase.html
Otto

đây là một câu trả lời tuyệt vời cho vấn đề cụ thể của người dùng, nhưng không phải cho câu hỏi thực tế! câu trả lời của davitenio là một câu trả lời tuyệt vời cho câu hỏi thực tế.
Sam Watkins

27

Bạn có thể sử dụng git filter-branchcác phép ghép để làm cho cam kết số 4 trở thành cam kết gốc mới của nhánh của bạn. Chỉ cần tạo tệp .git/info/graftsvới chỉ một dòng trong đó chứa SHA1 của cam kết số 4.

Nếu bây giờ bạn thực hiện một git loghoặc gitkbạn sẽ thấy rằng các lệnh đó sẽ hiển thị cam kết số 4 là gốc của nhánh của bạn. Nhưng sẽ không có gì thực sự thay đổi trong kho lưu trữ của bạn. Bạn có thể xóa .git/info/graftsvà đầu ra của git loghoặc gitksẽ như trước. Để thực sự làm cho cam kết số 4 trở thành gốc mới, bạn sẽ phải chạy git filter-branch, không có đối số.


Điều này tốt hơn nhiều so với rebase vì nó không có vấn đề gì khi bảo toàn các cam kết hợp nhất và không làm cho dấu thời gian thay đổi. Dễ dàng hơn và nhanh hơn tất cả các phương pháp rebase.
mmrobins

Trên thực tế, có cách nào để xóa tất cả các cam kết không còn là một phần của nhánh đó nữa không? git gc --prune=0dường như không làm sạch chúng.
Verhogen

1
@verhogen git gc --prune=nowthực hiện xóa tất cả các cam kết không còn được tham chiếu. Nếu điều này không hiệu quả với bạn, thì bạn có thể có một số nhánh theo dõi từ xa vẫn tham chiếu đến gốc cũ. Liệt kê với git branch -r, sau đó xóa nhánh từ xa, ví dụ với với git branch -rd origin/mastervà sau đó chạy git gc --prune=nowlại.
kayahr

20

Cảm ơn bài đăng của JesperE mà tôi đã xem xét git-filter-branch- đó thực sự có thể là những gì bạn muốn. Có vẻ như bạn cũng có thể giữ lại các cam kết trước đó ngoại trừ chúng sẽ được sửa đổi vì Big Files của bạn đã bị xóa. Từ trang git-filter-branch man :

Giả sử bạn muốn xóa một tệp (chứa thông tin bí mật hoặc vi phạm bản quyền) khỏi tất cả các cam kết:

git filter-branch --tree-filter 'rm filename' HEAD

Hãy nhớ đọc trang người đàn ông đó ... rõ ràng bạn muốn thực hiện việc này trên một bản sao dự phòng của kho lưu trữ của mình để đảm bảo nó hoạt động như mong đợi.


2
Kiểm tra liên kết github của ... có một số tùy chọn mạnh mẽ với lệnh git-filter-branch: help.github.com/articles/remove-sensitive-data
ricosrealm

5

Có phải git-fast-exportnhững gì bạn đang tìm kiếm?

NAME
   git-fast-export - Git data exporter

SYNOPSIS
   git-fast-export [options] | git-fast-import

DESCRIPTION
   This program dumps the given revisions in a form suitable to be piped into git-fast-
   import(1).

   You can use it as a human readable bundle replacement (see git-bundle(1)), or as a kind
   of an interactive git-filter-branch(1).
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.