Xóa thư mục và nội dung của nó khỏi lịch sử của git / GitHub


318

Tôi đã làm việc trên một kho lưu trữ trên tài khoản GitHub của mình và đây là vấn đề tôi gặp phải.

  • Dự án Node.js với một thư mục có vài gói npm được cài đặt
  • Các gói trong node_modulesthư mục
  • Đã thêm thư mục đó vào kho git và đẩy mã lên github (lúc đó không nghĩ về phần npm)
  • Nhận ra rằng bạn không thực sự cần thư mục đó là một phần của mã
  • Xóa thư mục đó, đẩy nó

Trong trường hợp đó, kích thước của tổng số git repo là khoảng 6 MB trong đó mã thực tế (tất cả ngoại trừ thư mục đó) chỉ khoảng 300 KB .

Bây giờ, điều tôi đang tìm kiếm cuối cùng là một cách để loại bỏ các chi tiết của thư mục gói đó khỏi lịch sử của git, vì vậy nếu ai đó sao chép nó, họ không phải tải xuống lịch sử trị giá 6mb trong đó các tệp thực tế duy nhất họ sẽ nhận được như cam kết cuối cùng sẽ là 300KB.

Tôi đã tìm kiếm các giải pháp khả thi cho việc này và đã thử 2 phương pháp này

Gist có vẻ như nó hoạt động khi sau khi chạy tập lệnh, nó cho thấy rằng nó đã thoát khỏi thư mục đó và sau đó nó cho thấy 50 cam kết khác nhau đã được sửa đổi. Nhưng nó đã không cho phép tôi đẩy mã đó. Khi tôi cố gắng đẩy nó, nó nói Branch up to datenhưng cho thấy 50 lần cam kết đã được sửa đổi khi a git status. Hai phương pháp khác cũng không giúp được gì.

Bây giờ mặc dù nó cho thấy rằng nó đã thoát khỏi lịch sử của thư mục đó, khi tôi kiểm tra kích thước của repo đó trên localhost của mình, nó vẫn còn khoảng 6MB. (Tôi cũng đã xóa refs/originalthư mục nhưng không thấy sự thay đổi kích thước của repo).

Điều tôi đang tìm cách làm rõ là, nếu có một cách để loại bỏ không chỉ lịch sử cam kết (đó là điều duy nhất tôi nghĩ đã xảy ra) mà cả những tập tin mà git đang giữ giả sử muốn quay ngược lại.

Hãy nói rằng một giải pháp được trình bày cho điều này và được áp dụng trên localhost của tôi nhưng không thể sao chép vào repo GitHub đó, có thể sao chép repo đó, quay lại cam kết đầu tiên thực hiện thủ thuật và đẩy nó (hoặc điều đó có nghĩa là git sẽ vẫn có một lịch sử của tất cả những cam kết đó? - hay còn gọi là 6MB).

Mục tiêu cuối cùng của tôi ở đây là về cơ bản là tìm ra cách tốt nhất để loại bỏ nội dung thư mục khỏi git để người dùng không phải tải xuống các nội dung có giá trị 6 MB và vẫn có thể có các cam kết khác không bao giờ chạm vào thư mục mô-đun (điều đó thật tuyệt nhiều trong số họ) trong lịch sử của git.

Tôi có thể làm cái này như thế nào?


3
Nếu bất kỳ câu trả lời nào dưới đây giải quyết được vấn đề của bạn, có lẽ bạn nên xem xét chấp nhận một câu trả lời cho câu hỏi của mình. meta.stackexchange.com/questions/5234/ Cách
starbeamrainbowlabs 22/03/2017

Câu trả lời tốt nhất là: stackoverflow.com/a/32886427/5973334
Kuzeko

Câu trả lời:


556

Nếu bạn ở đây để sao chép-dán mã:

Đây là một ví dụ loại bỏ node_moduleskhỏi lịch sử

git filter-branch --tree-filter "rm -rf node_modules" --prune-empty HEAD
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
echo node_modules/ >> .gitignore
git add .gitignore
git commit -m 'Removing node_modules from git history'
git gc
git push origin master --force

Những gì git thực sự làm:

