Periksa subdirektori di Git?

160

Apakah mungkin untuk memeriksa subdirektori dari repositori di Git?

Bayangkan saya sedang menyiapkan instalasi WordPress baru. Saya akan membuat dua direktori baru untuk plugin dan penyesuaian tema:

  • wordpress/wp-content/plugins/myplugins/
  • wordpress/wp-content/themes/mytheme/

Saya ingin mempertahankan direktori ini melalui Git. Dalam Subversion, saya akan menyelesaikan ini dengan memiliki trunk/myplugins/dan trunk/mytheme/direktori dan memeriksa subdirektori. Apakah Git memiliki cara untuk menyelesaikan tugas yang sama menggunakan repositori tunggal?

Saya bisa saja ketinggalan perahu pada beberapa paradigma Git, sebagai pengguna SVN lama dengan sedikit paparan Git.

Sunting: Beberapa cabang menyimpan konten yang berbeda adalah cara yang menarik untuk menangani ini.

Annika Backstrom
sumber
2
mengapa Anda tidak checkout seluruh repo, dan membuat tautan simbolis ke subdirektori yang ingin Anda kerjakan?
randomness2077
Jawaban sederhana di sini .
Peter Krauss
Apakah mungkin untuk melakukan checkout jarang dan referensi repositori Git?
luka5z

Jawaban:

121

Checkout jarang yang sekarang di Git 1.7 .

Lihat juga pertanyaan “ Apakah mungkin melakukan checkout jarang tanpa memeriksa seluruh repositori terlebih dahulu?

Perhatikan bahwa checkout jarang masih mengharuskan Anda untuk mengunduh seluruh repositori, meskipun beberapa file yang diunduh Git tidak akan berakhir di pohon kerja Anda.

Collin Anderson
sumber
1
Di mana de git cloneperintah sederhana ?? Yah, saya menggunakan jawaban ini , berfungsi!
Peter Krauss
4
Dan apakah ada cara untuk mengganti nama folder itu? Jika saya jarang checkout /foo/bar/foobar, apakah mungkin melihatnya hanya /foobardi repositori lokal saya?
graywolf
17

Tidak ada cara nyata untuk melakukannya di git. Dan jika Anda tidak akan membuat perubahan yang memengaruhi kedua pohon sekaligus sebagai unit kerja tunggal, tidak ada alasan yang baik untuk menggunakan repositori tunggal untuk keduanya. Saya pikir saya akan kehilangan fitur Subversion ini, tetapi saya menemukan bahwa membuat repositori hanya memiliki sedikit overhead mental administratif (hanya karena fakta bahwa repositori disimpan tepat di sebelah copy pekerjaan mereka, daripada mengharuskan saya untuk secara eksplisit memilih tempat di luar dari copy pekerjaan) yang saya terbiasa hanya membuat banyak repositori kecil tujuan tunggal.

Jika Anda bersikeras (atau benar-benar membutuhkannya), meskipun, Anda bisa membuat repositori git hanya dengan mythemedan mypluginsdirektori dan symlink orang dari dalam WordPress install.


MDCore menulis:

membuat komitmen untuk, misalnya, mitos akan menambah nomor revisi untuk myplugin

Perhatikan bahwa ini bukan masalah bagi git, jika Anda memutuskan untuk meletakkan kedua direktori dalam satu repositori, karena git menghilangkan sepenuhnya dengan konsep peningkatan angka revisi dalam bentuk apa pun secara monoton.

Satu-satunya kriteria untuk hal-hal apa yang harus disatukan dalam repositori tunggal di git adalah apakah itu merupakan satu unit, yaitu. dalam kasus Anda apakah ada perubahan di mana tidak masuk akal untuk melihat pengeditan di setiap direktori secara terpisah. Jika Anda memiliki perubahan di mana Anda perlu mengedit file di kedua direktori sekaligus dan edit milik bersama, itu harus menjadi satu repositori. Jika tidak, maka jangan gosok mereka menjadi satu.

Git benar-benar ingin Anda menggunakan repositori terpisah untuk entitas yang terpisah.

submodules

