Version control (GIT)

Version control systems (VCS) adalah alat yang digunakan untuk melacak perubahan pada kode sumber (atau koleksi file dan folder lainnya). Sesuai dengan namanya, alat ini membantu menjaga riwayat perubahan; lebih jauh lagi, alat ini memfasilitasi kolaborasi. VCS melacak perubahan pada folder dan isinya dalam serangkaian snapshot, di mana setiap snapshot merangkum seluruh status file/folder dalam tingkat atas direktori tingkat atas. VCS juga menyimpan metadata seperti siapa yang membuat setiap snapshot, pesan yang terkait dengan setiap snapshot, dan sebagainya.

Mengapa Version Control berguna? Bahkan ketika Anda bekerja sendiri, ini dapat memungkinkan Anda melihat snapshot lama dari sebuah proyek, menyimpan catatan mengapa perubahan tertentu dibuat, bekerja pada cabang pengembangan paralel, dan banyak lagi. Saat bekerja dengan orang lain, ini adalah alat yang sangat berharga untuk melihat apa yang telah diubah oleh orang lain, serta menyelesaikan konflik dalam pengembangan bersamaan.

VCS modern juga memungkinkan Anda dengan mudah (dan sering kali secara otomatis) menjawab pertanyaan seperti:

  • Siapa yang menulis modul ini?
  • Kapan baris tertentu dari file ini diedit? Oleh siapa? Mengapa diedit?
  • Selama 1000 revisi terakhir, kapan/mengapa unit test tertentu berhenti bekerja?

Meskipun ada VCS lain, Git adalah standar de facto untuk Version Control. Komik XKCD ini menangkap reputasi Git:

komik git

Karena Interface Git adalah sebuah abstraksi, mempelajari Git secara top-down (mulai dengan interface / command-line interface) dapat menyebabkan banyak kebingungan. Anda mungkin dapat menghafal beberapa command dan memikirkannya sebagai mantra sihir, dan ikuti pendekatan dalam komik diatas setiap kali terjadi kesalahan.

Meskipun Git memang memiliki interface yang jelek, desain dan ide yang mendasarinya indah. " Meskipun interface yang jelek harus dihafal, desain yang bagus dapat dipahami." Untuk alasan ini, kami memberikan penjelasan dari bawah ke atas tentang Git, dimulai dengan model datanya dan kemudian mencakup command-line interface. Setelah model data dipahami, perintah-perintahnya dapat dipahami dengan lebih baik dalam hal bagaimana mereka memanipulasi model data yang mendasarinya.

Git’s data model

Ada banyak pendekatan ad-hoc yang bisa Anda lakukan untuk Version Control. Git memiliki model yang dipikirkan dengan matang yang memungkinkan semua fitur bagus dari Version Control, seperti mempertahankan riwayat, mendukung cabang, dan memungkinkan kolaborasi.

Snapshots

Git memodelkan riwayat kumpulan file dan folder di dalam beberapa direktori tingkat atas sebagai serangkaian snapshot. Dalam terminologi Git, sebuah file adalah disebut “blob”, dan itu hanya sekumpulan byte. Sebuah direktori disebut “tree”, dan memetakan nama ke blob atau tree (sehingga direktori dapat berisi direktori lain). Sebuah snapshot adalah tree tingkat atas yang sedang dilacak. Untuk Sebagai contoh, kita mungkin memiliki tree sebagai berikut:

<root> (tree)
  |
  +- foo (tree)
  |  |
  |  + bar.txt (blob, contents = "hello world")
  |
  +- baz.txt (blob, contents = "git is wonderful")

Tree tingkat teratas berisi dua elemen, sebuah tree “foo” (yang berisi satu elemen, sebuah gumpalan “bar.txt”), dan sebuah gumpalan “baz.txt”.

Modeling history: relating snapshots

Bagaimana seharusnya sistem Version Control menghubungkan snapshot? Salah satu model sederhana adalah memiliki riwayat linier. Riwayat akan menjadi daftar snapshot dalam urutan waktu. Karena berbagai alasan, Git tidak menggunakan model sederhana seperti ini.

Di Git, riwayat adalah Directed Acyclic Graph (DAG) dari snapshot. Itu mungkin terdengar seperti kata matematika yang rumit, tetapi jangan terintimidasi. Yang dimaksud dengan ini adalah bahwa setiap snapshot di Git mengacu pada satu set “parent”, snapshot yang mendahuluinya itu. Ini adalah sekumpulan parent, bukan single parent (seperti yang terjadi pada linier history) karena sebuah snapshot mungkin diturunkan dari beberapa parent, misalnya, karena menggabungkan (merging) dua cabang pengembangan paralel.

Git menyebut snapshot ini sebagai “commit”. Memvisualisasikan riwayat commit mungkin terlihat terlihat seperti ini:

