2013-07-04 25 views
17

Chúng tôi có một số kho lưu trữ git đã phát triển thành kích thước không thể quản lý do việc bao gồm lịch sử tệp thử nghiệm nhị phân và tệp java .jar.Có thể thu gọn kho lưu trữ .git mà không cần viết lại lịch sử không?

Chúng tôi sắp sửa thực hiện git filter-branch trong các kho lưu trữ này, sao chép chúng ở mọi nơi chúng được sử dụng (từ hàng chục đến hàng trăm triển khai, tùy thuộc vào repo) và được cung cấp problems with rewriting history. có thể là bất kỳ giải pháp nào khác.

Lý tưởng nhất là tôi muốn bên ngoài các tệp vấn đề mà không cần viết lại lịch sử của từng kho lưu trữ. Về lý thuyết, điều này có thể xảy ra vì bạn đang kiểm tra cùng một tệp, với cùng kích thước và cùng một băm, chỉ tìm nguồn cung ứng từ một vị trí khác (từ xa thay vì lưu trữ đối tượng cục bộ). Không ai trong số các giải pháp tiềm năng tôi đã tìm thấy cho đến nay dường như cho phép tôi làm điều này.

Bắt đầu với git-annex, gần nhất tôi có thể tìm đến một giải pháp cho vấn đề của tôi là How to retroactively annex a file already in a git repo, nhưng như với chỉ cần loại bỏ các tập tin lớn, điều này đòi hỏi lịch sử được viết lại để chuyển đổi bản gốc git add thành một git annex add.

Di chuyển từ đó, tôi bắt đầu xem xét các dự án khác được liệt kê trên what git-annex is not, vì vậy tôi đã kiểm tra git-bigfiles, git-mediagit-fat. Rất tiếc, chúng tôi không thể sử dụng số điện thoại git-bigfiles ngã ba của git vì chúng tôi là một cửa hàng của Eclipse và sử dụng hỗn hợp gitEGit. Không giống như git-media hoặc git-fat cũng có thể làm những gì tôi muốn, vì trong khi bạn có thể thay thế các tệp lớn hiện tại bằng các phần tử tương đương bên ngoài, bạn vẫn cần viết lại lịch sử để xóa các tệp đã được cam kết.

Vì vậy, có thể thu gọn kho lưu trữ .git mà không cần viết lại lịch sử hay chúng ta nên quay lại kế hoạch sử dụng git filter-branch và toàn bộ quá trình triển khai lại?


Là một sang một bên, tin rằng này nên thể, nhưng có lẽ gắn liền với những hạn chế tương tự như những người git s shallow clone thực hiện.

Git đã hỗ trợ nhiều địa điểm nhất có thể cho các blob cùng, vì bất kỳ blob cụ thể có thể trong loose object store (.git/objects) hoặc trong một pack file (.git/objects) nên về mặt lý thuyết, bạn sẽ chỉ cần một cái gì đó giống như git-annex được nối trong ở cấp độ đó thay vì cao hơn (nghĩa là có khái niệm về tải xuống theo yêu cầu blob từ xa nếu bạn muốn). Thật không may tôi không thể tìm thấy bất cứ ai đã thực hiện hoặc thậm chí đề xuất bất cứ điều gì như thế này.

+0

Theo tôi có thể cho biết bạn đang hỏi cách viết lại lịch sử mà không cần viết lại lịch sử. – alternative

+0

@alternative không hoàn toàn, tôi hỏi nếu có một cách để mỏng kho lưu trữ * mà không * viết lại lịch sử. Tại thời điểm này có vẻ như sử dụng * nhái nông * có thể là cách duy nhất, nhưng những giới hạn có thể sẽ không hoạt động tốt với quy trình làm việc của chúng tôi và thậm chí nếu nó làm như vậy thì chúng sẽ chỉ làm mỏng bản sao cục bộ (không nhân bản). repos. –

+0

Cách duy nhất để "mỏng" kho lưu trữ sẽ được để xóa các nội dung bạn đang giảm béo - do đó, viết lại (đó là lý do tại sao mỗi câu trả lời nói rằng điều này là không thể). Không có bất kỳ vấn đề gì với lịch sử viết lại miễn là bạn làm điều đó một cách chính xác. Và có, các bản sao nông sẽ chỉ ảnh hưởng đến các kho lưu trữ địa phương. – alternative

Trả lời

8