Dòng đầu tiên lặp qua tất cả các tham chiếu trên cùng một cây ( --tree-filter) là HEAD (nhánh hiện tại của bạn), chạy lệnh rm -rf node_modules. Lệnh này xóa thư mục node_modules ( -r, không có -r, rmsẽ không xóa các thư mục), không có lời nhắc nào được đưa ra cho người dùng ( -f). Việc thêm vào --prune-emptyxóa vô dụng (không thay đổi bất cứ điều gì) cam kết đệ quy.

Dòng thứ hai xóa tham chiếu đến nhánh cũ đó.

Phần còn lại của các lệnh tương đối đơn giản.


3
Chỉ là một lưu ý phụ: Tôi đã sử dụng git count-objects -vđể kiểm tra xem các tệp có thực sự bị xóa hay không nhưng kích thước của kho lưu trữ vẫn giữ nguyên cho đến khi tôi nhân bản lại kho lưu trữ. Tôi nghĩ rằng đó là bản sao của tất cả các tệp gốc mà tôi nghĩ.
Davide Icardi

4
Với một git không cổ, điều này có lẽ nên đọc --force-with-lease, không --force.
Griwes

4
Không có lệnh nào trong số này hoạt động trên windows. Hoặc ít nhất không phải Windows 10, vui lòng đăng lên HĐH rằng "cắt và dán" hoạt động vào
David

3
Đối với người dùng Windows 10, tính năng này hoạt động độc đáo dưới Bash cho Windows (Tôi đã sử dụng Ubuntu)
Andrej Kyselica

3
Tôi đã thử nó với windows shell và với git bash, và không hoạt động. Lệnh đầu tiên vượt qua, lệnh thứ hai thất bại!
Mohy Eldeen

240

Tôi thấy rằng --tree-filtertùy chọn được sử dụng trong các câu trả lời khác có thể rất chậm, đặc biệt là trên các kho lưu trữ lớn hơn với nhiều cam kết.

Đây là phương pháp tôi sử dụng để loại bỏ hoàn toàn một thư mục khỏi lịch sử git bằng --index-filtertùy chọn, nó chạy nhanh hơn nhiều:

# Make a fresh clone of YOUR_REPO
git clone YOUR_REPO
cd YOUR_REPO

# Create tracking branches of all branches
for remote in `git branch -r | grep -v /HEAD`; do git checkout --track $remote ; done

# Remove DIRECTORY_NAME from all commits, then remove the refs to the old commits
# (repeat these two commands for as many directories that you want to remove)
git filter-branch --index-filter 'git rm -rf --cached --ignore-unmatch DIRECTORY_NAME/' --prune-empty --tag-name-filter cat -- --all
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d

# Ensure all old refs are fully removed
rm -Rf .git/logs .git/refs/original

# Perform a garbage collection to remove commits with no refs
git gc --prune=all --aggressive

# Force push all branches to overwrite their history
# (use with caution!)
git push origin --all --force
git push origin --tags --force

Bạn có thể kiểm tra kích thước của kho lưu trữ trước và sau gcvới:

git count-objects -vH

3
bạn có thể giải thích tại sao điều này nhanh hơn nhiều?
knocte

7
@knocte: từ các tài liệu ( git-scm.com/docs/git-filter-branch ). "--index-filter: ... tương tự như bộ lọc cây nhưng không kiểm tra cây, điều này làm cho nó nhanh hơn nhiều"
Lee Netherton

23
Tại sao đây không phải là câu trả lời được chấp nhận? Thật là kỹ lưỡng.
Nhà vật lý điên

2
Nếu làm điều này trong Windows, bạn cần dấu ngoặc kép thay vì dấu ngoặc đơn.
Kris Morness

12
Chuyển --quietđến phần git rmtrên đã tăng tốc viết lại của tôi ít nhất là theo yếu tố 4.
ctusch

46

Ngoài câu trả lời phổ biến ở trên, tôi muốn thêm một vài ghi chú cho Windows -systems. Lệnh

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
  • hoạt động hoàn hảo mà không cần sửa đổi! Do đó, bạn không được sử dụng Remove-Item, delhoặc bất cứ điều gì khác thay vì rm -rf.

  • Nếu bạn cần chỉ định đường dẫn đến tệp hoặc thư mục, hãy sử dụng dấu gạch chéo như./path/to/node_modules