Submodules tidak membahas keinginan untuk menyimpan kedua direktori dalam satu repositori, karena mereka sebenarnya akan menegakkan memiliki repositori terpisah untuk setiap direktori, yang kemudian disatukan dalam repositori lain menggunakan submodules. Lebih buruk lagi, karena direktori di dalam instalasi WordPress bukan subdirektori langsung dari direktori yang sama dan juga merupakan bagian dari hierarki dengan banyak file lain, menggunakan repositori per-direktori sebagai submodul dalam repositori terpadu tidak akan memberikan keuntungan apa pun, karena unified repositori tidak akan mencerminkan use case / need.

Aristoteles Pagaltzis
sumber
Di mana git cloneurutan perintah sederhana ?? Yah, saya menggunakan jawaban ini , berfungsi!
Peter Krauss
16

Satu hal yang saya tidak suka tentang sparse checkout, adalah bahwa jika Anda ingin checkout subdirektori dengan kedalaman beberapa direktori, struktur direktori Anda harus berisi semua direktori yang mengarah ke sana.

Cara saya mengatasinya adalah dengan mengkloning repo di tempat yang bukan merupakan ruang kerja saya dan kemudian membuat tautan simbolis di direktori ruang kerja saya ke subdirektori dalam repositori. Git berfungsi seperti ini dengan cukup baik karena hal-hal seperti status git akan menampilkan perubahan file relatif terhadap direktori kerja Anda saat ini.

Travis Stevens
sumber
Ini hanya berfungsi di OS yang mendukung tautan simbolik. Mereka perlu mengubah cara kerja pembayaran jarang.
Anders Lindén
1
+1 untuk gagasan dengan tautan simbolis pada direktori check out. Namun, checkout jarang dan tautan simbolik tidak eksklusif satu sama lain: Anda tidak perlu klon lengkap penuh.
apitsch
10

Sebenarnya, pemeriksaan "sempit" atau "parsial" atau "jarang" sedang dalam pengembangan saat ini, berat untuk Git. Catatan, Anda masih memiliki repositori lengkap di bawah .git. Jadi, dua posting lainnya adalah terbaru untuk kondisi Git saat ini, tetapi sepertinya kita akan dapat melakukan checkout jarang pada akhirnya. Periksa milis jika Anda tertarik pada detail lebih lanjut - mereka berubah dengan cepat.

Pat Notz
sumber
Senang mendengarnya! Saya memang suka memiliki direktori yang terkait erat di bawah satu repositori, dan akan melakukannya jika memungkinkan.
Annika Backstrom
6

git clone --filter dari Git 2.19

Opsi ini sebenarnya akan melewatkan mengambil objek yang tidak dibutuhkan dari server:

git clone --depth 1 --no-checkout --filter=blob:none \
  "file://$(pwd)/server_repo" local_repo
cd local_repo
git checkout master -- mdir/

Server harus dikonfigurasi dengan:

git config --local uploadpack.allowfilter 1
git config --local uploadpack.allowanysha1inwant 1

Tidak ada dukungan server pada v2.19.0, tetapi sudah dapat diuji secara lokal.

file://$(path)diperlukan untuk mengatasi git cloneprotokol shenanigans: Bagaimana cara dangkal mengkloning repositori git lokal dengan path relatif?

Ingat yang --depth 1sudah tersirat --single-branch, lihat juga: Bagaimana saya mengkloning satu cabang di Git?

TODO: --filter=blob:nonemelompati semua gumpalan, tetapi masih mengambil semua objek pohon. Tetapi pada repo yang normal, ini harus kecil dibandingkan dengan file itu sendiri, jadi ini sudah cukup baik. Ditanya di: https://www.spinics.net/lists/git/msg342006.html Devs menjawab a --filter=tree:0sedang mengerjakan hal itu.

Format --filterdidokumentasikan pada man git-rev-list.

Ekstensi dibuat untuk protokol jarak jauh Git untuk mendukung fitur ini.

Documents on Git tree:

Uji itu

#!/usr/bin/env bash
set -eu

list-objects() (
  git rev-list --all --objects
  echo "master commit SHA: $(git log -1 --format="%H")"
  echo "mybranch commit SHA: $(git log -1 --format="%H")"
  git ls-tree master
  git ls-tree mybranch | grep mybranch
  git ls-tree master~ | grep root
)

