Chia kho lưu trữ Git lớn thành nhiều kho nhỏ hơn


86

Sau khi chuyển đổi thành công kho lưu trữ SVN sang Git, bây giờ tôi có một kho lưu trữ Git rất lớn mà tôi muốn chia nhỏ thành nhiều kho lưu trữ nhỏ hơn và duy trì lịch sử.

Vì vậy, ai đó có thể giúp phá vỡ repo có thể trông như thế này:

MyHugeRepo/
   .git/
   DIR_A/
   DIR_B/
   DIR_1/
   DIR_2/

Thành hai kho giống như sau:

MyABRepo/
   .git
   DIR_A/
   DIR_B/

My12Repo/
   .git
   DIR_1/
   DIR_2/

Tôi đã thử làm theo các hướng dẫn trong câu hỏi trước này nhưng nó không thực sự phù hợp khi cố gắng đặt nhiều thư mục vào một kho riêng biệt ( Tách (di chuyển) thư mục con vào kho lưu trữ Git riêng biệt ).


11
Khi bạn hài lòng với một câu trả lời, hãy đánh dấu nó là được chấp nhận.
Ben Fowler

1
Đối với bất kỳ ai muốn tách nhiều thư mục (lồng nhau) thành một kho mới (thay vì tìm cách xóa nhiều thư mục, điều này có thể khó hơn trong một số dự án), câu trả lời này hữu ích cho tôi: stackoverflow.com/a/19957874/164439
thaddeusmt

Câu trả lời:


80

Điều này sẽ thiết lập MyABRepo; tất nhiên bạn có thể thực hiện My12Repo tương tự.

git clone MyHugeRepo/ MyABRepo.tmp/
cd MyABRepo.tmp
git filter-branch --prune-empty --index-filter 'git rm --cached --ignore-unmatch DIR_1/* DIR_2/*' HEAD 

Tham chiếu đến .git / refs / original / refs / heads / master vẫn còn. Bạn có thể xóa nó bằng:

cd ..
git clone MyABRepo.tmp MyABRepo

Nếu mọi việc suôn sẻ, bạn có thể xóa MyABRepo.tmp.


Nếu vì lý do nào đó mà bạn gặp lỗi liên quan đến .git-rewrite, bạn có thể thử cách này:

git clone MyHugeRepo/ MyABRepo.tmp/
cd MyABRepo.tmp
git filter-branch -d /tmp/git-rewrite.tmp --prune-empty --index-filter 'git rm --cached --ignore-unmatch DIR_1/* DIR_2/*' HEAD 
cd ..
git clone MyABRepo.tmp MyABRepo

Điều này sẽ tạo và sử dụng /tmp/git-rewrite.tmp làm thư mục tạm thời, thay vì .git-rewrite. Đương nhiên, bạn có thể thay thế bất kỳ đường dẫn nào mà bạn muốn /tmp/git-rewrite.tmp, miễn là bạn có quyền ghi và thư mục chưa tồn tại.


manpage 'git filter-branch' khuyên bạn nên tạo một bản sao mới của kho lưu trữ được viết lại thay vì bước cuối cùng được đề cập ở trên.
Jakub Narębski

Tôi đã thử điều này và gặp lỗi khi nó đang cố gắng xóa thư mục .git-rewrite ở cuối.
MikeM

-d <path-on-aosystem-physical-disk> đã làm việc cho tôi và loại bỏ lỗi 'mv' stange trong --tree-filter.
Vertigo

Bạn có ý tưởng về cách thực hiện lần cam kết đầu tiên, nếu nó liên quan đến một đường dẫn bị loại trừ ( DIR_Achẳng hạn như)?
bitmask

1
Tôi không nhận ra sự phân chia đầy đủ của filter-branch. Đối với những người không biết, nó sẽ ghi lại lịch sử, vì vậy nếu bạn định đẩy repo sau khi bạn đã thực hiện xong việc này, các băm cam kết sẽ khác ngay bây giờ và nó sẽ không hoạt động.
thaddeusmt

10

Bạn có thể sử dụng git filter-branch --index-filtervới git rm --cachedđể xóa các thư mục không mong muốn khỏi các bản sao / bản sao của kho lưu trữ gốc của bạn.

Ví dụ:

