Git alt dizini nasıl çıkarılır ve bundan bir alt modül nasıl oluşturulur?


120

Birkaç ay önce bir projeye başladım ve her şeyi bir ana dizinde sakladım. Ana dizinim olan "Project" içinde farklı şeyler içeren birkaç alt dizin var: Project / paper, LaTeX Project / sourcecode / RailsApp'ta yazılmış bir belge içerir, benim raylar uygulamamı içerir.

"Proje" GITified ve hem "kağıt" hem de "RailsApp" dizininde çok sayıda taahhüt var. Şimdi, "RailsApp" için cruisecontrol.rb'yi kullanmak istediğimden, geçmişi kaybetmeden "RailsApp" den bir alt modül yapmanın bir yolu olup olmadığını merak ediyorum.



Yanıtlar:


123

Günümüzde bunu yapmanın git filter-branch'ı manuel olarak kullanmaktan çok daha kolay bir yolu var: git subtree

Kurulum

NOT git-subtree artık 1.7.11'den itibaren git(katkı yüklerseniz) ' nin bir parçasıdır , bu nedenle zaten yüklemiş olabilirsiniz. Çalıştırarak kontrol edebilirsiniz git subtree.


Git-subtree'yi kaynaktan yüklemek için (git'in eski sürümleri için):

git clone https://github.com/apenwarr/git-subtree.git

cd git-subtree
sudo rsync -a ./git-subtree.sh /usr/local/bin/git-subtree

Veya man sayfalarını falan istiyorsanız

make doc
make install

kullanım

Daha büyük bir parçayı daha küçük parçalara ayırın:

# Go into the project root
cd ~/my-project

# Create a branch which only contains commits for the children of 'foo'
git subtree split --prefix=foo --branch=foo-only

# Remove 'foo' from the project
git rm -rf ./foo

# Create a git repo for 'foo' (assuming we already created it on github)
mkdir foo
pushd foo
git init
git remote add origin git@github.com:my-user/new-project.git
git pull ../ foo-only
git push origin -u master
popd

# Add 'foo' as a git submodule to `my-project`
git submodule add git@github.com:my-user/new-project.git foo

Ayrıntılı belgeler için (man sayfası) lütfen okuyun git-subtree.txt.


10
git subtree kayalar!
Simon Woodside

3
Ancak git-subtree'nin amacı alt modülleri kullanmaktan kaçınmak değil mi? Demek istediğim, gerçekten git-subtree'nin yazarısınız (bir takma ad çakışması olmadığı sürece), ancak gösterdiğiniz komut hala geçerli görünse de git-subtree değişmiş gibi görünüyor. Bunu doğru anlıyor muyum?
Blaisorblade 01

18
git-subtree, 1.7.11'den itibaren git'in bir parçası (katkıda bulunursanız)
Jeremy

8
Peki , tam geçmişinden git rm -rf ./fookaldırılır foo, HEADancak filtrelemez my-project. Ardından, git submodule add git@github.com:my-user/new-project.git fooyalnızca foobaşlayarak bir alt modül oluşturur HEAD. Bu açıdan, komut dosyası oluşturma filter-branch, "
altdizin

bunun için teşekkürler
git subtree

38

Ödeme git filtre dalı .

ExamplesBölüm 's tarihinin tüm tutmak ve (aradığınız sadece ne) diğer dosyaları / dizinleri tarihini bir kenara atar, bu kendi projeye bir alt dizini ayıklamak için nasıl adam sayfası gösterileri.

Depoyu foodir/proje kökümüş gibi yeniden yazmak ve diğer tüm geçmişi atmak için:

   git filter-branch --subdirectory-filter foodir -- --all

Böylece, örneğin, bir kütüphane alt dizinini kendi başına bir havuza dönüştürebilirsiniz.
Not --olduğunu ayırır filter-branchrevizyon seçenekler arasından seçeneklerini ve --alltüm şube ve etiketleri yeniden yazmak.


1
Bu benim için iyi çalıştı. Tek dezavantajı, sonucun tüm taahhütleri içeren tek bir ana dal olmasıydı.
aseofspades

@aceofspades: Bu neden bir dezavantaj?
naught101

2
Benim için bir git deposundan taahhütleri çıkarmanın tüm amacı, geçmişi korumak istememdir.
aceofspades

13

Bunu yapmanın bir yolu tersidir - saklamak istediğiniz dosya dışındaki her şeyi kaldırın.

Temel olarak, deponun bir kopyasını oluşturun, ardından git filter-branchsaklamak istediğiniz dosya / klasörler dışındaki her şeyi kaldırmak için kullanın .

Örneğin, dosyayı tvnamer.pyyeni bir depoya çıkarmak istediğim bir projem var :

git filter-branch --tree-filter 'for f in *; do if [ $f != "tvnamer.py" ]; then rm -rf $f; fi; done' HEAD

Bu, git filter-branch --tree-filterher kaydetme işleminden geçmek, komutu çalıştırmak ve ortaya çıkan dizin içeriğini yeniden teslim etmek için kullanılır. Bu son derece yıkıcıdır (bu nedenle bunu yalnızca deponuzun bir kopyasında yapmalısınız!) Ve biraz zaman alabilir (300 kaydetme ve yaklaşık 20 dosya içeren bir havuzda yaklaşık 1 dakika)

