Pertanyaan Lepaskan (pindahkan) subdirektori ke dalam repositori Git yang terpisah


Saya punya Git repositori yang berisi sejumlah subdirektori. Sekarang saya telah menemukan bahwa salah satu subdirektori tidak berhubungan dengan yang lain dan harus dipisahkan ke repositori terpisah.

Bagaimana saya bisa melakukan ini sambil menyimpan sejarah file dalam subdirektori?

Saya kira saya bisa membuat klon dan menghapus bagian yang tidak diinginkan dari setiap klon, tapi saya kira ini akan memberi saya pohon lengkap ketika memeriksa revisi yang lebih tua dll. Ini mungkin dapat diterima, tetapi saya lebih suka untuk dapat berpura-pura bahwa dua repositori tidak memiliki riwayat bersama.

Hanya untuk membuatnya jelas, saya memiliki struktur berikut:

XYZ/
    .git/
    XY1/
    ABC/
    XY2/

Tapi saya ingin ini sebagai gantinya:

XYZ/
    .git/
    XY1/
    XY2/
ABC/
    .git/
    ABC/

1595
2017-12-11 13:57


asal


Jawaban:


Memperbarui: Proses ini sangat umum, bahwa tim git membuatnya lebih sederhana dengan alat baru, git subtree. Lihat disini: Lepaskan (pindahkan) subdirektori ke dalam repositori Git yang terpisah


Anda ingin mengkloning repositori Anda dan kemudian menggunakannya git filter-branch untuk menandai semuanya kecuali subdirektori yang Anda inginkan dalam repo baru Anda untuk dikumpulkan dari sampah.

  1. Untuk mengkloning repositori lokal Anda:

    git clone /XYZ /ABC
    

    (Catatan: repositori akan dikloning menggunakan hard-link, tapi itu bukan masalah karena file-file yang sulit terhubung tidak akan dimodifikasi sendiri - yang baru akan dibuat.)

  2. Sekarang, mari kita lestarikan cabang-cabang menarik yang ingin kita tulis ulang juga, dan kemudian hapus asalnya untuk menghindari dorongan di sana dan untuk memastikan bahwa komitmen lama tidak akan direferensikan oleh asalnya:

    cd /ABC
    for i in branch1 br2 br3; do git branch -t $i origin/$i; done
    git remote rm origin
    

    atau untuk semua cabang jarak jauh:

    cd /ABC
    for i in $(git branch -r | sed "s/.*origin\///"); do git branch -t $i origin/$i; done
    git remote rm origin
    
  3. Sekarang Anda mungkin ingin juga menghapus tag yang tidak ada hubungannya dengan subproyek; Anda juga dapat melakukannya nanti, tetapi Anda mungkin perlu memangkas repo Anda lagi. Saya tidak melakukannya dan mendapat WARNING: Ref 'refs/tags/v0.1' is unchanged untuk semua tag (karena semuanya tidak berhubungan dengan sub proyek); Selain itu, setelah menghapus tag tersebut, lebih banyak ruang akan direklamasi. Tampaknya git filter-branch harus dapat menulis ulang tag lain, tetapi saya tidak dapat memverifikasi ini. Jika Anda ingin menghapus semua tag, gunakan git tag -l | xargs git tag -d.

  4. Kemudian gunakan cabang-filter dan reset untuk mengecualikan file-file lain, sehingga mereka dapat dipangkas. Mari juga tambahkan --tag-name-filter cat --prune-empty untuk menghapus komit kosong dan menulis ulang tag (perhatikan bahwa ini harus menghapus tanda tangannya):

    git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC -- --all
    

    atau alternatifnya, untuk hanya menulis ulang cabang HEAD dan mengabaikan tag dan cabang lain:

    git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC HEAD
    
  5. Kemudian hapus pencadangan cadangan sehingga ruang dapat benar-benar direklamasi (walaupun sekarang operasinya merusak)

    git reset --hard
    git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
    git reflog expire --expire=now --all
    git gc --aggressive --prune=now
    

    dan sekarang Anda memiliki gudang git lokal dari sub direktori ABC dengan semua sejarahnya terlestarikan.

Catatan: Untuk sebagian besar penggunaan, git filter-branch memang harus memiliki parameter tambahan -- --all. Ya itu benar --ruang--  all. Ini perlu menjadi parameter terakhir untuk perintah. Seperti yang ditemukan Matli, ini membuat cabang dan tag proyek dimasukkan ke dalam repo baru.

Edit: berbagai saran dari komentar di bawah digabungkan untuk memastikan, misalnya, bahwa repositori sebenarnya menyusut (yang tidak selalu terjadi sebelumnya).


1155
2017-07-25 17:10



The Easy Way ™

