Kursus
Anda sedang mengerjakan proyek analisis data dan perlu menyertakan library utilitas bersama yang dikelola tim Anda di repositori lain. Anda bisa saja menyalin-tempel kodenya, tetapi Anda akan kehilangan jalur pembaruan. Anda bisa menggunakan submodule Git, tetapi konon itu rumit. Ada opsi ketiga: git subtree.
git subtree memungkinkan Anda menyematkan satu repositori Git ke dalam repositori lain sebagai subdirektori, sekaligus mempertahankan riwayat lengkap dan jalur pembaruan yang mudah.
Panduan ini membahas kapan harus menggunakan git subtree, cara kerjanya, dan contoh praktis untuk alur kerja umum. Kita akan menggunakan skenario realistis yang mungkin Anda temui dalam proyek data science.
Apa itu git subtree?
Git subtree menyertakan repositori Git lain di bawah folder tertentu dalam proyek Anda sambil mempertahankan seluruh riwayat commit-nya. Berbeda dengan menyalin kode atau menggunakan symbolic link, subtree mempertahankan koneksi ke repositori asli sehingga Anda dapat menarik pembaruan dan mendorong perubahan kembali.
Berikut yang membuatnya berbeda dari sekadar menyalin kode:
Menyalin-tempel kode:
- Tidak ada koneksi ke repo asli
- Tidak ada jalur pembaruan saat library berubah
- Tidak ada riwayat asal-usul kode
Git subtree:
- Riwayat commit lengkap dari repo asli
- Menarik pembaruan dengan satu perintah
- Mendorong perubahan kembali ke repo asli jika diperlukan
- Semuanya berada dalam satu checkout repositori
Perbedaan kunci dari submodule Git: konten subtree dikomit langsung ke repositori utama Anda. Saat seseorang mengkloning proyek Anda, mereka mendapatkan semuanya dalam satu checkout tanpa langkah penyiapan tambahan.
Semua ini bermuara pada onboarding yang lebih sederhana dan lebih sedikit masalah “berfungsi di mesin saya”.
Cara Kerja git subtree
Saat Anda menambahkan subtree, Git melakukan sesuatu yang cerdas: ia menggabungkan riwayat repositori lain ke dalam subdirektori proyek Anda. Berikut yang terjadi:
- Git mengambil riwayat repositori remote
- Git menulis ulang commit-commit tersebut sehingga muncul di bawah subdirektori pilihan Anda
- Git menggabungkan riwayat yang telah ditulis ulang ini ke cabang Anda saat ini
- Pembaruan mendatang mengikuti pola yang sama: fetch, rewrite, merge
Repositori Anda berisi file nyata dan riwayat commit penuh dari subtree, bukan hanya penunjuk atau referensi. Saat seseorang mengkloning proyek Anda atau checkout cabang, mereka langsung memiliki semua kode. Tidak ada langkah terpisah “initialize submodules”.
Konsekuensinya: repositori Anda membesar karena berisi kode Anda dan kode subtree beserta riwayat lengkapnya. Apakah sepadan? Tergantung situasi Anda, tetapi bagi sebagian besar tim data science, saya rasa demikian.
Cara Menggunakan git subtree (Perintah Umum)
Sekarang setelah Anda memahami apa yang dilakukan subtree, mari kita bahas operasi yang paling umum. Kita akan menggunakan skenario realistis saat Anda membangun proyek analisis data dan perlu menyertakan library utilitas bersama.
Contoh penyiapan:
-
Proyek utama:
data-pipeline -
Library bersama:
shared-utils(berada dihttps://github.com/yourteam/shared-utils.git) -
Anda ingin menyertakannya di bawah
libs/shared-utils/dalam proyek Anda
git subtree add
Perintah add menyertakan repositori lain ke dalam proyek Anda untuk pertama kalinya.
Sintaks dasar, sebagai referensi, seperti ini:
git subtree add --prefix=<directory> <remote-url> <branch> --squash
Contoh nyata:
# Tambahkan repo shared-utils di bawah libs/shared-utils
git subtree add --prefix=libs/shared-utils \
https://github.com/yourteam/shared-utils.git \
main \
--squash
Apa yang dilakukan perintah ini:
- Membuat direktori libs/shared-utils
- Menyalin semua file dari shared-utils ke dalamnya
- Mengkomit semuanya ke repositori Anda
- Mencatat koneksi untuk pembaruan di masa mendatang
Memahami flag:
-
--prefix=libs/shared-utils: Lokasi subtree dalam proyek Anda. Anda harus menggunakan prefix yang persis sama untuk semua operasi selanjutnya. Mengubahnya sekali saja dan Anda akan mencari-cari di Stack Overflow jam 2 pagi mencoba memahami mengapa semuanya rusak. -
--squash: Menggabungkan seluruh riwayat commit subtree menjadi satu commit di proyek Anda. Ini membuat riwayat proyek Anda lebih rapi. Tanpa --squash, Anda akan melihat setiap commit dari repo asli di riwayat proyek Anda, yang bisa berlebihan.
Kapan men-squash: Gunakan --squash kecuali Anda perlu mempertahankan atribusi commit terperinci dari repo asli. Untuk sebagian besar proyek data dengan utilitas bersama, riwayat yang di-squash lebih rapi dan mudah digunakan. Saya selalu menggunakannya.
Setelah menjalankan ini, Anda akan melihat commit baru di proyek Anda dengan pesan seperti "Add 'libs/shared-utils/' from commit 'abc123'". Direktori libs/shared-utils kini berisi semua file, dan Anda bisa langsung menggunakannya.
git subtree pull
Perintah pull memperbarui subtree Anda dengan perubahan dari repositori asli. Jika tim library bersama memperbaiki bug atau menambahkan fitur, ini cara Anda mendapatkan pembaruan tersebut.
Sintaks dasar:
git subtree pull --prefix=<directory> <remote-url> <branch> --squash
Contoh nyata:
# Tarik perubahan terbaru dari shared-utils
git subtree pull --prefix=libs/shared-utils \
https://github.com/yourteam/shared-utils.git \
main \
--squash
Apa yang dilakukan perintah ini:
- Mengambil commit baru dari repositori remote
- Menggabungkannya ke dalam direktori libs/shared-utils Anda
- Membuat merge commit di proyek Anda
Prefix harus persis sama. Jika Anda menggunakan libs/shared-utils saat add, Anda harus menggunakan libs/shared-utils untuk pull. Menggunakan prefix berbeda tidak akan berhasil. Percayalah.
Konsistensi squash: Jika Anda menggunakan --squash saat add, Anda harus menggunakan --squash untuk setiap pull. Mencampur pembaruan yang di-squash dan tidak di-squash menciptakan riwayat yang membingungkan. Saya belajar ini dengan cara yang sulit.
Menangani konflik: Jika Anda telah memodifikasi file di libs/shared-utils dan file yang sama juga berubah di hulu, Anda akan mendapatkan konflik merge. Selesaikan seperti Anda menyelesaikan konflik merge Git apa pun—edit file, stage, dan selesaikan merge.
Dokumentasikan di README tim apakah Anda menggunakan --squash atau tidak, agar semua orang tetap konsisten.
git subtree push
Perintah push mengirim perubahan yang Anda buat di folder subtree kembali ke repositori asli.
Sintaks dasar:
git subtree push --prefix=<directory> <remote-url> <branch>
Contoh nyata:
# Dorong perubahan dari libs/shared-utils kembali ke repo asli
git subtree push --prefix=libs/shared-utils \
https://github.com/yourteam/shared-utils.git \
main
Apa yang dilakukan perintah ini:
- Mengekstrak hanya commit yang memengaruhi libs/shared-utils
- Menulis ulang seakan-akan dibuat di root shared-utils
- Mendorong commit tersebut ke repositori asli
Kapan digunakan: Anda memelihara library bersama di dalam repositori aplikasi dan ingin menyumbangkan perbaikan kembali. Misalnya Anda menambahkan fungsi validasi data yang berguna ke shared-utils saat mengerjakan proyek data-pipeline, dan proyek lain harus bisa memanfaatkannya.
Anggap push sebagai kebalikan dari pull. Pull membawa perubahan masuk; push mengirimkan perubahan keluar. Git mengekstrak hanya riwayat yang relevan dan menerjemahkannya kembali ke struktur repositori asli. Cukup cerdas.
Anda memerlukan akses tulis ke repositori asli. Jika tidak punya izin, Anda perlu melakukan fork repo, push ke fork Anda, dan membuat pull request.
git subtree split
Perintah split mengekstrak riwayat subdirektori ke cabang tersendiri.
Sintaks dasar:
git subtree split --prefix=<directory> -b <new-branch-name>
Berikut contoh nyata:
# Ekstrak libs/shared-utils ke cabangnya sendiri
git subtree split --prefix=libs/shared-utils -b shared-utils-extracted
Apa yang dilakukan perintah ini:
- Membuat cabang baru yang hanya berisi commit yang memengaruhi libs/shared-utils
- Cabang ini terlihat seperti repositori mandiri hanya untuk direktori tersebut
- Cabang asli tetap tidak berubah
Kasus penggunaan umum:
Memisahkan library dari monorepo: Pipeline pemrosesan data Anda telah membesar, dan Anda ingin mengekstrak komponen yang dapat digunakan ulang sebagai library mandiri. Split membuat cabang hanya dengan riwayat komponen tersebut, yang kemudian dapat Anda push ke repositori baru.
Menerbitkan subdirektori sebagai proyek tersendiri: Anda membangun modul visualisasi data yang berguna di dalam proyek analisis dan ingin membagikannya sebagai paket mandiri. Split mengekstraknya dengan riwayat penuh yang tetap utuh.
Contoh alur kerja membuat repo mandiri baru:
# Ekstrak subdirektori
git subtree split --prefix=libs/shared-utils -b shared-utils-standalone
# Buat repo baru dan push ke sana
git remote add shared-utils-origin https://github.com/yourteam/shared-utils-new.git
git push shared-utils-origin shared-utils-standalone:main
Sekarang shared-utils-new adalah repositori lengkap dengan riwayat penuh hanya untuk subdirektori tersebut.
git subtree vs. submodule
Inilah yang sering ditanyakan orang: subtree versus submodule.
Keduanya memungkinkan Anda menyertakan kode eksternal di repositori, tetapi ada perbedaan. Jika Anda perlu penjepitan versi yang presisi, atau jika ukuran repo terbatas, gunakan submodule. Jika tim Anda memiliki tingkat kemampuan Git yang beragam dan Anda menginginkan kesederhanaan, gunakan subtree. Selain itu, keduanya bisa bekerja.
|
Aspek |
git subtree |
git submodule |
|
Kompleksitas penyiapan |
Sedang (perintah langsung) |
Tinggi (memerlukan langkah |
|
Pengalaman clone |
Sederhana (satu clone, semuanya berfungsi) |
Kompleks ( |
|
Ukuran repositori |
Lebih besar (mencakup konten subtree penuh + riwayat) |
Lebih kecil (hanya penunjuk ke repo lain) |
|
Onboarding developer |
Mudah (semuanya berfungsi setelah clone) |
Lebih sulit (harus memahami alur kerja submodule) |
|
Kompleksitas CI/CD |
Sederhana (clone dan langsung jalan) |
Lebih kompleks (harus menginisialisasi submodule) |
|
Penjepitan versi |
Kurang presisi (menggabungkan commit terbaru atau tertentu) |
Presisi (hash commit persis) |
|
Alur pembaruan |
|
|
|
Risiko "berfungsi di mesin saya" |
Lebih rendah (semuanya dikomit) |
Lebih tinggi (keadaan submodule bisa menyimpang) |
|
Mendorong perubahan kembali |
|
git push normal di dalam submodule |
|
Pemisahan tanggung jawab |
Campur (kode berada di repo utama) |
Jelas (submodule adalah repo terpisah) |
Gunakan git submodule ketika Anda memerlukan:
- Penjepitan versi ketat (harus menggunakan commit X tepat dari library Y)
- Pemisahan yang jelas antar proyek (benar-benar independen)
- Beberapa proyek berbagi dependensi yang sama pada versi berbeda
- Ukuran repositori utama yang kecil penting bagi alur kerja Anda
Gunakan git subtree ketika Anda menginginkan:
- Tanpa langkah inisialisasi tambahan
- Anggota tim baru meng-clone sekali lalu menjalankan pengujian
- Lebih sedikit konsep Git yang perlu dipelajari tim Anda
- Vendoring dependensi yang sesekali ditarik pembaruannya
Tidak ada yang selalu lebih baik. Pilihan bergantung pada apakah Anda lebih menghargai kesederhanaan dibanding kontrol versi yang ketat. Menurut saya: Untuk tim data science di mana sebagian besar orang fokus pada analisis alih-alih seluk-beluk Git, subtree sering mengurangi friksi. Untuk tim platform yang mengelola layanan dengan persyaratan dependensi, submodule masuk akal.

Kapan Tidak Menggunakan git subtree
Jangan gunakan git subtree saat ukuran repositori merupakan batas keras. Subtree membengkakkan ukuran repo karena mencakup konten dan riwayat penuh. Jika Anda mendekati batas platform Git atau jika kecepatan jaringan sangat penting, submodule lebih ringan.
Juga, jangan gunakan git subtree jika Anda memerlukan penjepitan versi ketat. Anda harus menggunakan tepat versi 2.3.1 dari library dan tidak ada yang lain. Submodule menjepit ke commit persis; subtree menggabungkan rentang commit.
Jangan gunakan git subtree jika repo eksternal sering berubah. Subtree diperbarui setiap hari dengan perubahan signifikan. Terus-menerus menarik pembaruan menciptakan riwayat merge yang berantakan. Pertimbangkan apakah Anda benar-benar perlu menyematkannya atau apakah pengelola paket akan lebih rapi.
Terakhir, jangan gunakan git subtree jika pemisahan organisasi diperlukan. Konten subtree memiliki kontrol akses, lisensi, atau kepemilikan yang berbeda. Menjaganya di repositori terpisah membuat batasan lebih jelas.
Keuntungan git subtree
Pengalaman clone satu repositori: Jalankan git clone, dan selesai. Semua yang Anda butuhkan ada. Ini mengurangi friksi onboarding bagi tim di mana tidak semua orang ahli Git.
Lebih sedikit komponen bergerak dibanding submodule: Tidak ada langkah inisialisasi terpisah. Tidak ada keadaan HEAD terlepas yang perlu dijelaskan. Tidak ada kebingungan “submodule Anda tidak sinkron”. Alur kerja lebih dekat ke operasi Git standar (add, pull, push).
Kesederhanaan CI/CD: Skrip continuous integration Anda lebih sederhana. Dengan subtree, Anda clone dan jalankan pengujian. Dengan submodule, Anda clone, inisialisasi secara rekursif, lalu jalankan pengujian. Langkah ekstra itu membuat build gagal saat terlupa.
Bekerja baik untuk tim dengan kenyamanan Git yang beragam: Praktisi data junior tidak perlu memahami mekanisme submodule. Mereka menggunakan perintah Git yang familiar dan semuanya berfungsi.
Bagus untuk vendoring dengan pembaruan sesekali: Anda menyertakan versi 1.2 dari sebuah library. Enam bulan kemudian, versi 1.3 punya perbaikan bug yang Anda inginkan. Tarik dengan satu perintah. Anda tidak terjebak dengan kode salinan yang tak terpelihara, namun juga tidak terus-menerus melacak perubahan hulu.

Keterbatasan dan Kompromi git subtree
Repositori tumbuh signifikan: Repo Anda berisi kode Anda dan kode subtree beserta riwayat commit penuh (kecuali Anda menggunakan --squash, yang membantu). Subtree 50MB menambah sekitar 50MB ke ukuran repo Anda.
Untuk subtree besar atau banyak subtree, ini bertambah. Pertimbangkan apakah kemudahan ini sepadan dengan ruang disk dan waktu clone.
-
Kompleksitas grafik riwayat: Bahkan dengan
--squash, merge commit untuk pembaruan subtree menambah kompleksitas pada riwayat Git Anda. Jika Anda sering menarik pembaruan subtree, grafik commit Anda menjadi berantakan. -
Kebingungan perbedaan dengan upstream: Jika Anda memodifikasi file subtree dan upstream juga mengubahnya, konflik merge terjadi. Menyelesaikan konflik di subtree bisa membingungkan karena Anda menggabungkan repo orang lain ke subdirektori milik Anda.
-
Alur push punya jebakan: git subtree push perlu menulis ulang riwayat, yang bisa lambat untuk subtree besar. Jika Anda membuat banyak commit di folder subtree, push bisa memakan waktu. Bersabarlah.
-
Konflik merge tetap terjadi: Jika Anda dan upstream sama-sama memodifikasi file yang sama, Anda harus menyelesaikan konflik. Ini bukan khusus subtree, dan subtree tidak secara ajaib mencegah konflik Git.
-
Diperlukan disiplin: Menggunakan nilai
--prefixberbeda atau flag--squashyang tidak konsisten menimbulkan masalah. Tim perlu mendokumentasikan dan mengikuti alur kerja yang sama.
Keterbatasan ini bukan penghalang, tetapi patut diketahui sejak awal. Komprominya: alur kerja lebih sederhana dengan imbalan ukuran repo lebih besar dan potensi kompleksitas riwayat.
Kesalahan Umum Saat Menggunakan git subtree
Saat mulai menggunakan git subtree, waspadai masalah umum berikut. Ini mudah dihindari setelah Anda mengetahuinya.
Penggunaan --squash yang tidak konsisten
Anda menggunakan --squash saat menambahkan subtree tetapi lupa saat git subtree pull. Sekarang riwayat Anda memiliki commit subtree yang di-squash dan tidak di-squash, membuatnya membingungkan.
Solusinya adalah mendokumentasikan di README proyek. Tulis catatan seperti, "Selalu gunakan --squash untuk subtree shared-utils."
Prefix salah atau berubah-ubah
Anda menambahkan subtree dengan --prefix=libs/shared-utils tetapi kemudian mencoba pull dengan --prefix=libs/utils. Ini tidak berfungsi karena Git tidak dapat menemukan subtree.
Solusinya adalah menggunakan prefix yang persis sama setiap saat. Pertimbangkan untuk mendokumentasikannya dalam skrip, seperti ini:
# scripts/update-subtree.sh
#!/bin/bash
git subtree pull --prefix=libs/shared-utils \
https://github.com/yourteam/shared-utils.git \
main \
--squash
Mengharapkan subtree berperilaku seperti submodule
Anda mengira bisa cd libs/shared-utils && git checkout untuk mengganti versi. Itu tidak berfungsi karena konten subtree hanyalah file di repo Anda, bukan repositori Git terpisah.
Solusinya adalah memahami bahwa subtree menggabungkan commit tertentu. Untuk “menurunkan versi” subtree, Anda perlu menemukan dan menggabungkan commit yang lebih lama atau membalikkan commit pembaruan subtree.
Tidak mendokumentasikan alur kerja
Anggota tim tidak tahu apakah harus menggunakan --squash, URL remote apa yang digunakan, atau kapan harus mendorong perubahan kembali. Jika atau ketika semua orang melakukannya berbeda, itu akan menciptakan riwayat yang tidak konsisten.
Solusinya adalah menambahkan bagian ke README Anda:
## Memperbarui shared-utils
## Untuk menarik perubahan terbaru:
git subtree pull --prefix=libs/shared-utils https://github.com/yourteam/shared-utils.git main --squash
## Untuk mendorong perubahan kembali:
git subtree push --prefix=libs/shared-utils https://github.com/yourteam/shared-utils.git main
Terakhir, memodifikasi file subtree tanpa perencanaan
Anda mengedit file di libs/shared-utils untuk proyek spesifik Anda, tetapi tidak pernah mendorong kembali. Enam bulan kemudian, Anda menarik pembaruan dan terjadi konflik karena perubahan Anda dan perubahan upstream bertabrakan.
Solusinya: Jika Anda memodifikasi file subtree, dorong perubahan kembali ke shared-utils jika bermanfaat untuk semua orang, atau terima bahwa Anda perlu menangani konflik saat pembaruan.
Kesimpulan
Git subtree menukar ukuran repositori dan kompleksitas riwayat dengan kesederhanaan alur kerja. Untuk tim data science di mana sebagian besar orang fokus pada analisis alih-alih seluk-beluk Git, subtree mengurangi friksi. Anggota tim baru meng-clone repo dan langsung mulai bekerja.
Untuk mempelajari lebih lanjut tentang alur kerja Git, jelajahi Introduction to Git kami.
Penulis teknis yang mengkhususkan diri pada AI, ML, dan ilmu data, membuat gagasan kompleks menjadi jelas dan mudah diakses.