Yukarıdaki komut, elbette değiştirmeniz gereken her revizyonda aşağıdaki kabuk komut dosyasını çalıştırır (bunun yerine alt dizininizi hariç tutmak için tvnamer.py):

for f in *; do
    if [ $f != "tvnamer.py" ]; then
        rm -rf $f;
    fi;
done

En bariz sorun, kalan dosyayla ilgisiz olsalar bile tüm commit mesajlarını bırakmasıdır. Git-remove-empty-commits betiği bunu düzeltir ..

git filter-branch --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "$@"; else git commit-tree "$@"; fi'

Herhangi bir şeyle tekrar -fçalıştırılan force argümanını kullanmanız gerekir (temelde bir yedek)filter-branchrefs/original/

Elbette bu asla mükemmel olmayacak, örneğin commit mesajlarınız başka dosyalardan bahsediyorsa, ancak git akımının izin verdiği kadar yakın (her halükarda bildiğim kadarıyla).

Yine, bunu yalnızca deponuzun bir kopyasında çalıştırın! - ama özet olarak, "thisismyfilename.txt" dışındaki tüm dosyaları kaldırmak için:

git filter-branch --tree-filter 'for f in *; do if [ $f != "thisismyfilename.txt" ]; then rm -rf $f; fi; done' HEAD
git filter-branch -f --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "$@"; else git commit-tree "$@"; fi'

4
git filter-branch(bugünlerde?) boş kaydetmeleri kaldırmak için yerleşik bir seçeneğe sahiptir, yani --prune-empty. İçin daha iyi bir rehber git filter-branchBu sorunun yanıtları içinde: stackoverflow.com/questions/359424/...
Blaisorblade

4

Hem CoolAJ86 hem de apenwarr cevapları çok benzer. İkisinde de eksik olan bitleri anlamaya çalışırken ikisi arasında gidip geldim. Aşağıda bunların bir kombinasyonu var.

Öncelikle Git Bash'i bölünecek git deposunun köküne gidin. Buradaki örneğimde bu~/Documents/OriginalRepo (master)

# move the folder at prefix to a new branch
git subtree split --prefix=SubFolderName/FolderToBeNewRepo --branch=to-be-new-repo

# create a new repository out of the newly made branch
mkdir ~/Documents/NewRepo
pushd ~/Documents/NewRepo
git init
git pull ~/Documents/OriginalRepo to-be-new-repo

# upload the new repository to a place that should be referenced for submodules
git remote add origin git@github.com:myUsername/newRepo.git
git push -u origin master
popd

# replace the folder with a submodule
git rm -rf ./SubFolderName/FolderToBeNewRepo
git submodule add git@github.com:myUsername/newRepo.git SubFolderName/FolderToBeNewRepo
git branch --delete --force to-be-new-repo

Aşağıda, özelleştirilebilir adlar değiştirilmiş ve bunun yerine https kullanılarak yukarıdakinin bir kopyası bulunmaktadır. Kök klasör şimdi~/Documents/_Shawn/UnityProjects/SoProject (master)

# move the folder at prefix to a new branch
git subtree split --prefix=Assets/SoArchitecture --branch=so-package

# create a new repository out of the newly made branch
mkdir ~/Documents/_Shawn/UnityProjects/SoArchitecture
pushd ~/Documents/_Shawn/UnityProjects/SoArchitecture
git init
git pull ~/Documents/_Shawn/UnityProjects/SoProject so-package

# upload the new repository to a place that should be referenced for submodules
git remote add origin https://github.com/Feddas/SoArchitecture.git
git push -u origin master
popd

# replace the folder with a submodule
git rm -rf ./Assets/SoArchitecture
git submodule add https://github.com/Feddas/SoArchitecture.git
git branch --delete --force so-package

3

Bazı dosya alt kümelerini yeni bir depoya aktarmak, ancak geçmişi saklamak istiyorsanız, temelde tamamen yeni bir geçmişe sahip olacaksınız. Bunun çalışma şekli temelde aşağıdaki gibidir:

  1. Yeni depo oluşturun.
  2. Eski deponuzun her revizyonu için, modülünüzdeki değişiklikleri yeni depoda birleştirin. Bu, mevcut proje geçmişinizin bir "kopyasını" oluşturacaktır.

Küçük ama tüylü bir senaryo yazmanın sakıncası yoksa, bunu otomatikleştirmek biraz kolay olmalı. Anlaşılır, evet, ama aynı zamanda acı verici. Geçmişte insanlar Git'te geçmişi yeniden yazdılar, bunun için bir arama yapabilirsiniz.

Alternatif olarak: depoyu klonlayın ve klondaki kağıdı silin, orijinaldeki uygulamayı silin. Bu bir dakika sürer, çalışması garantilidir ve git geçmişinizi temizlemeye çalışmaktan daha önemli şeylere geri dönebilirsiniz. Gereksiz geçmiş kopyalarının kapladığı sabit disk alanı konusunda endişelenmeyin.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.