Tampilan
Bab 37 — Git & GitHub
Git bukan topik Flutter, tetapi ia adalah keterampilan yang paling sering dianggap sudah dikuasai padahal tidak. Bab ini membahas apa yang benar-benar kamu butuhkan untuk mengelola proyek Flutter — termasuk hal-hal khusus Flutter seperti berkas apa yang tidak boleh ikut ter-commit.
Kenapa ini penting sekarang
Sampai bab ini, kamu sudah membangun aplikasi dengan kunci API, konfigurasi Firebase, dan berkas rahasia. Satu commit yang ceroboh bisa membocorkan semuanya ke internet secara permanen — dan menghapusnya dari riwayat Git jauh lebih sulit daripada mencegahnya.
Konsep dasar
WORKING DIRECTORY STAGING AREA REPOSITORY
(berkas yang kamu edit) (siap di-commit) (riwayat permanen)
│ │ │
│ git add │ git commit │
├───────────────────────►│───────────────────►│
│ │ │
│◄───────────────────────┴────────────────────┤
│ git checkout / git restore │
│
git push │
▼
GITHUBWorking directory adalah berkas di komputermu saat ini.
Staging area adalah tempat kamu memilih perubahan mana yang akan masuk ke commit berikutnya. Ini yang memungkinkan kamu membuat commit yang rapi meskipun sedang mengerjakan beberapa hal sekaligus.
Repository adalah riwayat commit yang tersimpan permanen.
Memulai
bash
# Konfigurasi sekali di komputermu
git config --global user.name "Nama Kamu"
git config --global user.email "email@contoh.com"
# Nama branch utama — 'main' adalah standar sekarang
git config --global init.defaultBranch main
# Editor untuk pesan commit
git config --global core.editor "code --wait"Di dalam folder proyek Flutter:
bash
git init
git add .
git commit -m "Initial commit: proyek Flutter baru".gitignore untuk Flutter
Ini bagian yang paling penting dan paling sering salah.
flutter create sudah membuat .gitignore dasar, tetapi ia tidak mencakup berkas rahasia yang kamu tambahkan sendiri.
gitignore
# ============================================
# Flutter & Dart
# ============================================
.dart_tool/
.packages
.pub-cache/
.pub/
build/
.flutter-plugins
.flutter-plugins-dependencies
.metadata
# Berkas ini HARUS di-commit — jangan diabaikan
!pubspec.lock
# ============================================
# IDE
# ============================================
.idea/
*.iml
*.iws
.vscode/*
!.vscode/settings.json
!.vscode/launch.json
!.vscode/extensions.json
# ============================================
# Android
# ============================================
android/.gradle/
android/local.properties
android/key.properties # ⚠️ berisi kata sandi keystore
android/app/*.jks # ⚠️ keystore penandatanganan
android/app/*.keystore
android/app/google-services.json # ⚠️ kalau repositori publik
# ============================================
# iOS
# ============================================
ios/Pods/
ios/.symlinks/
ios/Flutter/flutter_export_environment.sh
ios/Flutter/Generated.xcconfig
ios/Runner/GoogleService-Info.plist # ⚠️ kalau repositori publik
*.mobileprovision
*.p8
*.p12
*.cer
# ============================================
# Rahasia
# ============================================
.env
.env.*
!.env.example
lib/config/rahasia.dart
service-account*.json
*-adminsdk-*.json # ⚠️ kunci Firebase Admin
# ============================================
# Sistem
# ============================================
.DS_Store
Thumbs.db
*.logBerkas yang paling berbahaya kalau bocor
| Berkas | Risikonya |
|---|---|
android/key.properties | Kata sandi keystore — orang lain bisa merilis pembaruan palsu |
*.jks / *.keystore | Kunci penandatanganan aplikasi |
*-adminsdk-*.json | Akses penuh ke seluruh proyek Firebase |
.env dengan sk_... | Kunci rahasia Stripe |
*.p8 | Kunci APNs |
Kalau salah satu ini pernah ter-commit, menghapusnya di commit berikutnya tidak cukup — ia tetap ada di riwayat. Kamu harus menganggapnya bocor dan segera membuat kunci baru.
Tentang pubspec.lock dan google-services.json
Dua berkas ini sering diperdebatkan.
pubspec.lock sebaiknya di-commit untuk aplikasi. Ia mencatat versi persis setiap paket, sehingga seluruh tim dan CI memakai versi yang sama. Untuk pustaka yang akan dipakai orang lain, jangan di-commit.
google-services.json tidak berisi rahasia (lihat Bab 27), tetapi ia menunjukkan struktur proyek Firebase-mu. Untuk repositori privat, aman di-commit. Untuk repositori publik, lebih baik diabaikan dan disediakan lewat CI.
Berkas yang sudah terlanjur ter-commit
bash
# Hapus dari Git tapi pertahankan di komputer
git rm --cached android/key.properties
echo "android/key.properties" >> .gitignore
git commit -m "Hapus berkas rahasia dari version control"Untuk menghapusnya dari seluruh riwayat:
bash
# Perlu dipasang: pip install git-filter-repo
git filter-repo --path android/key.properties --invert-pathsAnggap kunci yang pernah ter-push sudah bocor
Menghapus dari riwayat tidak menghapusnya dari klona orang lain, dari cache GitHub, atau dari bot yang memindai repositori publik.
Kalau kunci rahasia pernah ter-push ke repositori publik, buat kunci baru dan cabut yang lama. Itu satu-satunya perbaikan yang sungguhan.
Alur kerja harian
bash
# Melihat apa yang berubah
git status
git diff # perubahan yang belum di-stage
git diff --staged # yang sudah di-stage
# Memilih perubahan
git add lib/screens/beranda.dart
git add lib/ # seluruh folder
git add . # semuanya
git add -p # interaktif, per potongan
# Commit
git commit -m "Tambah layar detail polling"
# Melihat riwayat
git log --oneline --graph --decorate --all
git log -p lib/main.dart # riwayat satu berkasgit add -p layak dibiasakan. Ia menampilkan setiap potongan perubahan dan menanyakan apakah kamu ingin memasukkannya — sangat berguna untuk memisahkan perbaikan bug dari eksperimen yang tidak sengaja tertinggal.
Pesan commit yang baik
bash
# ❌ Tidak memberi informasi
git commit -m "update"
git commit -m "fix"
git commit -m "perubahan"
# ✅ Menjelaskan APA dan KENAPA
git commit -m "Perbaiki crash saat memuat polling tanpa gambar"
git commit -m "Tambah validasi minimal 2 opsi pada form polling"
git commit -m "Ganti ListView dengan ListView.builder di daftar polling"Untuk perubahan yang butuh penjelasan lebih:
bash
git commitLalu di editor:
Perbaiki kebocoran memori pada layar chat
TextEditingController dan ScrollController tidak di-dispose,
sehingga setiap kali layar chat dibuka dan ditutup, memori
bertambah sekitar 2 MB.
Ditemukan lewat tab Memory di DevTools setelah laporan
pengguna bahwa aplikasi melambat setelah dipakai lama.Konvensi yang banyak dipakai adalah Conventional Commits:
bash
feat: tambah fitur polling unggulan
fix: perbaiki crash saat gambar gagal dimuat
docs: perbarui README dengan cara instalasi
refactor: pisahkan logika polling ke repository
test: tambah pengujian untuk PollCubit
chore: perbarui dependensi Firebase
perf: ganti ListView dengan ListView.builderSelain rapi, format ini memungkinkan pembuatan changelog otomatis.
Branch
Branch memungkinkan mengerjakan sesuatu tanpa mengganggu kode yang sudah berjalan.
bash
# Membuat dan berpindah sekaligus
git switch -c fitur/polling-unggulan
# Berpindah
git switch main
# Melihat daftar
git branch -a
# Menghapus setelah digabung
git branch -d fitur/polling-unggulanswitch dan restore menggantikan checkout
git checkout melakukan dua hal yang berbeda — berpindah branch dan memulihkan berkas — yang membingungkan. Git modern memisahkannya:
bash
git switch nama-branch # berpindah branch
git restore lib/main.dart # batalkan perubahan pada berkas
git restore --staged lib/ # keluarkan dari stagingKeduanya lebih jelas maksudnya dan lebih sulit dipakai secara keliru.
Penamaan branch
main ← selalu bisa dirilis
develop ← integrasi (kalau timnya besar)
fitur/nama-fitur ← pengembangan fitur
perbaikan/nama-bug ← perbaikan bug
rilis/1.2.0 ← persiapan rilisMenggabungkan
bash
git switch main
git merge fitur/polling-unggulanKalau ada konflik, Git menandainya di berkas:
dart
<<<<<<< HEAD
final batasPolling = 3;
=======
final batasPolling = 5;
>>>>>>> fitur/polling-unggulanSelesaikan dengan memilih atau menggabungkan keduanya, hapus penanda, lalu:
bash
git add lib/config.dart
git commitGitHub
bash
# Hubungkan ke repositori remote
git remote add origin https://github.com/pengguna/repo.git
git branch -M main
git push -u origin main
# Setelahnya cukup
git push
git pullGitHub CLI
Jauh lebih nyaman daripada bolak-balik ke peramban:
bash
# Pasang, lalu masuk
gh auth login
# Buat repositori dari folder saat ini
gh repo create nama-repo --private --source=. --push
# Buat pull request
gh pr create --title "Tambah polling unggulan" --body "Menutup #12"
# Lihat & checkout PR
gh pr list
gh pr checkout 5Pull request
Meskipun bekerja sendiri, PR berguna: ia memberi tempat untuk meninjau perubahanmu sendiri sebelum digabung, dan menjalankan CI.
bash
git switch -c fitur/notifikasi
# ... kerjakan, commit
git push -u origin fitur/notifikasi
gh pr createAlur kerja tim
Alurnya:
- Buat branch dari
mainuntuk setiap tugas. - Commit sesering mungkin di branch itu.
- Push dan buat pull request.
- Tinjau, perbaiki kalau perlu.
- Gabungkan ke
main. - Hapus branch.
Memperbaiki kesalahan
Bagian yang paling sering dibutuhkan dan paling menakutkan.
bash
# Batalkan perubahan yang belum di-stage
git restore lib/main.dart
# Keluarkan dari staging (perubahan tetap ada)
git restore --staged lib/main.dart
# Ubah pesan commit terakhir
git commit --amend -m "Pesan yang benar"
# Tambahkan berkas yang lupa ke commit terakhir
git add berkas-yang-lupa.dart
git commit --amend --no-edit
# Batalkan commit terakhir, pertahankan perubahannya
git reset --soft HEAD~1
# Batalkan commit terakhir dan buang perubahannya
git reset --hard HEAD~1 # ⚠️ perubahan hilang permanen
# Batalkan commit yang SUDAH di-push (aman)
git revert abc1234reset versus revert
resetmenulis ulang riwayat. Aman untuk commit yang belum di-push.revertmembuat commit baru yang membatalkan commit lama. Ini yang benar untuk commit yang sudah di-push.
Melakukan reset lalu push --force pada branch bersama akan menghapus pekerjaan orang lain. Jangan lakukan itu di main.
Stash: menyimpan sementara
bash
# Simpan perubahan yang sedang dikerjakan
git stash push -m "Setengah jadi: layar profil"
# Lihat daftar
git stash list
# Ambil kembali
git stash pop # ambil dan hapus dari stash
git stash apply # ambil tapi biarkan di stash
# Buang
git stash dropBerguna ketika kamu sedang mengerjakan sesuatu lalu harus segera memperbaiki bug di branch lain.
Menemukan commit yang menyebabkan bug
bash
git bisect start
git bisect bad # commit sekarang bermasalah
git bisect good v1.0 # versi ini masih baik
# Git akan checkout ke commit di tengah.
# Uji aplikasinya, lalu:
git bisect good # atau: git bisect bad
# Ulangi sampai Git menemukan commit penyebabnya
git bisect resetUntuk riwayat 1.000 commit, ini menemukan penyebabnya dalam sekitar 10 langkah.
GitHub Actions untuk Flutter
Otomatiskan pemeriksaan setiap kali kamu push.
yaml
# .github/workflows/flutter.yml
name: Flutter CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
analisis-dan-uji:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.27.x'
channel: stable
cache: true
- name: Pasang dependensi
run: flutter pub get
- name: Periksa format
run: dart format --output=none --set-exit-if-changed .
- name: Analisis statis
run: flutter analyze --fatal-infos
- name: Jalankan pengujian
run: flutter test --coverage
- name: Unggah cakupan
uses: codecov/codecov-action@v4
with:
file: coverage/lcov.info
bangun-android:
needs: analisis-dan-uji
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '17'
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.27.x'
channel: stable
cache: true
- name: Pulihkan berkas rahasia
env:
GOOGLE_SERVICES: ${{ secrets.GOOGLE_SERVICES_JSON }}
run: echo "$GOOGLE_SERVICES" > android/app/google-services.json
- run: flutter pub get
- name: Bangun APK
run: flutter build apk --release --dart-define=STRIPE_PUBLISHABLE_KEY=${{ secrets.STRIPE_PK }}
- uses: actions/upload-artifact@v4
with:
name: apk-rilis
path: build/app/outputs/flutter-apk/app-release.apkBerkas rahasia disimpan di GitHub → Settings → Secrets and variables → Actions, lalu dipulihkan saat build. Ini yang memungkinkan kamu mengabaikan google-services.json di .gitignore tanpa merusak CI.
Menandai rilis
bash
git tag -a v1.2.0 -m "Rilis 1.2.0: polling unggulan & notifikasi"
git push origin v1.2.0
# Buat rilis GitHub sekaligus
gh release create v1.2.0 \
--title "Versi 1.2.0" \
--notes "Fitur baru: polling unggulan, push notification"Tag berguna karena memberi nama pada titik tertentu di riwayat — jauh lebih mudah diingat daripada abc1234.
Hook: pemeriksaan otomatis sebelum commit
bash
# .git/hooks/pre-commit — jangan lupa: chmod +x
#!/bin/sh
echo "Memeriksa format..."
if ! dart format --output=none --set-exit-if-changed .; then
echo "❌ Format tidak sesuai. Jalankan: dart format ."
exit 1
fi
echo "Menjalankan analisis..."
if ! flutter analyze --no-pub; then
echo "❌ Analisis gagal."
exit 1
fi
echo "✅ Semua pemeriksaan lolos."Hook ini mencegah commit yang tidak lolos analisis. Untuk berbaginya dengan tim (hook di .git/ tidak ikut ter-commit), pakai paket lefthook atau husky.
Praktik yang baik
Commit kecil dan sering. Satu commit = satu perubahan logis. Ini membuat git bisect berguna dan revert presisi.
Jangan commit kode yang tidak berjalan ke main. Branch pribadi bebas.
Tarik sebelum mendorong.
bash
git pull --rebase origin main--rebase menghasilkan riwayat linier yang lebih mudah dibaca daripada merge commit yang bertumpuk.
Tinjau sebelum commit.
bash
git diff --stagedSering kali ada print() atau kode eksperimen yang tertinggal.
Jangan commit berkas yang dihasilkan. build/, .dart_tool/, ios/Pods/ bisa dibuat ulang dan hanya memperbesar repositori.
Latihan Mandiri
Kerjakan salah satu, beberapa, atau semuanya secara berurutan untuk melatih pemahamanmu sampai benar-benar lekat.
Variasi 1: Satu Siklus Kerja Penuh — ⭐⭐ · 40–60 menit
Tantangan: Ambil satu proyek Flutter yang sudah kamu punya dan belum berada di Git. Buat repositorinya dengan benar dari nol, lalu jalani satu siklus kerja lengkap: branch, beberapa commit, pull request, dan gabungkan. Ini latihan yang benar-benar dikerjakan, bukan dibaca.
Kriteria selesai:
.gitignoremencakup berkas rahasia yang tidak ada di template bawaan Flutter —key.properties,*.jks,*-adminsdk-*.json,.env.pubspec.lockikut di-commit, dan kamu tahu kenapa (dan kapan sebaliknya).- Ada minimal lima commit dengan pesan yang menjelaskan apa dan kenapa, bukan "update" atau "fix".
- Satu pull request sungguhan di GitHub, dengan deskripsi yang bisa dibaca orang lain, lalu digabungkan.
- Kamu sudah mencoba
git add -psetidaknya sekali untuk memecah satu perubahan besar menjadi dua commit yang terpisah rapi.
Petunjuk: Sebelum commit pertama, luangkan waktu memeriksa git status baris demi baris. Berkas rahasia yang ikut di commit pertama adalah masalah yang jauh lebih mahal daripada terlihat: menghapusnya di commit berikutnya tidak menghilangkannya dari riwayat, dan kalau repositorinya publik, kuncinya harus dianggap sudah bocor — buat yang baru, jangan sekadar hapus. Untuk git add -p, jalankan setelah mengubah dua hal yang tidak berhubungan dalam satu berkas; ia akan menawarkan setiap potongan satu per satu. Terasa aneh dua kali pertama, lalu menjadi kebiasaan.
Variasi 2: Membatalkan dengan Benar — ⭐⭐ · 30–45 menit
Tantangan: Di repositori latihan, buat empat situasi kesalahan dengan sengaja lalu pulihkan masing-masing dengan perintah yang tepat: perubahan yang belum di-stage tetapi ingin dibuang, commit terakhir yang belum di-push dan ingin diubah, commit yang sudah di-push dan harus dibatalkan, dan pekerjaan setengah jadi yang harus ditinggalkan sementara.
Kriteria selesai:
- Keempat situasi dibuat dan dipulihkan, dan kamu mencatat perintah yang dipakai untuk masing-masing.
- Kamu bisa menjelaskan kenapa commit yang sudah di-push dibatalkan dengan
revert, bukanreset. git stashdipakai setidaknya sekali, termasuk mengembalikannya lagi.- Tidak ada
push --forceke branch yang dipakai bersama.
Petunjuk: Perbedaan reset dan revert menjadi jelas begitu kamu memikirkan orang lain: reset menulis ulang riwayat, dan riwayat yang sudah dimiliki orang lain tidak bisa ditulis ulang tanpa merusak salinan mereka. revert membuat commit baru yang membatalkan efek commit lama — riwayatnya tetap utuh dan aman bagi semua orang. Untuk git restore dan git switch: keduanya menggantikan git checkout yang dulu mengerjakan dua hal berbeda dengan nama yang sama.
Variasi 3: Menelusuri Riwayat — ⭐⭐⭐ · 45–60 menit
Tantangan: Ambil repositori dengan riwayat yang cukup panjang — milikmu atau proyek sumber terbuka mana pun — lalu jawab empat pertanyaan hanya dengan perintah Git: kapan sebuah baris terakhir diubah dan oleh siapa, commit mana yang memperkenalkan sebuah bug, berkas apa yang paling sering diubah, dan apa yang berubah antara dua rilis.
Kriteria selesai:
git bisectdipakai untuk menemukan commit penyebab, bukan pemeriksaan satu per satu.git blamedipakai dan kamu bisa membaca keluarannya.- Kamu bisa menghasilkan daftar perubahan antara dua tag.
- Kamu menemukan berkas yang paling sering diubah dan punya dugaan kenapa.
Petunjuk: git bisect menemukan commit penyebab dalam jumlah langkah yang logaritmik — riwayat seribu commit selesai dalam sekitar sepuluh pemeriksaan. Ia terasa seperti sihir pertama kali dipakai, dan syarat satu-satunya adalah kamu bisa menjawab "rusak atau tidak" pada setiap titik. Berkas yang paling sering diubah biasanya menunjukkan sesuatu tentang desain — ia mungkin memegang terlalu banyak tanggung jawab.
Variasi 4: Pemeriksaan Otomatis — ⭐⭐⭐ · 45–60 menit
Tantangan: Siapkan GitHub Actions untuk proyek Flutter-mu yang menjalankan format, analisis, dan pengujian pada setiap pull request. Tambahkan juga hook lokal yang menolak commit kalau formatnya belum rapi.
Kriteria selesai:
- Alur kerja berjalan pada pull request dan hasilnya terlihat di halaman PR.
- Pull request yang gagal pemeriksaan tidak bisa digabungkan.
- Ada satu berkas rahasia yang dipulihkan dari GitHub Secrets saat build, bukan disimpan di repositori.
- Hook lokal berjalan cepat — di bawah beberapa detik — supaya tidak mengganggu.
Petunjuk: Hook yang lambat akan dilewati orang, termasuk dirimu sendiri beberapa minggu lagi. Batasi hook lokal pada pemeriksaan yang cepat seperti format, dan serahkan pengujian penuh ke CI yang berjalan di latar belakang. Untuk berkas rahasia di CI, polanya adalah menyimpan isinya dalam bentuk terkodekan sebagai secret, lalu menuliskannya kembali menjadi berkas di awal build.
Variasi 5: Kerja Bersama — ⭐⭐⭐⭐ · 60–90 menit
Tantangan: Simulasikan kerja tim: kloning repositorimu ke dua folder berbeda dan perlakukan keduanya sebagai dua orang. Buat konflik penggabungan yang sungguhan, selesaikan, lalu jalani satu siklus tinjauan kode lengkap termasuk perubahan yang diminta.
Kriteria selesai:
- Konflik dibuat, dipahami, dan diselesaikan tanpa membuang perubahan salah satu pihak.
- Satu pull request menerima komentar tinjauan, lalu commit perbaikan, lalu digabungkan.
- Branch yang sudah digabungkan dibersihkan di lokal maupun di remote.
- Kamu menulis konvensi tim singkat: penamaan branch, format pesan commit, dan siapa yang boleh menggabungkan.
Petunjuk: Konflik penggabungan menakutkan sampai kamu menyadari Git tidak pernah menebak — ia menandai persis bagian yang tidak bisa ia putuskan sendiri dan menyerahkan keputusannya padamu. Yang membuat konflik terasa buruk biasanya adalah ukurannya, dan ukurannya berbanding lurus dengan berapa lama branch dibiarkan hidup. Branch pendek yang sering digabungkan hampir tidak pernah menghasilkan konflik yang menyakitkan.
Ikhtisar
- Git punya tiga area: working directory, staging, dan repository. Staging yang memungkinkan commit rapi.
.gitignorebawaan Flutter tidak mencakup berkas rahasia yang kamu tambahkan —key.properties,*.jks,*-adminsdk-*.json,.env.- Berkas rahasia yang pernah ter-push ke repositori publik harus dianggap bocor. Buat kunci baru; menghapus dari riwayat tidak cukup.
- Commit
pubspec.lockuntuk aplikasi (bukan untuk pustaka). - Pesan commit menjelaskan apa dan kenapa, bukan "update" atau "fix". Conventional Commits (
feat:,fix:,refactor:) memudahkan changelog. git switchdangit restoremenggantikangit checkoutyang membingungkan.resetuntuk commit yang belum di-push;revertuntuk yang sudah. Jangan pernahpush --forceke branch bersama.git add -pmemisahkan perubahan per potongan — berguna untuk commit yang bersih.git stashuntuk menyimpan pekerjaan setengah jadi saat harus berpindah.git bisectmenemukan commit penyebab bug dalam waktu logaritmik.- GitHub Actions menjalankan format, analisis, dan pengujian otomatis; berkas rahasia disimpan di Secrets dan dipulihkan saat build.
- Tandai rilis dengan tag agar titik penting di riwayat mudah ditemukan.
- Commit kecil dan sering;
git pull --rebasemenjaga riwayat tetap linier.
Berikutnya: Bab 38 — Branding & Rilis.
Transkrip asli
Disintesis dari 4_flutter_ai-chatbot-n-firebase/9_version-control-with-git-and-github.md (4 video: GitHub Desktop Installation and Setup, Creating Repository and Pushing Changes, Cloning Repository and Creating Branches, Merging Branches and Discarding Unwanted Changes). Lihat PDF Firebase & AI.