Ambil komit tertentu dari repositori Git jarak jauh

189

Apakah ada cara untuk mengambil hanya satu komit tertentu dari repo Git jarak jauh tanpa mengkloningnya pada PC saya? Struktur repo jarak jauh sama sekali dengan repo saya dan karenanya tidak akan ada konflik tetapi saya tidak tahu bagaimana melakukan ini dan saya tidak ingin mengkloning repositori besar itu.

Saya baru git, apakah ada cara?

Varun Chitre
sumber
1
Apakah repo yang ada sudah menjadi klon dari yang jauh, atau apakah itu sama sekali berbeda?
CharlesB
Yah, repo itu adalah sumber kernel Linux, dan itu hampir sama
Varun Chitre
jadi itu klon atau bukan?
CharlesB
1
Tidak persis. Pertimbangkan ini, Biarkan repo jarak jauh berada di kepala D dan tambang saya di kepala A dan di belakang oleh B, C, D melakukan. Saya ingin menggabungkan komit B dari satu repo dan C dari yang lain dan D dari yang lain karena komitmen B, C, D dalam repo ini berbeda dengan spesialisasi mereka sendiri
Varun Chitre
1
@VarunChitre dapatkah Anda menerima jawaban lain dari VonC?
CharlesB

Jawaban:

109

Dimulai dengan Git versi 2.5+ (Q2 2015), mengambil komit tunggal (tanpa mengkloning repo penuh) sebenarnya mungkin.

Lihat commit 68ee628 oleh Fredrik Medley ( moroten) , 21 Mei 2015.
(Digabung oleh Junio ​​C Hamano - gitster- dalam commit a9d3493 , 01 Jun 2015)

Anda sekarang memiliki konfigurasi baru (di sisi server)

uploadpack.allowReachableSHA1InWant

Izinkan upload-packuntuk menerima permintaan pengambilan yang meminta objek yang dapat dijangkau dari ujung referensi apa pun. Namun, perhatikan bahwa menghitung jangkauan objek adalah mahal secara komputasi.
Default ke false.

