Cách xóa các đốm màu không tham chiếu khỏi git repo của tôi


124

Tôi có một repo GitHub có hai nhánh - chính và phát hành.

Nhánh phát hành chứa các tệp phân phối nhị phân góp phần vào kích thước repo rất lớn (> 250MB), vì vậy tôi quyết định dọn dẹp mọi thứ.

Đầu tiên, tôi đã xóa nhánh phát hành từ xa, thông qua git push origin :release

Sau đó, tôi đã xóa nhánh phát hành cục bộ. Đầu tiên, tôi đã thử git branch -d release, nhưng git cho biết "lỗi: Nhánh 'phát hành' không phải là tổ tiên của HEAD hiện tại của bạn." đó là sự thật, vì vậy sau đó tôi đã git branch -D releasebuộc phải xóa nó.

Nhưng kích thước kho lưu trữ của tôi, cả cục bộ và trên GitHub, vẫn rất lớn. Vì vậy, sau đó tôi chạy qua danh sách các lệnh git thông thường, giống như git gc --prune=today --aggressive, không may mắn.

Bằng cách làm theo hướng dẫn của Charles Bailey tại SO 1029969, tôi có thể lấy danh sách SHA1 cho các đốm màu lớn nhất. Sau đó tôi sử dụng tập lệnh từ SO 460331 để tìm các đốm màu ... và năm đốm màu lớn nhất không tồn tại, mặc dù các đốm màu nhỏ hơn được tìm thấy, vì vậy tôi biết tập lệnh đang hoạt động.

Tôi nghĩ những blog này là nhị phân từ nhánh phát hành, và bằng cách nào đó, chúng đã bị bỏ lại sau khi xóa nhánh đó. Cách phù hợp để loại bỏ chúng là gì?


Bạn đang sử dụng phiên bản Git nào? Và bạn đã thử stackoverflow.com/questions/1106529/… ?
VonC

git phiên bản 1.6.2.3 Tôi đã thử gc và lược bớt các đối số khác nhau. Tôi đã không thử repack -a -d -l, chỉ chạy nó, không có thay đổi.
kkrugler

2
Thông tin mới - một bản sao mới từ GitHub không còn có các đốm màu không được tham chiếu và giảm xuống "chỉ" 84MB từ 250MB.
kkrugler

Câu trả lời:


219

... và không cần giải thích gì thêm, tôi có thể giới thiệu cho bạn lệnh hữu ích này, "git-gc-all", đảm bảo loại bỏ tất cả git rác của bạn cho đến khi chúng có thể xuất hiện thêm các biến cấu hình:

git -c gc.reflogExpire=0 -c gc.reflogExpireUnreachable=0 -c gc.rerereresolved=0 -c gc.rerereunresolved=0 -c gc.pruneExpire=now gc

Bạn cũng có thể cần phải chạy một cái gì đó như thế này trước, bạn ơi, git phức tạp !!

