Pisahkan repositori Git besar menjadi banyak yang lebih kecil

86

Setelah berhasil mengubah repositori SVN ke Git, sekarang saya memiliki repositori Git yang sangat besar yang ingin saya bagi menjadi beberapa repositori yang lebih kecil dan mempertahankan riwayat.

Jadi, dapatkah seseorang membantu memecahkan repo yang mungkin terlihat seperti ini:

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

Ke dalam dua repositori yang terlihat seperti ini:

MyABRepo/
   .git
   DIR_A/
   DIR_B/

My12Repo/
   .git
   DIR_1/
   DIR_2/

Saya telah mencoba mengikuti petunjuk dalam pertanyaan sebelumnya ini tetapi tidak benar-benar cocok ketika mencoba menempatkan beberapa direktori ke dalam repo terpisah ( Subdirektori Lepaskan (pindah) ke dalam repositori Git terpisah ).

MikeM
sumber
11
Jika Anda senang dengan sebuah jawaban, tandai sebagai diterima.
Ben Fowler
1
Bagi siapa pun yang ingin membagi beberapa direktori (bersarang) menjadi repo baru (alih-alih ingin menghapus beberapa direktori, yang mungkin lebih sulit pada beberapa proyek), jawaban ini bermanfaat bagi saya: stackoverflow.com/a/19957874/164439
thaddeusmt

Jawaban:

80

Ini akan mengatur MyABRepo; Anda juga dapat melakukan My12Repo dengan cara yang sama.

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 

Referensi ke .git / refs / original / refs / heads / master tetap ada. Anda dapat menghapusnya dengan:

cd ..
git clone MyABRepo.tmp MyABRepo

Jika semuanya berjalan dengan baik, Anda dapat menghapus MyABRepo.tmp.


Jika karena alasan tertentu Anda mendapatkan error terkait .git-rewrite, Anda dapat mencoba ini:

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

Ini akan membuat dan menggunakan /tmp/git-rewrite.tmp sebagai direktori sementara, bukan .git-rewrite. Biasanya, Anda dapat mengganti jalur apa pun yang Anda inginkan /tmp/git-rewrite.tmp, selama Anda memiliki izin menulis, dan direktori tersebut belum ada.

unutbu
sumber
Halaman manual 'git filter-branch' merekomendasikan untuk membuat tiruan baru dari repositori yang ditulis ulang alih-alih langkah terakhir yang disebutkan di atas.
Jakub Narębski
Saya mencoba ini dan mendapat kesalahan ketika mencoba menghapus folder .git-rewrite di bagian akhir.
MikeM
-d <path-on-another-physical-disk> bekerja untuk saya dan menghilangkan kegagalan stange 'mv' dalam --tree-filter.
Vertigo
Apakah Anda memiliki ide untuk mendapatkan commit pertama, jika itu terkait dengan jalur yang dikecualikan (seperti DIR_A, misalnya)?
bitmask
1
Saya tidak menyadari konsekuensi penuh dari filter-branch. Bagi mereka yang tidak sadar, itu menulis ulang sejarah, jadi jika Anda berencana untuk mendorong repo setelah Anda melakukan ini, hash komit akan berbeda sekarang dan itu tidak akan berfungsi.
thaddeusmt
10

Anda dapat menggunakan git filter-branch --index-filterwith git rm --cacheduntuk menghapus direktori yang tidak diinginkan dari klon / salinan repositori asli Anda.

Sebagai contoh:

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

Anda perlu menghapus secara manual setiap cabang atau tag repositori yang tidak dibutuhkan (misalnya jika Anda memiliki cabang feature-x-for-AB , maka Anda mungkin ingin menghapusnya dari repositori “12”).

Chris Johnsen
sumber
1
:bukan karakter komentar di bash. Anda harus menggunakan #sebagai gantinya.
Daenyth
4
@Daenyth, :adalah perintah bawaan tradisional ( juga ditentukan dalam POSIX ). Ini termasuk dalam bash , tapi ini bukan komentar. Saya secara khusus menggunakannya sebagai preferensi #karena tidak semua shell mengambil #sebagai pengantar komentar dalam semua konteks (misalnya zsh interaktif tanpa opsi INTERACTIVE_COMMENTS diaktifkan). Menggunakan :membuat seluruh teks cocok untuk ditempelkan ke shell interaktif apa pun serta disimpan dalam file skrip.
Chris Johnsen
1
Cemerlang! Satu-satunya solusi yang saya temukan yang membuat semua cabang tetap utuh
pheelicks
Aneh, bagi saya itu berhenti dengan git remote rm origin, yang sepertinya selalu kembali 1. Oleh karena itu saya mengganti &&dengan ;untuk baris ini.
kynan
Bagus, $ @ bekerja untuk lebih dari dua dir saat dibutuhkan. Setelah selesai saya telepon git remote add origin $TARGET; git push origin master.
Walter A
7

Proyek git_split adalah skrip sederhana yang melakukan persis seperti yang Anda cari. https://github.com/vangorra/git_split

Ubah direktori git menjadi repositori mereka sendiri di lokasinya sendiri. Tidak ada bisnis lucu subtree. Skrip ini akan mengambil direktori yang ada di repositori git Anda dan mengubah direktori itu menjadi repositori independennya sendiri. Sepanjang jalan, itu akan menyalin seluruh riwayat perubahan untuk direktori yang Anda berikan.

./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.
vangorra.dll
sumber
1

Terima kasih atas jawaban Anda tetapi saya akhirnya hanya menyalin repositori dua kali kemudian menghapus file yang tidak saya inginkan dari masing-masing file. Saya akan menggunakan cabang filter di kemudian hari untuk menghapus semua komit untuk file yang dihapus karena versi tersebut sudah dikontrol di tempat lain.

cp -R MyHugeRepo MyABRepo
cp -R MyHugeRepo My12Repo

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

Ini berhasil untuk apa yang saya butuhkan.

EDIT: Tentu saja, hal yang sama dilakukan di My12Repo terhadap direktori A dan B. Ini memberi saya dua repo dengan sejarah yang identik sampai saya menghapus direktori yang tidak diinginkan.

MikeM
sumber
1
Ini tidak mempertahankan sejarah komit.
Daenyth
bagaimana? Saya masih memiliki semua sejarah, bahkan untuk file yang dihapus.
MikeM
1
Karena kebutuhan Anda bukanlah bahwa repo A harus berpura-pura bahwa repo B tidak pernah ada, saya pikir ini (meninggalkan catatan komit yang hanya memengaruhi B) adalah solusi yang tepat. Lebih baik menduplikasi sedikit sejarah daripada mengacaukannya.
Steve Clay