o <-- o <-- o <-- o
              ^
              |
              --- o <-- o

Dalam contoh ASCII di atas, o berhubungan dengan commit individual (snapshot). Panah menunjuk ke induk dari setiap commit (ini adalah relasi “datang sebelum”, bukan “datang setelah”). Setelah commit ketiga, riwayat bercabang menjadi dua cabang yang terpisah. Hal ini mungkin berhubungan dengan, misalnya, dua fitur yang terpisah dikembangkan secara paralel, secara independen satu sama lain. Di masa depan, cabang-cabang ini dapat digabungkan untuk membuat snapshot baru yang menggabungkan kedua fitur, menghasilkan riwayat baru yang terlihat seperti ini, dengan commit penggabungan yang baru dibuat ditampilkan dalam huruf tebal:

o <-- o <-- o <-- o <---- o
              ^            /
              |          v
              --- o <-- o

Commit di Git tidak dapat diubah. Ini tidak berarti bahwa kesalahan tidak dapat dikoreksi; hanya saja “edits” pada riwayat Commit sebenarnya membuat Commit yang benar-benar baru, dan references (lihat di bawah) diperbarui ke titik ke yang baru.

Data model, sebagai pseudocode

Mungkin akan sangat membantu jika melihat model data Git dituliskan dalam bentuk pseudocode:

// a file is a bunch of bytes
  type blob = array<byte>

  // a directory contains named files and directories
  type tree = map<string, tree | blob>

  // a commit has parents, metadata, and the top-level tree
  type commit = struct {
      parents: array<commit>
      author: string
      message: string
      snapshot: tree
  }

Ini adalah model sejarah yang bersih dan sederhana.

Objects and content-addressing

Sebuah “object” adalah sebuah blob, tree, atau commit:

type object = blob | tree | commit

Dalam penyimpanan data Git, semua objects dialamatkan dengan content-addressed SHA-1 hash.

objects = map<string, object>

  def store(object):
      id = sha1(object)
      objects[id] = object

  def load(id):
      return objects[id]

Blob, tree, dan commits disatukan dengan cara ini: semuanya adalah objects. Ketika mereka mereferenceskan objects lain, mereka tidak benar-benar mengandung objects tersebut dalam representasi on-disk mereka, tetapi memiliki references ke mereka dengan hash mereka.

Sebagai contoh, tree untuk contoh struktur direktori di atas (divisualisasikan dengan menggunakan git cat-file -p 698281bc680d1995c5f4caaf3359721a5a58d48d), terlihat seperti ini:

100644 blob 4448adbf7ecd394f42ae135bbeed9676e894af85    baz.txt
  040000 tree c68d233a33c5c06e0340e4c224f0afca87c8ce87    foo

Tree itu sendiri berisi penunjuk ke isinya, baz.txt (blob) dan foo (sebuah tree). Jika kita melihat konten yang dialamatkan oleh hash yang berhubungan dengan baz.txt dengan git cat-file -p 4448adbf7ecd394f42ae135bbeed9676e894af85, kita mendapatkan berikut ini:

git is wonderful

References

Sekarang, semua snapshot dapat diidentifikasi dengan hash SHA-1-nya. Ini merepotkan, karena manusia tidak mudah mengingat string yang terdiri dari 40 karakter heksadesimal.

Solusi Git untuk masalah ini adalah nama yang mudah dibaca manusia untuk hash SHA-1, yang disebut “references”. references adalah penunjuk untuk melakukan commit. Tidak seperti objects, yang tidak dapat diubah, references dapat diubah (dapat diperbarui untuk menunjuk ke commit baru). Sebagai contoh, references master biasanya menunjuk ke commit terbaru di main branch of development.

references = map<string, string>

  def update_reference(name, id):
      references[name] = id

  def read_reference(name):
      return references[name]

  def load_reference(name_or_id):
      if name_or_id in references:
          return load(references[name_or_id])
      else:
          return load(name_or_id)

Dengan ini, Git dapat menggunakan nama yang dapat dibaca manusia seperti “master” untuk merujuk ke snapshot tertentu dalam history, bukannya string heksadesimal yang panjang.

Salah satu detailnya adalah bahwa kita sering menginginkan gagasan tentang “di mana kita saat ini berada” di history, sehingga ketika kita mengambil snapshot baru, kita tahu apa yang relatif terhadapnya (bagaimana kita mengatur field parents pada commit). Di Git, “di mana kita saat ini” adalah references khusus yang disebut ‘HEAD’.

Repositories

Terakhir, kita dapat mendefinisikan apa yang (secara kasar) merupakan repositori Git: ini adalah data objects dan references.

Di dalam disk, semua simpanan Git adalah objects dan references: hanya itu yang ada di dalam Git's Data Model. Semua perintah git memetakan ke beberapa manipulasi commit DAG dengan menambahkan objects dan menambah/memperbaharui references.