Jika Anda menggabungkan konfigurasi sisi server dengan clone dangkal ( git fetch --depth=1), Anda dapat meminta satu komit (lihat t/t5516-fetch-push.sh:

git fetch --depth=1 ../testrepo/.git $SHA1

Anda dapat menggunakan git cat-fileperintah untuk melihat bahwa komit telah diambil:

git cat-file commit $SHA1

" git upload-pack" yang berfungsi " git fetch" dapat diperintahkan untuk melayani komit yang tidak ada di ujung referensi, selama mereka dapat dijangkau dari referensi, dengan uploadpack.allowReachableSHA1InWant variabel konfigurasi.


Dokumentasi lengkapnya adalah:

upload-pack: opsional memungkinkan mengambil sha1 yang dapat dijangkau

Dengan uploadpack.allowReachableSHA1InWantopsi konfigurasi yang diatur di sisi server, " git fetch" dapat membuat permintaan dengan baris "ingin" yang menamai objek yang belum diiklankan (kemungkinan telah diperoleh dari band atau dari pointer submodule).
Hanya objek yang dapat dijangkau dari ujung cabang, yaitu gabungan cabang dan cabang yang diiklankan yang disembunyikan transfer.hideRefs, akan diproses.
Perhatikan bahwa ada biaya terkait karena harus berjalan kembali ke sejarah untuk memeriksa jangkauan.

Fitur ini dapat digunakan ketika mendapatkan konten dari komit tertentu, yang sha1 diketahui, tanpa perlu mengkloning seluruh repositori, terutama jika pengambilan dangkal digunakan .

Kasus yang bermanfaat misalnya

  • repositori yang berisi file besar dalam sejarah,
  • hanya mengambil data yang diperlukan untuk checkout submodule,
  • saat berbagi sha1 tanpa memberi tahu cabang mana tepatnya miliknya dan di Gerrit, jika Anda berpikir dalam hal melakukan alih-alih mengubah nomor.
    (Kasus Gerrit telah diselesaikan allowTipSHA1InWantkarena setiap perubahan Gerrit memiliki referensi.)

Git 2.6 (Q3 2015) akan meningkatkan model itu.
Lihat komit 2bc31d1 , komit cc118a6 (28 Jul 2015) oleh Jeff King ( peff) .
(Digabung oleh Junio ​​C Hamano - gitster- dalam commit 824a0be , 19 Agu 2015)

refs: dukungan negatif transfer.hideRefs

Jika Anda menyembunyikan hierarki referensi menggunakan transfer.hideRefskonfigurasi, nantinya tidak ada cara untuk mengesampingkan konfigurasi tersebut untuk "menyembunyikannya".
Tambalan ini menerapkan menyembunyikan "negatif" yang menyebabkan kecocokan segera ditandai sebagai tidak disembunyikan, bahkan jika kecocokan lain akan menyembunyikannya.
Kami berhati-hati untuk menerapkan pertandingan dalam urutan terbalik dari bagaimana mereka diumpankan kepada kami oleh mesin konfigurasi, karena itu memungkinkan pekerjaan prioritas konfigurasi "terakhir yang menang" kami bekerja (dan entri dalam .git/config, misalnya, akan menimpa /etc/gitconfig).

Jadi sekarang Anda dapat melakukan:

git config --system transfer.hideRefs refs/secret
git config transfer.hideRefs '!refs/secret/not-so-secret'

bersembunyi refs/secretdi semua repo, kecuali satu bit publik dalam satu repo tertentu.


Git 2.7 (Nov / Des 2015) akan meningkat lagi:

Lihat komit 948bfa2 , komit 00b293e (5 November 2015), melakukan 78a766a , komit 92cab49 , komit 92cab49 , komit 92cab49 (3 November 2015), melakukan 00b293e , komit 00b293e (5 November 2015), dan komit 92cab49 , komit 92cab49 , komit 92cab49 , commit 92cab49 (03 Nov 2015) oleh Lukas Fleischer ( lfos) .
Dibantu-oleh: Eric Sunshine ( sunshineco) .
(Digabung oleh Jeff King - peff- di commit dbba85e , 20 Nov 2015)

config.txt: mendokumentasikan semantik hideRefsdengan namespaces

Saat ini, tidak ada definisi yang jelas tentang bagaimana transfer.hideRefsseharusnya berperilaku ketika namespace diatur.
Jelaskan bahwa hideRefsawalan cocok dengan nama yang dilucuti dalam kasus itu. Beginilah hideRefspola saat ini ditangani dalam paket-terima.

hideRefs: tambahkan dukungan untuk mencocokkan referensi penuh

Selain mencocokkan ref yang dilucuti, orang sekarang dapat menambahkan hideRefspola yang ref penuh (tidak terhalang) dicocokkan.
Untuk membedakan antara kecocokan stripped dan full, pola-pola baru tersebut harus diawali dengan circumflex ( ^).

Karenanya dokumentasi baru :

transfer.hideRefs:

Jika namespace sedang digunakan, awalan namespace dilucuti dari setiap referensi sebelum dicocokkan dengan transfer.hiderefspola.
Misalnya, jika refs/heads/masterditentukan transfer.hideRefsdan namespace saat ini adalah foo, maka refs/namespaces/foo/refs/heads/master dihilangkan dari iklan tetapi refs/heads/masterdan refs/namespaces/bar/refs/heads/mastermasih diiklankan sebagai apa yang disebut "memiliki" baris.
Untuk mencocokkan ref sebelum stripping, tambahkan ^di depan nama ref. Jika Anda menggabungkan !dan ^, !harus ditentukan terlebih dahulu.


R .. menyebutkan dalam komentar konfigurasi uploadpack.allowAnySHA1InWant, yang memungkinkan upload-packuntuk menerima fetchpermintaan yang meminta objek apa pun. (Default ke false).

Lihat commit f8edeaa (November 2016, Git v2.11.1) oleh David "novalis" Turner ( novalis) :

upload-pack: secara opsional mengizinkan mengambil sha1 apa pun

Tampaknya agak konyol untuk melakukan pemeriksaan reachabilty dalam kasus di mana kami percaya pengguna untuk benar-benar mengakses semua yang ada di repositori.

Juga, itu sangat kuat dalam sistem terdistribusi - mungkin satu server mengiklankan referensi, tetapi yang lain sejak itu telah memaksa-paksa untuk referensi itu, dan mungkin dua permintaan HTTP akhirnya diarahkan ke server yang berbeda ini.

VONC
sumber
4
Bisakah Anda memberikan contoh yang lebih lengkap tentang cara membuat repo clone hanya dengan komit tunggal ini? Saya mencoba tetapi gagal .. Terima kasih!
Lars Bilke
1
Saya ingin mendorong ke GitHub. Mungkin mereka tidak mengizinkan ini.
Lars Bilke
2
@ LarsBilke kita berbicara tentang kloning atau menarik di sini, bukan mendorong. Dan saya cukup yakin GitHub belum memiliki Git 2.5 di sisi server.
VonC
2
Sekarang bahkan lebih baik, ada uploadpack.allowAnySHA1InWanttanpa penalti reachability-komputasi (dan vektor DoS).
R .. GitHub BERHENTI MEMBANTU ICE
1
Terima kasih! Saya merasa lucu bahwa mereka menggambarkannya sebagai "percaya pengguna untuk mengakses" daripada "percaya penulis repo untuk tidak mendorong omong kosong acak mereka tidak bermaksud untuk membuat publik".
R .. GitHub BERHENTI MEMBANTU ICE
98

Anda hanya mengkloning sekali, jadi jika Anda sudah memiliki klon repositori jarak jauh, menariknya tidak akan mengunduh semuanya lagi. Cukup tunjukkan cabang apa yang ingin Anda tarik, atau ambil perubahan dan checkout komit yang Anda inginkan.

Mengambil dari repositori baru sangat murah dalam bandwidth, karena hanya akan mengunduh perubahan yang tidak Anda miliki. Pikirkan dalam hal Git membuat hal yang benar, dengan beban minimum.

Git menyimpan semua yang ada di .gitfolder. Komit tidak dapat diambil dan disimpan dalam isolasi, ia membutuhkan semua leluhurnya. Mereka saling terkait .


Untuk mengurangi ukuran unduhan, Anda dapat meminta git untuk mengambil hanya objek yang terkait dengan cabang tertentu atau melakukan:

git fetch origin refs/heads/branch:refs/remotes/origin/branch

Ini akan mengunduh hanya komit yang terdapat di cabang jarak jauh branch (dan hanya yang Anda lewatkan) , dan menyimpannya origin/branch. Anda kemudian dapat menggabungkan atau checkout.

Anda juga dapat menentukan hanya komit SHA1:

git fetch origin 96de5297df870:refs/remotes/origin/foo-commit

Ini hanya akan mengunduh komitmen dari SHA-1 96de5297df870 yang ditentukan (dan leluhurnya yang Anda lewatkan), dan menyimpannya sebagai cabang jarak jauh (yang tidak ada) origin/foo-commit.

CharlesB
sumber
3
Sepertinya Anda membuat kebingungan tentang apa artinya kloning. Saat Anda mengambil perubahan dari repo jarak jauh yang tidak Anda tiru, Anda hanya mendapatkan komitmen dalam riwayat Anda. Kemudian Anda memilih komit yang ingin Anda periksa, atau menggabungkannya dalam riwayat Anda
CharlesB
1
Masih mengunduh banyak data (430mb) dengan git fetch. Komit yang diperlukan hanya beberapa kb. Tidak ada perintah khusus untuk melakukan ini? Dan bagaimana jika saya ingin menghapus repo 'git fetched'? dimana itu disimpan?
Varun Chitre
9
Ini agak ketinggalan zaman sekarang. Kami memiliki kemampuan untuk melakukan klon dangkal , serta mengambil satu komit . Klon dangkal sekarang diperbolehkan untuk mendorong dan mengambil secara normal, tanpa harus mengetahui sejarah lengkap proyek, sehingga tidak benar lagi untuk mengatakan bahwa sebuah komit tidak dapat ada sendirian tanpa leluhurnya. Apa yang Anda katakan tentang mengambil setelah klon awal sangat benar, tetapi kami juga memiliki opsi yang lebih murah.
Theodore Murdock
6
Perintah terakhir (menggunakan SHA1 commit) tidak berfungsi untuk saya. Perintah diam-diam melakukan "sesuatu" untuk sementara waktu, dan kemudian keluar tanpa pesan atau efek samping yang jelas.
HRJ
1
@ HRJ Ya, saya menemukan ini juga, di Ubuntu 16.04 dengan Git 2.7.4-0ubuntu1.3. Namun, ketika menggunakan 2.16.2-0ppa1~ubuntu16.04.1dari PPA git-core, ini berfungsi sebagaimana mestinya. Kedengarannya seperti bug yang diperbaiki. Tidak dapat menemukan referensi untuk itu dengan pencarian cepat. Jika seseorang bisa memberi saya petunjuk tentang hal itu, saya akan senang mendapatkan perbaikan ini di-backport.
gertvdijk
62

Saya melakukan git repo:

git pull --rebase <repo> <branch>

Mengizinkan git untuk menarik semua kode untuk cabang dan kemudian saya melakukan reset ke komit yang menarik minat saya.

git reset --hard <commit-hash>

Semoga ini membantu.

Piu Sharma
sumber
1
Tidak ada jawaban yang berhasil, tetapi yang ini menyelamatkan hidup saya! Terima kasih banyak!
michaeltintiuc
Reset --hard bekerja untuk saya setelah kloning! Terima kasih.
Nick-ACNB
3
-1: perintah "destruktif" seperti git reset --hard, ketika dibagikan dalam solusi umum, dapat mengarahkan orang ke dalam jebakan di mana mereka kehilangan data (atau, dalam kasus ini: dalam keadaan di mana mendapatkan kembali data mereka tidak penting).
yaauie
54

Anda dapat mengambil komit tunggal dari repo jarak jauh dengan

git fetch <repo> <commit>

dimana,

  • <repo>dapat berupa nama repo jarak jauh (mis. origin) atau bahkan URL repo jarak jauh (mis. https://git.foo.com/myrepo.git)
  • <commit> dapat berupa komit SHA1

sebagai contoh

git fetch https://git.foo.com/myrepo.git 0a071603d87e0b89738599c160583a19a6d95545

Setelah Anda mengambil komit (dan leluhur yang hilang), Anda dapat checkout dengan

git checkout FETCH_HEAD

Perhatikan bahwa ini akan membawa Anda dalam kondisi "kepala terpisah".

Mengalir
sumber
10
Ketika saya mencoba fetchrev tertentu seperti yang Anda lakukan di sana, git gagal dengan kode kesalahan 1 dan tidak ada output. Apakah ini sesuatu yang dulu berfungsi di versi sebelumnya? (Saya v2.0.2.)
Jack O'Connor
2
Sunting: Ini berfungsi jika saya sudah memiliki komit itu secara lokal, seperti jika saya sudah melakukan penuh fetch, meskipun dalam kasus itu saya tidak yakin apa gunanya.
Jack O'Connor
2
Memang, ini sepertinya tidak berfungsi lagi untuk saya dengan git 2.0.2. :(
Alur
2
git checkout FETCH_HEADmembantu.
lzl124631x
1
Metode ini tidak akan bekerja dengan pengambilan dangkal (misalnya --depth=1)!
kingmakerking
16

Anda dapat mengambil repo jarak jauh dengan:

git fetch <repo>

dimana,

  • <repo>dapat berupa nama repo jarak jauh (mis. origin) atau bahkan URL repo jarak jauh (mis. https://git.foo.com/myrepo.git)

sebagai contoh:

git fetch https://git.foo.com/myrepo.git 

setelah Anda mengambil repo Anda dapat menggabungkan komit yang Anda inginkan (karena pertanyaannya adalah tentang mengambil satu komit, alih-alih menggabungkan Anda dapat menggunakan cherry-pick untuk memilih hanya satu komit):

git merge <commit>
  • <commit> dapat berupa komit SHA1

sebagai contoh:

git cherry-pick 0a071603d87e0b89738599c160583a19a6d95545

atau

git merge 0a071603d87e0b89738599c160583a19a6d95545

jika komit terbaru yang ingin Anda gabungkan, Anda juga dapat menggunakan variabel FETCH_HEAD:

git cherry-pick (or merge) FETCH_HEAD
Sérgio
sumber
Ini membutuhkan pengaturan akun Git pada mesin. Ini tidak berfungsi di bawah akun uji. Apakah Anda memiliki sesuatu yang berfungsi di bawah akun uji?
jww
apa maksudmu ? kamu tidak bisa melakukan git fetch?
Sérgio
Ummm jadi perintahnya git config set uploadpack.allowReachableSHA1InWant ?
Alexander Mills
2

Ini yang terbaik:

git fetch origin specific_commit
git checkout -b temp FETCH_HEAD

nama "temp" apa pun yang Anda inginkan ... cabang ini mungkin menjadi yatim piatu

Alexander Mills
sumber
Jelas TIDAK dengan versi git yang lebih lama seperti 1.8.x
sorin
1

Akhirnya saya menemukan cara untuk mengkloning komit tertentu menggunakan git cherry-pick . Dengan asumsi Anda tidak memiliki repositori di lokal dan Anda menarik komit tertentu dari jarak jauh,

1) membuat repositori kosong di lokal dan git init

2) git remote tambahkan asal " url-of-repository "

3) git fetch origin [ini tidak akan memindahkan file Anda ke ruang kerja lokal Anda kecuali jika Anda bergabung]

4) git cherry-pick " Enter-long-commit-hash-that-you-need "

Selesai. Dengan cara ini, Anda hanya akan memiliki file dari komit tertentu di lokal Anda.

Masukkan-long-commit-hash:

Anda bisa mendapatkannya menggunakan -> git log --pretty = oneline

surya deepak
sumber
0

Saya pikir 'git ls-remote' ( http://git-scm.com/docs/git-ls-remote ) harus melakukan apa yang Anda inginkan. Tanpa paksa mengambil atau menarik.

Hubbitus
sumber
0

Jika komit yang diminta ada dalam permintaan tarik repo jarak jauh, Anda bisa mendapatkannya dengan ID-nya:

# Add the remote repo path, let's call it 'upstream':
git remote add upstream https://github.com/repo/project.git

# checkout the pull ID, for example ID '60':
git fetch upstream pull/60/head && git checkout FETCH_HEAD
Noam Manos
sumber