# Reproducibility.
export GIT_COMMITTER_NAME='a'
export GIT_COMMITTER_EMAIL='a'
export GIT_AUTHOR_NAME='a'
export GIT_AUTHOR_EMAIL='a'
export GIT_COMMITTER_DATE='2000-01-01T00:00:00+0000'
export GIT_AUTHOR_DATE='2000-01-01T00:00:00+0000'

rm -rf server_repo local_repo
mkdir server_repo
cd server_repo

# Create repo.
git init --quiet
git config --local uploadpack.allowfilter 1
git config --local uploadpack.allowanysha1inwant 1

# First commit.
# Directories present in all branches.
mkdir d1 d2
printf 'd1/a' > ./d1/a
printf 'd1/b' > ./d1/b
printf 'd2/a' > ./d2/a
printf 'd2/b' > ./d2/b
# Present only in root.
mkdir 'root'
printf 'root' > ./root/root
git add .
git commit -m 'root' --quiet

# Second commit only on master.
git rm --quiet -r ./root
mkdir 'master'
printf 'master' > ./master/master
git add .
git commit -m 'master commit' --quiet

# Second commit only on mybranch.
git checkout -b mybranch --quiet master~
git rm --quiet -r ./root
mkdir 'mybranch'
printf 'mybranch' > ./mybranch/mybranch
git add .
git commit -m 'mybranch commit' --quiet

echo "# List and identify all objects"
list-objects
echo

# Restore master.
git checkout --quiet master
cd ..

# Clone. Don't checkout for now, only .git/ dir.
git clone --depth 1 --quiet --no-checkout --filter=blob:none "file://$(pwd)/server_repo" local_repo
cd local_repo

# List missing objects from master.
echo "# Missing objects after --no-checkout"
git rev-list --all --quiet --objects --missing=print
echo

echo "# Git checkout fails without internet"
mv ../server_repo ../server_repo.off
! git checkout master
echo

echo "# Git checkout fetches the missing directory from internet"
mv ../server_repo.off ../server_repo
git checkout master -- d1/
echo

echo "# Missing objects after checking out d1"
git rev-list --all --quiet --objects --missing=print

GitHub hulu .

Output dalam Git v2.19:

# List and identify all objects
c6fcdfaf2b1462f809aecdad83a186eeec00f9c1
fc5e97944480982cfc180a6d6634699921ee63ec
7251a83be9a03161acde7b71a8fda9be19f47128
62d67bce3c672fe2b9065f372726a11e57bade7e
b64bf435a3e54c5208a1b70b7bcb0fc627463a75 d1
308150e8fddde043f3dbbb8573abb6af1df96e63 d1/a
f70a17f51b7b30fec48a32e4f19ac15e261fd1a4 d1/b
84de03c312dc741d0f2a66df7b2f168d823e122a d2
0975df9b39e23c15f63db194df7f45c76528bccb d2/a
41484c13520fcbb6e7243a26fdb1fc9405c08520 d2/b
7d5230379e4652f1b1da7ed1e78e0b8253e03ba3 master
8b25206ff90e9432f6f1a8600f87a7bd695a24af master/master
ef29f15c9a7c5417944cc09711b6a9ee51b01d89
19f7a4ca4a038aff89d803f017f76d2b66063043 mybranch
1b671b190e293aa091239b8b5e8c149411d00523 mybranch/mybranch
c3760bb1a0ece87cdbaf9a563c77a45e30a4e30e
a0234da53ec608b54813b4271fbf00ba5318b99f root
93ca1422a8da0a9effc465eccbcb17e23015542d root/root
master commit SHA: fc5e97944480982cfc180a6d6634699921ee63ec
mybranch commit SHA: fc5e97944480982cfc180a6d6634699921ee63ec
040000 tree b64bf435a3e54c5208a1b70b7bcb0fc627463a75    d1
040000 tree 84de03c312dc741d0f2a66df7b2f168d823e122a    d2
040000 tree 7d5230379e4652f1b1da7ed1e78e0b8253e03ba3    master
040000 tree 19f7a4ca4a038aff89d803f017f76d2b66063043    mybranch
040000 tree a0234da53ec608b54813b4271fbf00ba5318b99f    root