Setiap kali Anda mengetikkan perintah apa pun, pikirkan tentang manipulasi apa yang yang dibuat oleh perintah tersebut pada struktur data grafik yang mendasarinya. Sebaliknya, jika Anda mencoba untuk membuat perubahan tertentu pada commit DAG, misalnya "discard uncommitted changes and make the 'master' ref point to commit 5d83f9e", mungkin ada perintah untuk melakukannya (misalnya, dalam kasus ini, git checkout master; git reset --hard 5d83f9e).

Staging Area

Ini adalah konsep lain yang orthogonal dengan Data Model, tetapi merupakan bagian dari interface untuk membuat commit.

Salah satu cara yang dapat Anda bayangkan untuk mengimplementasikan snapshot seperti yang dijelaskan di atas adalah dengan memiliki perintah "create snapshot” yang membuat snapshot baru berdasarkan state saat ini dari direktori kerja. Beberapa alat Version Control bekerja seperti ini, tetapi tidak dengan Git. Kita menginginkan snapshot yang bersih, dan mungkin tidak selalu ideal untuk membuat snapshot dari kondisi saat ini. Sebagai contoh, bayangkan sebuah skenario di mana Anda mengimplementasikan dua fitur terpisah, dan Anda ingin membuat dua commit terpisah, di mana commit pertama memperkenalkan fitur pertama, dan commit berikutnya memperkenalkan fitur kedua. Atau bayangkan sebuah skenario di mana Anda telah men-debug print statement yang ditambahkan ke seluruh kode Anda, bersama dengan bugfix; Anda ingin mengcommit bugfix sambil membuang semua print statement.

Git mengakomodasi skenario seperti itu dengan mengizinkan Anda untuk menentukan modifikasi mana yang yang harus disertakan dalam snapshot berikutnya melalui mekanisme yang disebut “Staging Area”.

Git command-line interface

Untuk menghindari duplikasi informasi, kami tidak akan menjelaskan perintah-perintah di bawah ini di bawah ini secara mendetail. Lihat Pro Git yang sangat direkomendasikan untuk informasi lebih lanjut, atau tonton video lecture diatas.

Basics

  • git help <command>: mendapatkan bantuan untuk git command
  • git init: membuat repo git baru, dengan data yang disimpan di direktori .git
  • git status: memberi tahu kamu apa yang sedang terjadi
  • git add <filename>: menambahkan berkas ke staging area
  • git commit: membuat commit baru
  • git log: menampilkan riwayat log yang diratakan
  • git log --all --graph --decorate: memvisualisasikan riwayat sebagai sebuah DAG
  • git diff <filename>: menampilkan perubahan yang kamu buat relatif terhadap staging area
  • git diff <revision> <filename>: menampilkan perbedaan dalam sebuah file di antara snapshot
  • git checkout <revision>: memperbarui HEAD dan current branch

Branching and merging

  • git branch: menampilkan cabang
  • git branch <name>: membuat sebuah cabang
  • git checkout -b <name>: membuat sebuah cabang dan beralih ke cabang tersebut
    • sama seperti git branch <name>; git checkout <name>
  • git merge <revision>: merge ke cabang saat ini
  • git mergetool: menggunakan alat canggih untuk membantu menyelesaikan konflik merging
  • git rebase: merebase kumpulan patch ke dalam new base

Remotes

  • git remote: membuat daftar remote
  • git remote add <name> <url>: menambahkan remote
  • git push <remote> <local branch>:<branch remote>: mengirim objek ke remote, dan memperbarui referensi remote
  • git branch --set-upstream-to=<remote>/<remote branch>: mengatur korespondensi antara local branch dan remote
  • git fetch: mengambil objects/references dari remote
  • git pull: sama seperti git fetch; git merge
  • git clone: mengunduh repositori secara remote

Undo

  • git commit --amend: mengedit isi/pesan commit
  • git reset HEAD <file>: unstage sebuah file
  • git checkout -- <file>: membuang perubahan

Advanced Git

  • git config: Git sangat mudah dikustomisasi
  • git clone --depth=1: klon yang dangkal, tanpa seluruh riwayat versi
  • git add -p: interaktif staging
  • git rebase -i: interaktif rebasing
  • git blame: menunjukkan siapa yang terakhir mengedit baris mana
  • git stash: menghapus sementara modifikasi pada working directory
  • git bisect: binary search history (misalnya untuk regresi)
  • .gitignore: tentukan berkas yang sengaja tidak dilacak untuk diabaikan