trim_repo() { : trim_repo src dst dir-to-trim-out...
  : uses printf %q: needs bash, zsh, or maybe ksh
  git clone "$1" "$2" &&
  (
    cd "$2" &&
    shift 2 &&

    : mirror original branches &&
    git checkout HEAD~0 2>/dev/null &&
    d=$(printf ' %q' "$@") &&
    git for-each-ref --shell --format='
      o=%(refname:short) b=${o#origin/} &&
      if test -n "$b" && test "$b" != HEAD; then 
        git branch --force --no-track "$b" "$o"
      fi
    ' refs/remotes/origin/ | sh -e &&
    git checkout - &&
    git remote rm origin &&

    : do the filtering &&
    git filter-branch \
      --index-filter 'git rm --ignore-unmatch --cached -r -- '"$d" \
      --tag-name-filter cat \
      --prune-empty \
      -- --all
  )
}
trim_repo MyHugeRepo MyABRepo DIR_1 DIR_2
trim_repo MyHugeRepo My12Repo DIR_A DIR_B

Bạn sẽ cần xóa theo cách thủ công các nhánh hoặc thẻ không cần thiết của từng kho lưu trữ (ví dụ: nếu bạn có chi nhánh feature-x-for-AB , thì bạn có thể muốn xóa nó khỏi kho lưu trữ “12”).


1
:không phải là một ký tự bình luận trong bash. Bạn nên sử dụng #thay thế.
Daenyth

4
@Daenyth, :là một lệnh tích hợp sẵn truyền thống ( cũng được chỉ định trong POSIX ). Nó được bao gồm trong bash , nhưng nó không phải là một bình luận. Tôi đặc biệt sử dụng nó để ưu tiên #vì không phải tất cả các trình bao đều coi #là người giới thiệu nhận xét trong mọi ngữ cảnh (ví dụ: zsh tương tác mà không bật tùy chọn INTERACTIVE_COMMENTS). Việc sử dụng :làm cho toàn bộ văn bản phù hợp để dán vào bất kỳ trình bao tương tác nào cũng như lưu trong tệp script.
Chris Johnsen

1
Xuất sắc! Giải pháp duy nhất tôi thấy rằng giữ tất cả các ngành còn nguyên vẹn
pheelicks

Kỳ lạ, đối với tôi, nó dừng lại bằng git remote rm origin, dường như luôn trả về 1. Do đó tôi đã thay thế &&bằng ;cho dòng này.
kynan

Tuyệt vời, $ @ hoạt động trong hơn hai dirs khi cần thiết. Khi hoàn thành tôi gọi git remote add origin $TARGET; git push origin master.
Walter A

6

Dự án git_split là một tập lệnh đơn giản thực hiện chính xác những gì bạn đang tìm kiếm. https://github.com/vangorra/git_split

Biến các thư mục git thành các kho lưu trữ của riêng chúng ở vị trí của riêng chúng. Không có kinh doanh cây con vui nhộn. Tập lệnh này sẽ lấy một thư mục hiện có trong kho lưu trữ git của bạn và biến thư mục đó thành một kho lưu trữ độc lập của riêng nó. Trên đường đi, nó sẽ sao chép toàn bộ lịch sử thay đổi cho thư mục bạn đã cung cấp.

./git_split.sh <src_repo> <src_branch> <relative_dir_path> <dest_repo>
        src_repo  - The source repo to pull from.
        src_branch - The branch of the source repo to pull from. (usually master)
        relative_dir_path   - Relative path of the directory in the source repo to split.
        dest_repo - The repo to push to.


1

Cảm ơn câu trả lời của bạn nhưng cuối cùng tôi chỉ sao chép kho lưu trữ hai lần sau đó xóa các tệp tôi không muốn khỏi mỗi tệp. Tôi sẽ sử dụng nhánh bộ lọc vào một ngày sau đó để loại bỏ tất cả các cam kết cho các tệp đã xóa vì chúng đã được kiểm soát phiên bản ở nơi khác.

cp -R MyHugeRepo MyABRepo
cp -R MyHugeRepo My12Repo

cd MyABRepo/
rm -Rf DIR_1/ DIR_2/
git add -A
git commit -a

Điều này đã làm việc cho những gì tôi cần.

CHỈNH SỬA: Tất nhiên, điều tương tự cũng được thực hiện trong My12Repo đối với thư mục A và B. Điều này đã mang lại cho tôi hai kho lưu trữ có lịch sử giống hệt nhau cho đến thời điểm tôi đã xóa các thư mục không mong muốn.


1
Điều này không bảo tồn lịch sử cam kết.
Daenyth

như thế nào? Tôi vẫn có tất cả lịch sử, ngay cả đối với các tệp đã xóa.
MikeM

1
Vì yêu cầu của bạn không phải là repo A phải giả vờ như repo B chưa bao giờ tồn tại, tôi nghĩ điều này (để lại hồ sơ cam kết chỉ ảnh hưởng đến B) là một giải pháp thích hợp. Tốt hơn là sao chép một chút lịch sử hơn là mang nó đi.
Steve Clay
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.