Sắp xếp. Bạn có thể sử dụng Git's replace feature để đặt sang một bên lịch sử cồng kềnh lớn để nó chỉ được tải xuống nếu cần. Nó giống như một bản sao nông, nhưng không có giới hạn của một bản sao nông.

Ý tưởng là bạn khởi động lại một chi nhánh bằng cách tạo một cam kết gốc mới, sau đó chọn lấy cam kết của nhánh cũ. Thông thường bạn sẽ mất tất cả lịch sử theo cách này (điều này cũng có nghĩa là bạn không phải sao chép các tệp .jar lớn), nhưng nếu cần lịch sử, bạn có thể tìm nạp các cam kết lịch sử và sử dụng git replace để liên tục khâu chúng lại.

Xem Scott Chacon's excellent blog post để có giải thích và hướng dẫn chi tiết.

Ưu điểm của phương pháp này:

  • Lịch sử không được sửa đổi. Nếu bạn cần phải quay trở lại một cam kết cũ hơn với nó lớn .jars và tất cả mọi thứ, bạn vẫn có thể.
  • Nếu bạn không cần phải nhìn vào lịch sử cũ, kích thước của bản sao cục bộ của bạn là tốt đẹp và nhỏ, và bất kỳ bản sao mới bạn thực hiện sẽ không yêu cầu tải về tấn dữ liệu chủ yếu là vô dụng.

Nhược điểm của phương pháp này:

  • Lịch sử hoàn toàn không có sẵn theo mặc định — người dùng cần phải nhảy qua một số hoops để có được vào lịch sử.
  • Nếu bạn cần truy cập thường xuyên vào lịch sử, bạn sẽ vẫn tải xuống các cam kết cồng kềnh.
  • Cách tiếp cận này vẫn có một số vấn đề tương tự như viết lại lịch sử. Ví dụ, nếu kho mới của bạn trông như thế này:

    * modify bar (master) 
    | 
    * modify foo <--replace--> * modify foo (historical/master) 
    |       | 
    * instructions    * remove all of the big .jar files 
              | 
              * add another jar 
              | 
              * modify a jar 
              | 
    

    và ai đó có một chi nhánh cũ tắt của chi nhánh lịch sử mà họ hợp nhất trong:

    * merge feature xyz into master (master) 
    |\__________________________ 
    |       \ 
    * modify bar     * add feature xyz 
    |       | 
    * modify foo <--replace--> * modify foo (historical/master) 
    |       | 
    * instructions    * remove all of the big .jar files 
              | 
              * add another jar 
              | 
              * modify a jar 
              | 
    

    sau đó các cam kết lịch sử lớn sẽ xuất hiện trở lại trong bạn kho lưu trữ chính và bạn quay lại nơi bạn đã bắt đầu. Lưu ý rằng đây không phải là tồi tệ hơn so với lịch sử viết lại — ai đó có thể vô tình hợp nhất trong các cam kết viết lại trước.

    Điều này có thể được giảm nhẹ bằng cách thêm móc update vào kho lưu trữ được chia sẻ của bạn để từ chối mọi lần đẩy sẽ giới thiệu lại (các) cam kết gốc lịch sử.

+0

Wow, cảm ơn Richard, có vẻ như đó chỉ là những gì tôi đang tìm kiếm.Tôi sẽ xem nếu tôi có thể làm cho nó hoạt động vào tuần tới và nếu như vậy, sẽ có một đánh dấu đến theo cách của bạn quá ... –

+0

Ah, tôi thấy, vì vậy ví dụ viết lại lịch sử của * cam kết gần đây * để loại bỏ lớn cam kết lịch sử mà không cần phải viết lại lịch sử của những * commit lịch sử *, nhưng sử dụng 'git replace' để cho phép bạn mang lại * commit lịch sử * sau này nếu bạn cần. Vì vậy, đó không phải là những gì tôi đang theo sau, nhưng tôi sẽ suy nghĩ một số chi tiết về cách tôi có thể tận dụng nó để giải quyết vấn đề của tôi. –

+0

Tôi ước gì tôi biết về điều này khi chúng ta tạo ra repos 'git' từ repo' svn' cũ của chúng ta. Thay vì phải chọn giữa việc bắt đầu một kỷ nguyên mới mà không có lịch sử từ 'svn' hoặc bắt đầu' repo 'git' của chúng ta với nhiều năm tích lũy' svn' cruft, chúng ta có thể giữ toàn bộ 'svn' repo trong một bộ lịch sử' git' repos và sau đó sử dụng 'git replace' để đưa họ trở lại khi họ cần. Trong thực tế, tôi tự hỏi liệu chúng ta có thể vẫn có thể quay trở lại và thêm các mục tiêu truy tìm 'git replace'. Thú vị, rất thú vị ... –