Miscellaneous

  • GUI: ada banyak GUI Client di luar sana untuk Git. Kami secara pribadi tidak menggunakannya dan menggunakan command-line interface sebagai gantinya.
  • Shell integration: sangat berguna untuk memiliki status Git sebagai bagian dari shell prompt (zsh, bash). Sering disertakan dalam frameworks seperti Oh My Zsh.
  • Frameworks: mirip dengan yang di atas, integrasi yang praktis dengan banyak fitur. fugitive.vim adalah standar untuk Vim.
  • Workflows: kami telah mengajarkan model data ditambah beberapa basic command; kami tidak memberi tahu anda best practice apa yang harus diikuti ketika anda mengerjakan proyek besar (dan ada banyak pendekatan berbeda).
  • GitHub: Git bukanlah GitHub. GitHub memiliki cara khusus untuk menyumbangkan kode ke proyek lain, yang disebut pull request.
  • Penyedia Git lainnya: GitHub tidak istimewa: ada banyak repositori Git lainnya, seperti GitLab dan BitBucket.

Resources

  • Pro Git adalah bacaan yang sangat direkomendasikan. Dengan mempelajari Bab 1-5, Anda akan mempelajari sebagian besar hal yang Anda perlukan untuk menggunakan Git dengan mahir, setelah Anda memahami model data. Bab-bab selanjutnya memiliki beberapa materi yang menarik dan materi lanjutan.
  • Oh shit, Git!?! adalah panduan singkat tentang cara memulihkan dari beberapa kesalahan umum Git.
  • Git for Computer Scientists adalah penjelasan singkat tentang Git's Data Model, dengan sedikit pseudocode dan lebih banyak diagram yang lebih mewah daripada catatan lecture ini.
  • Git from the Bottom Up adalah penjelasan rinci tentang detail implementasi Git di luar data model, untuk yang penasaran.
  • How to explain git in simple words
  • Learn Git Branching adalah game berbasis browser yang mengajarkan Anda Git.

Exercises

  1. Jika Anda tidak memiliki pengalaman sebelumnya dengan Git, cobalah membaca beberapa bab awal dari Pro Git atau ikuti tutorial seperti Learn Git Branching. Saat Anda mempelajarinya, hubungkan Git Commands dengan Data Model.
  2. Cloning repositori untuk web kelas ini.
    1. Jelajahi Version History dengan memvisualisasikannya sebagai grafik.
    2. Siapa orang terakhir yang memodifikasi README.md? (Petunjuk: gunakan git log dengan sebuah argumen).
    3. Apa pesan commit yang terkait dengan modifikasi terakhir pada baris collections: dari _config.yml? (Petunjuk: gunakan git blame dan git show).
  3. Salah satu kesalahan umum saat mempelajari Git adalah mengcommit file besar yang seharusnya tidak dikelola oleh Git atau menambahkan informasi sensitif. Coba tambahkan sebuah file ke repositori, melakukan beberapa commit, lalu menghapus berkas tersebut dari riwayat (Anda mungkin ingin melihat ini).
  4. Kloning beberapa repositori dari GitHub, dan modifikasi salah satu file yang ada. Apa yang terjadi ketika Anda melakukan git stash? Apa yang Anda lihat ketika menjalankan git log --all --online? Jalankan git stash pop untuk membatalkan apa yang Anda lakukan dengan git stash. Dalam skenario apa hal ini mungkin berguna?
  5. Seperti kebanyakan command-line tools, Git menyediakan berkas konfigurasi (atau dotfile) yang disebut ~/.gitconfig. Buat sebuah alias di ~/.gitconfig agar ketika kamu menjalankan git graph, kamu akan mendapatkan output dari git log --all --graph --decorate --oneline. Kamu dapat melakukan ini dengan langsung mengedit berkas ~/.gitconfig, atau kamu bisa menggunakan perintah git config untuk menambahkan alias. Informasi tentang alias git dapat ditemukan di sini.
  6. Kamu bisa mendefinisikan global ignore patterns di ~/.gitignore_global setelah menjalankan git config --global core.excludesfile ~/.gitignore_global. Lakukan ini, dan atur file gitignore global kamu untuk mengabaikan file khusus OS atau editor file sementara, seperti .DS_Store.
  7. Fork repositori untuk kelas ini, temukan kesalahan ketik atau perbaikan lain yang dapat Anda lakukan, dan kirimkan pull request di GitHub (Anda mungkin ingin melihat ini). Mohon hanya kirimkan PR yang berguna (jangan kirimkan spam kepada kami!). Jika Anda tidak dapat menemukan perbaikan yang bisa dilakukan, Anda bisa melewatkan latihan ini.

Contributors

Nama Kelas Kampus
Avatar One Piece
Ariel Gema Wardana
Indonesia
Pemrograman Web 1
IT201
Universitas Siber Asia
Avatar One Piece
Rasya Radja Fadillah
Indonesia
Pemrograman Web 1
IT201
Universitas Siber Asia