# Missing objects after --no-checkout
?f70a17f51b7b30fec48a32e4f19ac15e261fd1a4
?8b25206ff90e9432f6f1a8600f87a7bd695a24af
?41484c13520fcbb6e7243a26fdb1fc9405c08520
?0975df9b39e23c15f63db194df7f45c76528bccb
?308150e8fddde043f3dbbb8573abb6af1df96e63

# Git checkout fails without internet
fatal: '/home/ciro/bak/git/test-git-web-interface/other-test-repos/partial-clone.tmp/server_repo' does not appear to be a git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

# Git checkout fetches the missing directory from internet
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (1/1), 45 bytes | 45.00 KiB/s, done.
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (1/1), 45 bytes | 45.00 KiB/s, done.

# Missing objects after checking out d1
?8b25206ff90e9432f6f1a8600f87a7bd695a24af
?41484c13520fcbb6e7243a26fdb1fc9405c08520
?0975df9b39e23c15f63db194df7f45c76528bccb

Kesimpulan: semua gumpalan dari luar d1/hilang.

Catat itu root/rootdan mybranch/mybranchjuga hilang, tetapi --depth 1menyembunyikan itu dari daftar file yang hilang. Jika Anda menghapus --depth 1, maka mereka muncul di daftar file yang hilang.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
1

Saat hasil edit Anda menunjukkan, Anda dapat menggunakan dua cabang terpisah untuk menyimpan dua direktori terpisah. Ini membuat keduanya tetap dalam repositori yang sama, tetapi Anda masih tidak dapat memiliki komitmen yang mencakup kedua pohon direktori. Jika Anda memiliki perubahan di salah satu yang membutuhkan perubahan di yang lain, Anda harus melakukan itu sebagai dua komit terpisah, dan Anda membuka kemungkinan bahwa sepasang checkout dari dua direktori bisa tidak sinkron.

Jika Anda ingin memperlakukan pasangan direktori sebagai satu unit, Anda dapat menggunakan 'wordpress / wp-content' sebagai root dari repo Anda dan menggunakan file .gitignore di tingkat atas untuk mengabaikan segalanya kecuali dua subdirektori yang menarik. Ini mungkin solusi yang paling masuk akal pada saat ini.

Checkout jarang telah diduga datang selama dua tahun sekarang, tetapi masih belum ada tanda-tanda mereka dalam repo pengembangan git, juga tidak ada indikasi bahwa perubahan yang diperlukan akan pernah tiba di sana. Saya tidak akan mengandalkan mereka.

cjs
sumber
1

Anda tidak dapat checkout direktori tunggal repositori karena seluruh repositori ditangani oleh folder .git tunggal di root proyek alih-alih banyak subversi dari direktori .svn.

Masalah dengan bekerja pada plugin dalam repositori tunggal adalah membuat komit, misalnya, mitos akan menambah angka revisi untuk myplugin , jadi bahkan dalam subversi lebih baik menggunakan repositori terpisah.

Paradigma subversi untuk sub-proyek adalah svn: eksternal yang diterjemahkan agak ke submodul di git (tetapi tidak tepat jika Anda telah menggunakan svn: eksternal sebelumnya.)

MDCore
sumber
0

Ada inspirasi di sini. Hanya menggunakan shell regexatau git regex.

git checkout commit_id */*.bat  # *.bat in 1-depth subdir exclude current dir, shell regex  
git checkout commit_id '*.bat'  # *.bat in all subdir include current dir, git regex

Gunakan kutipan untuk menghindari interpretasi shell regex dan berikan wildcard untuk git.

Yang pertama tidak rekursif, hanya file dalam 1-kedalaman subdir. Tapi yang kedua bersifat rekursif.

Adapun situasi Anda, berikut ini mungkin cukup.

git checkout master */*/wp-content/*/*
git checkout master '*/wp-content/*'

Cukup retas garis sesuai kebutuhan.

W.Perrin
sumber
0

Anda dapat mengembalikan perubahan yang tidak dikomit hanya ke file atau direktori tertentu:

git checkout [some_dir|file.txt]
Yuliia Ashomok
sumber