Điều này sẽ không hoạt động trên Windows nếu thư mục chứa a. (dấu chấm) trong tên.
Corneliu Serediuc

4
Và tôi đã tìm ra giải pháp. Sử dụng dấu phẩy kép đảo ngược cho lệnh rm như thế này: "rm -rf node.modules".
Corneliu Serediuc

23

Phương pháp tốt nhất và chính xác nhất mà tôi tìm thấy là tải xuống tệp bfg.jar: https://rtyley.github.io/bfg-repo-cleaner/

Sau đó chạy các lệnh:

git clone --bare https://project/repository project-repository
cd project-repository
java -jar bfg.jar --delete-folders DIRECTORY_NAME  # i.e. 'node_modules' in other examples
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push --mirror https://project/new-repository

Nếu bạn muốn xóa tệp thì hãy sử dụng tùy chọn xóa tệp thay thế:

java -jar bfg.jar --delete-files *.pyc

1
rất dễ dàng :) nếu bạn muốn làm cho shure chỉ xóa một thư mục cụ thể, điều này sẽ giúp: stackoverflow.com/questions/21142986/ trên
emjay ngày

9

Có vẻ như câu trả lời cập nhật cho vấn đề này là không sử dụng filter-branchtrực tiếp (ít nhất là bản thân git không khuyến nghị nữa) và trì hoãn hoạt động với một công cụ bên ngoài. Đặc biệt, git-filter-repo hiện đang được khuyến nghị. Tác giả của công cụ đó cung cấp các lập luận về lý do tại sao sử dụng filter-branchtrực tiếp có thể dẫn đến các vấn đề.

Hầu hết các tập lệnh nhiều dòng ở trên để xóa dirkhỏi lịch sử có thể được viết lại thành:

git filter-repo --path dir --invert-paths

Công cụ này mạnh hơn thế, rõ ràng. Bạn có thể áp dụng các bộ lọc theo tác giả, email, tên gọi và nhiều hơn nữa ( toàn bộ trang web ở đây ). Hơn nữa, nó là nhanh chóng . Cài đặt rất dễ dàng - nó được phân phối theo nhiều định dạng .


Công cụ tuyệt vời! Hoạt động tốt trên Ubuntu 20.04, bạn chỉ có thể sử dụng pip3 install git-filter-repostdlib và không cài đặt bất kỳ phụ thuộc nào. Trên Ubuntu 18, nó không tương thích với phiên bản git của distro Error: need a version of git whose diff-tree command has the --combined-all-paths option, nhưng thật dễ dàng để chạy nó trên mộtdocker run -ti ubuntu:20.04
kubanchot

7

Hoàn thành công thức sao chép và dán, chỉ cần thêm các lệnh trong các nhận xét (đối với giải pháp sao chép-dán), sau khi kiểm tra chúng:

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
echo node_modules/ >> .gitignore
git add .gitignore
git commit -m 'Removing node_modules from git history'
git gc
git push origin master --force

Sau này, bạn có thể xóa dòng "node_modules /" khỏi .gitignore


Tại sao sau đó bạn sẽ loại bỏ node_moduleskhỏi .gitignore? Vì vậy, họ có thể vô tình cam kết một lần nữa ??
Adamski

1
Nó không bị xóa khỏi gitignore, nó được thêm vào gitignore. Thông điệp cam kết ghi "lịch sử git", không phải "gitignore" :)
Danny Tuppeny

nhưng những nhận xét nói rằng bạn có thể loại bỏ node_modulestừ .gitignore.
zavr

7

Đối với người dùng Windows, xin lưu ý sử dụng "thay vì ' Cũng được thêm vào -fđể buộc lệnh nếu đã có bản sao lưu khác.

git filter-branch -f --tree-filter "rm -rf FOLDERNAME" --prune-empty HEAD
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
echo FOLDERNAME/ >> .gitignore
git add .gitignore
git commit -m "Removing FOLDERNAME from git history"
git gc
git push origin master --force

3

Tôi đã xóa các thư mục bin và obj khỏi các dự án C # cũ bằng git trên windows. Cẩn thận với

git filter-branch --tree-filter "rm -rf bin" --prune-empty HEAD

Nó phá hủy tính toàn vẹn của cài đặt git bằng cách xóa thư mục usr / bin trong thư mục cài đặt git.

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.