4

Tôi không biết giải pháp nào có thể tránh viết lại lịch sử.

Trong trường hợp đó, làm sạch rpeo bằng công cụ như BFG- repo cleaner là giải pháp dễ nhất (dễ dàng hơn git filter-branch).

2

Tôi thành thật không thể nghĩ ra cách để làm điều đó.Nếu bạn nghĩ về những gì Git "hứa ​​hẹn" bạn như một người dùng, liên quan đến tính toàn vẹn dữ liệu, tôi không thể nghĩ ra cách bạn có thể xóa một tệp khỏi kho lưu trữ và giữ cùng một băm. Nói cách khác, nếu những gì bạn đang yêu cầu là có thể, thì Git sẽ ít đáng tin cậy hơn nhiều ...

8

Không, điều đó là không thể - Bạn sẽ phải viết lại lịch sử. Nhưng đây là một số gợi ý cho rằng:

  • As VonC mentioned: Nếu nó phù hợp với kịch bản của bạn, sử dụng BFG- repo cleaner - đó là dễ dàng hơn nhiều để sử dụng hơn git filter-branch.
  • Bạn không cần phải sao chép lại! Chỉ cần chạy các lệnh này thay vì git pull và bạn sẽ ổn thôi (thay thế originmaster với từ xa và chi nhánh của bạn):

    git fetch origin 
    git reset --hard origin/master 
    

    Nhưng lưu ý rằng không giống như git pull, bạn sẽ mất tất cả những thay đổi cục bộ không được đẩy lên máy chủ.

  • Nó giúp ích rất nhiều nếu bạn (hoặc ai đó trong nhóm của bạn) hiểu đầy đủ cách git nhìn thấy lịch sử và những gì git pull, git mergegit rebase (cũng như git rebase --onto) làm. Sau đó cung cấp cho tất cả mọi người tham gia đào tạo nhanh về cách xử lý tình huống viết lại này (5-10 phút là đủ, những điều cơ bản và những điều không nên làm).
  • Hãy lưu ý rằng git filter-branch không gây ra bất kỳ tác hại nào, nhưng gây ra rất nhiều quy trình làm việc tiêu chuẩn gây hại. Nếu mọi người không hành động phù hợp và hợp nhất lịch sử cũ, bạn có thể chỉ phải viết lại lịch sử nếu bạn không nhận thấy đủ sớm.
  • Bạn có thể ngăn mọi người sáp nhập (đẩy chính xác hơn) lịch sử cũ bằng cách viết (5 dòng) thích hợp update hook trên máy chủ. Chỉ cần kiểm tra xem lịch sử của người đứng đầu được đẩy có chứa một cam kết cũ cụ thể hay không.
+0

Cảm ơn Chronial. Vấn đề thực sự duy nhất với * not * tái nhân bản là phải 'reset' mỗi nhánh được sử dụng cục bộ (để loại bỏ tất cả các ref cục bộ đến nhánh lỗi thời) và chạy' git gc --prune = now --aggressive' để thu nhỏ repo. Nếu bạn làm điều này và repo * không * co lại, sau đó bạn biết rằng bạn bị mất một ref ở đâu đó. Việc nhân bản lại loại bỏ sự cần thiết cho tất cả các bước này (chúng tôi triển khai 20 repo 'git' repos của chúng tôi bằng cách sử dụng' buckminster' để tái nhân bản * mọi thứ * dễ dàng cho chúng ta). Đáng buồn thay, chúng tôi cũng sử dụng gitolite để lưu trữ repos 'git' của chúng tôi, nó lưu trữ hook' update' cho việc sử dụng riêng của nó. –

+0

Bạn không thể mở rộng móc 'cập nhật 'theo cùng một cách? – Chronial

+0

Tôi không biết * gitolite *, nhưng [móc và gitolite] (http://gitolite.com/gitolite/cust.html#hooks) nói rằng * Bạn có thể cài đặt bất kỳ móc nào ngoại trừ các móc này: (tất cả repos) dự trữ gitolite hook 'update' * vì vậy tôi sẽ phải đợi cho đến khi chuyên gia gitolite của chúng tôi quay trở lại để nói cho tôi biết nếu có cách nào đó xung quanh. –

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