git remote rm origin
rm -rf .git/refs/original/ .git/refs/remotes/ .git/*_HEAD .git/logs/
git for-each-ref --format="%(refname)" refs/original/ | xargs -n1 --no-run-if-empty git update-ref -d

Bạn cũng có thể cần phải xóa một số thẻ, cảm ơn Zitrax:

git tag | xargs git tag -d

Tôi đặt tất cả điều này vào một script: git-gc-all-ferocious .


1
Hấp dẫn. Một sự thay thế tốt cho câu trả lời chung chung hơn của tôi. +1
VonC

10
Điều này xứng đáng được nhiều phiếu bầu hơn. Cuối cùng nó đã loại bỏ rất nhiều đối tượng git mà các phương thức khác sẽ giữ lại. Cảm ơn!
Jean-Philippe Pellet,

1
Đã ủng hộ. Chà, tôi không biết mình vừa làm gì nhưng có vẻ dọn dẹp rất nhiều. Bạn có thể nói rõ hơn về những gì nó làm được không? Tôi có cảm giác nó xóa sạch tất cả của tôi objects. Đó là gì và tại sao chúng (dường như) không liên quan?
Redsandro

2
@Redsandro, theo tôi hiểu, các lệnh "git rm origin", "rm" và "git update-ref -d" loại bỏ các tham chiếu đến các cam kết cũ cho điều khiển từ xa và các lệnh này có thể ngăn chặn việc thu gom rác. Các tùy chọn để "git gc" yêu cầu nó không giữ các cam kết cũ khác nhau, nếu không nó sẽ giữ chúng trong một thời gian. Ví dụ: gc.rerereresolved dành cho "hồ sơ về hợp nhất xung đột mà bạn đã giải quyết trước đó", theo mặc định được lưu giữ trong 60 ngày. Các tùy chọn đó có trong git-gc manpage. Tôi không phải là chuyên gia về git và không biết chính xác tất cả những thứ này làm gì. Tôi đã tìm thấy chúng từ các trang manpages và gửi .git cho các refs cam kết.
Sam Watkins,

1
Đối tượng git là một tệp nén hoặc cây hoặc cam kết trong git repo của bạn, bao gồm cả nội dung cũ từ lịch sử. git gc xóa các đối tượng không cần thiết. Nó giữ các đối tượng vẫn cần thiết cho repo hiện tại của bạn và lịch sử của nó.
Sam Watkins

81

Như được mô tả ở đây , nếu bạn muốn xóa vĩnh viễn mọi thứ chỉ được tham chiếu qua reflog , chỉ cần sử dụng

git reflog expire --expire-unreachable=now --all
git gc --prune=now

git reflog expire --expire-unreachable=now --allloại bỏ tất cả các tham chiếu của các cam kết không thể truy cập trong reflog.

git gc --prune=now tự loại bỏ các cam kết.

Chú ý : Chỉ sử dụng git gc --prune=nowsẽ không hoạt động vì những cam kết đó vẫn được tham chiếu trong bản tóm tắt. Do đó, việc xóa reflog là bắt buộc. Cũng lưu ý rằng nếu bạn sử dụng rererenó, các tham chiếu bổ sung không bị xóa bởi các lệnh này. Xem git help rereređể biết thêm chi tiết. Ngoài ra, bất kỳ cam kết nào được tham chiếu bởi các nhánh hoặc thẻ cục bộ hoặc từ xa sẽ không bị xóa vì chúng được coi là dữ liệu có giá trị theo git.


14
Nó làm việc, nhưng bằng cách nào đó tôi đã mất ẩn nấp lưu của tôi trong quá trình này (không có gì quan trọng trong trường hợp của tôi, chỉ là một cách thận trọng cho người khác)
Amro

1
tại sao không - vi phạm?
JoelFan

3
Tôi nghĩ câu trả lời này cần một cảnh báo rõ ràng, tốt nhất là ở trên cùng. Đề xuất chỉnh sửa của tôi đã bị từ chối, bởi vì tôi đoán tôi nên đề xuất nó với tác giả trong một nhận xét? Vui lòng chấp nhận bản chỉnh sửa này stackoverflow.com/review/suggested-edits/26023988 hoặc thêm cảnh báo theo cách của riêng bạn. Ngoài ra, điều này làm giảm tất cả các kho lưu trữ của bạn . Điều đó cũng nên được ghi nhớ trong cảnh báo!
Inigo

Tôi đã thử nghiệm với git phiên bản 2.17 và các cam kết được lưu trữ sẽ không bị xóa bởi các lệnh trên. Bạn có chắc là bạn đã không chạy bất kỳ lệnh bổ sung nào không?
Mikko Rantalainen

1
git fetch --prunegiảm kích thước hơn nữa vì xóa các đốm màu cục bộ.
hectorpal

33

Như đã đề cập trong câu trả lời SO này , git gcthực sự có thể tăng kích thước của repo!

Xem thêm chủ đề này

Giờ đây, git có một cơ chế an toàn để không xóa các đối tượng không được tham chiếu ngay khi đang chạy ' git gc'.
Theo mặc định, các đối tượng không tham chiếu được giữ trong khoảng thời gian 2 tuần. Điều này là để giúp bạn dễ dàng khôi phục các nhánh hoặc cam kết đã vô tình bị xóa, hoặc để tránh một cuộc đua trong đó một đối tượng vừa được tạo đang trong quá trình tồn tại nhưng chưa được tham chiếu có thể bị xóa bởi một git gcquy trình '' chạy song song.

Vì vậy, để cung cấp thời gian gia hạn đó cho các đối tượng được đóng gói nhưng không được tham chiếu, quá trình đóng gói lại đẩy các đối tượng không được tham chiếu đó ra khỏi gói về dạng lỏng lẻo để chúng có thể già đi và cuối cùng bị cắt bớt.
Mặc dù vậy, các đối tượng trở nên không được tham chiếu thường không nhiều. Có khá nhiều đối tượng không được tham chiếu 404855 và việc gửi các đối tượng đó ngay từ đầu thông qua một bản sao là điều ngu ngốc và hoàn toàn lãng phí băng thông mạng.

Dù sao ... Để giải quyết vấn đề của bạn, bạn chỉ cần chạy ' git gc' với --prune=nowđối số để vô hiệu hóa thời gian gia hạn đó và loại bỏ các đối tượng không được tham chiếu đó ngay lập tức (chỉ an toàn nếu không có hoạt động git nào khác diễn ra cùng lúc dễ dàng đảm bảo trên máy trạm).

Và BTW, sử dụng ' git gc --aggressive' với phiên bản git mới hơn (hoặc ' git repack -a -f -d --window=250 --depth=250')

Các chủ đề cùng đề cập đến :

 git config pack.deltaCacheSize 1

Điều đó giới hạn kích thước bộ nhớ cache delta ở một byte (vô hiệu hóa nó một cách hiệu quả) thay vì mặc định là 0, nghĩa là không giới hạn. Với điều đó, tôi có thể đóng gói lại kho lưu trữ đó bằng git repacklệnh trên trên hệ thống x86-64 với 4GB RAM và sử dụng 4 luồng (đây là lõi tứ). Mặc dù vậy, mức sử dụng bộ nhớ thường trú đã tăng lên gần 3,3GB.

Nếu máy của bạn là SMP và bạn không có đủ RAM thì bạn có thể giảm số luồng chỉ còn một:

git config pack.threads 1

Ngoài ra, bạn có thể giới hạn hơn nữa việc sử dụng bộ nhớ với --window-memory argument' git repack'.
Ví dụ, việc sử dụng --window-memory=128Mnên giữ giới hạn trên hợp lý về việc sử dụng bộ nhớ tìm kiếm delta mặc dù điều này có thể dẫn đến kết quả khớp delta kém tối ưu hơn nếu repo chứa nhiều tệp lớn.


Ở mặt trước nhánh bộ lọc, bạn có thể xem xét (thận trọng) tập lệnh này

#!/bin/bash
set -o errexit

# Author: David Underhill
# Script to permanently delete files/folders from your git repository.  To use 
# it, cd to your repository's root and then run the script with a list of paths
# you want to delete, e.g., git-delete-history path1 path2

if [ $# -eq 0 ]; then
    exit 0
fi

# make sure we're at the root of git repo
if [ ! -d .git ]; then
    echo "Error: must run this script from the root of a git repository"
    exit 1
fi

# remove all paths passed as arguments from the history of the repo
files=$@
git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch $files" HEAD

# remove the temporary history git-filter-branch otherwise leaves behind for a long time
rm -rf .git/refs/original/ && git reflog expire --all &&  git gc --aggressive --prune

stackoverflow.com/questions/359424/… cũng là một khởi đầu tốt cho việc filter-branchsử dụng lệnh.
VonC

Xin chào VonC - NI'd đã thử git gc trimne = bây giờ không may. Nó thực sự trông giống như một lỗi git, ở chỗ tôi bị dính các đốm màu không tham chiếu cục bộ sau khi xóa nhánh, nhưng chúng không có ở đó với bản sao mới của repo GitHub ... vì vậy nó chỉ là một vấn đề cục bộ repo. Nhưng tôi có các tệp bổ sung mà tôi muốn xóa, vì vậy tập lệnh bạn đã tham khảo ở trên rất tuyệt - cảm ơn!
kkrugler


12

Mỗi khi HEAD của bạn di chuyển, git theo dõi điều này trong reflog. Nếu bạn đã xóa các cam kết, bạn vẫn có "cam kết treo" bởi vì chúng vẫn được tham chiếu bởi trong reflog~ 30 ngày. Đây là mạng lưới an toàn khi bạn vô tình xóa các cam kết.

Bạn có thể sử dụng git refloglệnh xóa các cam kết cụ thể, đóng gói lại, v.v. hoặc chỉ lệnh cấp cao:

git gc --prune=now

5

Bạn có thể sử dụng git forget-blob.

Cách sử dụng khá đơn giản git forget-blob file-to-forget. Bạn có thể biết thêm thông tin tại đây

https://ownyourbits.com/2017/01/18/compleently-remove-a-file-from-a-git-repository-with-git-forget-blob/

Nó sẽ biến mất khỏi tất cả các cam kết trong lịch sử, nhật ký lại, thẻ của bạn, v.v.

Thỉnh thoảng tôi gặp phải cùng một vấn đề và mọi lúc tôi phải quay lại bài đăng này và những bài khác, đó là lý do tại sao tôi tự động hóa quy trình.

Tín dụng cho những người đóng góp như Sam Watkins


2

Hãy thử sử dụng git-filter-branch - nó không loại bỏ các đốm màu lớn, nhưng nó có thể loại bỏ các tệp lớn mà bạn chỉ định khỏi toàn bộ repo. Đối với tôi, nó giảm kích thước repo từ hàng trăm MB xuống còn 12 MB.


6
Bây giờ đó là một lệnh đáng sợ :) Tôi sẽ phải thử nó khi git-fu của tôi cảm thấy mạnh hơn.
kkrugler

bạn có thể nói lại điều đó. Tôi luôn cảnh giác với bất kỳ lệnh nào thao túng lịch sử của kho lưu trữ. Mọi thứ có xu hướng trở nên rất sai khi nhiều người đang đẩy và kéo từ kho lưu trữ đó và đột nhiên một loạt các đối tượng mà git đang mong đợi không có ở đó.
Jonathan Dumaine,

1

Đôi khi, lý do mà "gc" không hoạt động tốt là do có một bản rebase hoặc kho lưu trữ chưa hoàn thành dựa trên một cam kết cũ.


Hoặc cam kết cũ được tham chiếu bởi HEAD, ORIG_HEAD, FETCH_HEAD, reflog hoặc một số thứ khác mà git tự động tiếp tục cố gắng đảm bảo rằng nó không bao giờ mất bất cứ thứ gì có giá trị. Nếu bạn thực sự muốn mất tất cả những thứ đó, bạn phải đi xa hơn để làm như vậy.
Mikko Rantalainen

1

Để thêm một mẹo khác, đừng quên sử dụng git remote trimne để xóa các nhánh lỗi thời của điều khiển từ xa trước khi sử dụng git gc

bạn có thể thấy chúng với git branch -a

Nó thường hữu ích khi bạn tìm nạp từ các kho lưu trữ github và fork ...


1

Trước khi thực hiện git filter-branchgit gc, bạn nên xem lại các thẻ có trong repo của bạn. Bất kỳ hệ thống thực nào có gắn thẻ tự động cho những thứ như tích hợp và triển khai liên tục sẽ làm cho các đối tượng không mong muốn vẫn được các thẻ này tham chiếu, do đó gckhông thể xóa chúng và bạn vẫn sẽ tiếp tục tự hỏi tại sao kích thước của repo vẫn lớn như vậy.

Cách tốt nhất để thoát khỏi tất cả những thứ chưa muốn là chạy git-filter& git gcvà sau đó đẩy tổng thể để một repo trần mới. Repo trần mới sẽ có cây đã được làm sạch.

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.