Ternyata ini adalah praktik yang umum dan berguna sehingga tuan git membuatnya sangat mudah, tetapi Anda harus memiliki versi git yang lebih baru (> = 1.7.11 Mei 2012). Lihat lampiran untuk cara menginstal git terbaru. Juga, ada a contoh dunia nyata dalam walkthrough di bawah.

  1. Persiapkan repo lama

    pushd <big-repo>
    git subtree split -P <name-of-folder> -b <name-of-new-branch>
    popd
    

    catatan:  <name-of-folder> TIDAK boleh berisi karakter utama atau tertinggal. Misalnya, folder bernama subproject HARUS lulus sebagai subprojectTIDAK ./subproject/

    Catatan untuk pengguna windows: ketika kedalaman folder Anda adalah> 1, <name-of-folder> harus memiliki pemisah folder gaya * nix (/). Misalnya, folder bernama path1\path2\subproject HARUS lulus sebagai path1/path2/subproject

  2. Buat repo baru

    mkdir <new-repo>
    pushd <new-repo>
    
    git init
    git pull </path/to/big-repo> <name-of-new-branch>
    
  3. Tautkan repo baru ke Github atau ke mana pun

    git remote add origin <git@github.com:my-user/new-repo.git>
    git push origin -u master
    
  4. Membersihkan, Jika diinginkan

    popd # get out of <new-repo>
    pushd <big-repo>
    
    git rm -rf <name-of-folder>
    

    Catatan: Ini meninggalkan semua referensi historis di repositori. Lihat Lampiran di bawah jika Anda benar-benar khawatir tentang memiliki sandi atau Anda perlu mengurangi ukuran file Anda .git map.

...

Walkthrough

Ini adalah langkah yang sama seperti di atas, tetapi mengikuti langkah tepat saya untuk repositori saya daripada menggunakan <meta-named-things>.

Berikut ini proyek yang saya miliki untuk mengimplementasikan modul browser JavaScript di node:

tree ~/Code/node-browser-compat

node-browser-compat
├── ArrayBuffer
├── Audio
├── Blob
├── FormData
├── atob
├── btoa
├── location
└── navigator

Saya ingin membagi satu folder, btoa, ke dalam repositori git yang terpisah

pushd ~/Code/node-browser-compat/
git subtree split -P btoa -b btoa-only
popd

Saya sekarang punya cabang baru, btoa-only, yang hanya berkomitmen untuk btoa dan saya ingin membuat repositori baru.

mkdir ~/Code/btoa/
pushd ~/Code/btoa/
git init
git pull ~/Code/node-browser-compat btoa-only

Selanjutnya saya membuat repo baru di Github atau bitbucket, atau apa pun dan menambahkannya origin (btw, "origin" hanyalah sebuah konvensi, bukan bagian dari perintah - Anda dapat menyebutnya "remote-server" atau apapun yang Anda suka)

git remote add origin git@github.com:node-browser-compat/btoa.git
git push origin -u master

Hari bahagia!

catatan: Jika Anda membuat repo dengan README.md, .gitignore dan LICENSE, Anda harus menarik lebih dulu:

git pull origin -u master
git push origin -u master

Terakhir, saya ingin menghapus folder dari repo yang lebih besar

git rm -rf btoa

...

Lampiran

Git terbaru di OS X

Untuk mendapatkan git versi terbaru:

brew install git

Untuk mendapatkan minuman untuk OS X:

http://brew.sh

Git terbaru di Ubuntu

sudo apt-get update
sudo apt-get install git
git --version

Jika itu tidak berhasil (Anda memiliki versi yang sangat lama dari ubuntu), cobalah

sudo add-apt-repository ppa:git-core/ppa
sudo apt-get update
sudo apt-get install git

Jika itu masih tidak berhasil, cobalah

sudo chmod +x /usr/share/doc/git/contrib/subtree/git-subtree.sh
sudo ln -s \
/usr/share/doc/git/contrib/subtree/git-subtree.sh \
/usr/lib/git-core/git-subtree

Terima kasih untuk rui.araujo dari komentar.

membersihkan riwayat Anda

Secara default menghapus file dari git tidak benar-benar menghapusnya dari git, itu hanya melakukan bahwa mereka tidak ada lagi. Jika Anda ingin benar-benar menghapus referensi historis (yaitu Anda memiliki kata sandi yang berkomitmen), Anda perlu melakukan ini:

git filter-branch --prune-empty --tree-filter 'rm -rf <name-of-folder>' HEAD

Setelah itu Anda dapat memeriksa apakah file atau folder Anda tidak lagi muncul dalam sejarah git sama sekali

git log -- <name-of-folder> # should show nothing

Namun, kamu tidak dapat "mendorong" menghapus ke github dan sejenisnya. Jika Anda mencoba, Anda akan mendapatkan kesalahan dan Anda harus melakukannya git pull sebelum kamu bisa git push - dan kemudian Anda kembali memiliki segalanya dalam sejarah Anda.

Jadi jika Anda ingin menghapus riwayat dari "asal" - artinya menghapusnya dari github, bitbucket, dll - Anda harus menghapus repo dan mendorong ulang salinan repo yang sudah dipangkas. Tapi tunggu - masih ada lagi! - Jika Anda benar-benar khawatir tentang menyingkirkan kata sandi atau sesuatu seperti itu Anda harus memangkas cadangan (lihat di bawah).

pembuatan .git lebih kecil

Perintah histori penghapusan yang disebutkan di atas masih menyisakan banyak file cadangan - karena git terlalu baik dalam membantu Anda untuk tidak merusak repo Anda secara tidak sengaja. Ini akhirnya akan menghapus file yatim selama beberapa hari dan bulan, tetapi itu meninggalkan mereka di sana untuk sementara waktu jika Anda menyadari bahwa Anda tidak sengaja menghapus sesuatu yang tidak Anda inginkan.

Jadi jika Anda benar-benar menginginkannya kosongkansampahnya untuk kurangi ukuran clone dari repo segera Anda harus melakukan semua hal yang benar-benar aneh ini:

rm -rf .git/refs/original/ && \
git reflog expire --all && \
git gc --aggressive --prune=now

git reflog expire --all --expire-unreachable=0
git repack -A -d
git prune

Yang mengatakan, saya akan merekomendasikan tidak melakukan langkah-langkah ini kecuali Anda tahu bahwa Anda perlu - kalau-kalau Anda memangkas subdirektori yang salah, ya? File cadangan tidak boleh dikloning saat Anda menekan repo, mereka hanya akan ada di salinan lokal Anda.

Kredit


1122
2018-06-05 13:15



Jawaban Paulus membuat repositori baru yang berisi / ABC, tetapi tidak menghapus / ABC dari dalam / XYZ. Perintah berikut akan menghapus / ABC dari dalam / XYZ:

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

Tentu saja, uji dalam repositori 'clone --no-hardlinks' terlebih dahulu, dan ikuti dengan reset, gc dan prune commands yang ada di daftar Paul.


131
2017-10-19 21:10



Saya telah menemukan bahwa untuk menghapus sejarah lama dengan benar dari repositori baru, Anda harus melakukan sedikit lebih banyak pekerjaan setelah filter-branch langkah.

  1. Lakukan kloning dan filter:

    git clone --no-hardlinks foo bar; cd bar
    git filter-branch --subdirectory-filter subdir/you/want
    
  2. Hapus semua referensi ke riwayat lama. "Asal" melacak kloning Anda, dan "asli" adalah tempat filter-cabang menyimpan hal-hal lama:

    git remote rm origin
    git update-ref -d refs/original/refs/heads/master
    git reflog expire --expire=now --all
    
  3. Bahkan sekarang, sejarah Anda mungkin terjebak dalam file paket yang tidak akan disentuh oleh fsck. Merobeknya sampai hancur, membuat file paket baru dan menghapus objek yang tidak digunakan:

    git repack -ad
    

Ada penjelasan tentang ini dalam manual untuk cabang-filter.


94
2018-06-09 15:41



Edit: Skrip Bash ditambahkan.

Jawaban yang diberikan di sini bekerja sebagian untuk saya; Banyak file besar tetap tersimpan di cache. Apa yang akhirnya berhasil (setelah jam di #git on freenode):

git clone --no-hardlinks file:///SOURCE /tmp/blubb
cd blubb
git filter-branch --subdirectory-filter ./PATH_TO_EXTRACT  --prune-empty --tag-name-filter cat -- --all
git clone file:///tmp/blubb/ /tmp/blooh
cd /tmp/blooh
git reflog expire --expire=now --all
git repack -ad
git gc --prune=now

Dengan solusi sebelumnya, ukuran repositori sekitar 100 MB. Yang ini membuatnya menjadi 1,7 MB. Mungkin itu membantu seseorang :)


Skrip bash berikut mengotomatiskan tugas:

!/bin/bash

if (( $# < 3 ))
then
    echo "Usage:   $0 </path/to/repo/> <directory/to/extract/> <newName>"
    echo
    echo "Example: $0 /Projects/42.git first/answer/ firstAnswer"
    exit 1
fi


clone=/tmp/${3}Clone
newN=/tmp/${3}

git clone --no-hardlinks file://$1 ${clone}
cd ${clone}

git filter-branch --subdirectory-filter $2  --prune-empty --tag-name-filter cat -- --all

git clone file://${clone} ${newN}
cd ${newN}

git reflog expire --expire=now --all
git repack -ad
git gc --prune=now

38
2017-08-20 14:11



Ini tidak lagi begitu rumit, Anda hanya bisa menggunakan git filter-cabang perintah pada klon Anda repo untuk menyisihkan subdirektori yang tidak Anda inginkan dan kemudian dorong ke remote baru.

git filter-branch --prune-empty --subdirectory-filter <YOUR_SUBDIR_TO_KEEP> master
git push <MY_NEW_REMOTE_URL> -f .

21
2018-03-22 20:55



Memperbarui: Modul git-subtree sangat berguna sehingga tim git menariknya ke inti dan membuatnya git subtree. Lihat disini: Lepaskan (pindahkan) subdirektori ke dalam repositori Git yang terpisah

git-subtree mungkin berguna untuk ini

http://github.com/apenwarr/git-subtree/blob/master/git-subtree.txt (tidak berlaku lagi)

http://psionides.jogger.pl/2010/02/04/sharing-code-between-projects-with-git-subtree/


19
2017-08-06 15:26