Stash
Git stash menyimpan perubahan direktori kerja dan index agar Anda dapat beralih ke tugas lain, lalu memulihkannya nanti. Pitmaster mengimplementasikan stash menggunakan refs/stash dan reflog, sesuai tata letak penyimpanan git. Repository tidak memiliki metode stash; gunakan class Stash di bawah.
Dua masalah yang diketahui: jika ada lebih dari satu entri, drop() (dan karena itu pop()) membiarkan entri tersebut tetap dalam daftar (#10), dan apply() menimpa perubahan yang belum di-commit pada file yang tidak disentuh stash (#11). Terapkan stash pada working tree yang bersih.
Cara kerja stash
Setiap entri stash adalah commit dengan dua parent:
- Parent 0: commit HEAD saat stash dibuat (keadaan dasar)
- Parent 1: commit yang merekam keadaan index (perubahan yang sudah di-stage)
Tree milik commit stash merekam keadaan working tree (termasuk perubahan yang belum di-stage).
"Stack" stash adalah reflog dari refs/stash. Stash terbaru adalah refs/stash itu sendiri. Entri yang lebih lama adalah entri reflog: stash@{0}, stash@{1}, dan seterusnya.
refs/stash -> stash commit (worktree state)
parent[0] -> HEAD at time of stash
parent[1] -> index state commit
parent[0] -> HEAD at time of stash
Membuat objek Stash
use Pitmaster\Stash\Stash;
$stash = new Stash(
$repo->objectDatabase(),
$repo->refDatabase(),
$repo->gitDir(),
$repo->workDir(),
);
Push (simpan perubahan)
// public function push(string $message = '', bool $includeUntracked = false): ObjectId
$stashId = $stash->push('Work in progress on login');
echo "Saved: {$stashId->hex}\n";
// Also stash untracked files
$stashId = $stash->push('With new files', includeUntracked: true);
Push melakukan hal berikut:
- Membaca commit HEAD saat ini
- Membuat tree dari indeks saat ini (status staged)
- Membuat "commit indeks" dengan HEAD sebagai induk
- Membuat tree dari direktori kerja saat ini (termasuk perubahan yang belum di-stage)
- Membuat commit stash (tree worktree, induk = HEAD + commit indeks)
- Memperbarui
refs/stashagar menunjuk ke commit stash - Menambahkan entri reflog untuk stack
- Mengatur ulang tree kerja dan indeks ke HEAD
Setelah push, file yang dilacak cocok dengan HEAD. File yang tidak dilacak tetap ada kecuali includeUntracked disetel; jika demikian, file tersebut dihapus dan disimpan di tree milik commit stash (git menyimpannya sebagai induk ketiga, sehingga git melihatnya sebagai perubahan yang dilacak). push() membuat entri meskipun tidak ada perubahan, saat git melaporkan "No local changes to save".
Dengan pesan default
$stashId = $stash->push();
// Message: "WIP on <branch>: <short hash> <HEAD subject>"
// With a message: "On <branch>: <message>"
Pada HEAD yang terlepas, <branch> adalah HEAD.
Pop (terapkan dan hapus)
$stash->pop();
Menerapkan entri stash teratas ke direktori kerja dan menghapusnya dari stack. Setara dengan apply() diikuti drop().
Pop entri tertentu
$stash->pop(1); // Pop stash@{1}
Apply (pulihkan tanpa menghapus)
$stash->apply(); // Apply stash@{0}
$stash->apply(2); // Apply stash@{2}
Apply menggabungkan stash ke file HEAD saat ini satu per satu (basis stash, HEAD saat ini, tree stash). Entri stash tetap berada di stack, dan indeks tidak disentuh. Konflik meninggalkan penanda berlabel Updated upstream dan Stashed changes di dalam file serta melempar Pitmaster\Exceptions\MergeConflictException; pop() kemudian mempertahankan entri tersebut. Apply tidak memeriksa perubahan lokal terlebih dahulu (lihat catatan di bagian atas).
Daftar entri
$entries = $stash->listEntries();
foreach ($entries as $entry) {
echo "stash@{{$entry['index']}}: {$entry['message']}\n";
// stash@{0}: On main: Work in progress on login
// stash@{1}: WIP on main: f707601 Add login form
}
Setiap entri berisi:
index: posisi stash (0 = terbaru)message: pesan stashhash: hash commit stash
Drop (hapus tanpa menerapkan)
$stash->drop(); // Drop stash@{0}
$stash->drop(1); // Drop stash@{1}
Jika satu-satunya entri dihapus, refs/stash dan reflog-nya juga dihapus. Jika ada lebih banyak entri, drop() saat ini hanya memindahkan refs/stash dan membiarkan reflog tetap sama, sehingga entri tetap berada di listEntries() (#10).
Detail penyimpanan
Struktur commit stash
commit <hash>
tree <worktree-tree-hash>
parent <HEAD-hash>
parent <index-commit-hash>
author <current identity>
committer <current identity>
<stash message>
Struktur commit indeks
commit <hash>
tree <index-tree-hash>
parent <HEAD-hash>
author <current identity>
committer <current identity>
index on <branch>: <short hash> <HEAD subject>
Identitas diambil dari GIT_COMMITTER_NAME, lalu GIT_AUTHOR_NAME, kemudian user.name dalam konfigurasi repositori, lalu Pitmaster (urutan yang sama berlaku untuk email, yang diakhiri dengan pitmaster@example.invalid). Tanggal diambil dari GIT_COMMITTER_DATE, lalu GIT_AUTHOR_DATE, kemudian waktu saat ini. Tidak seperti commit, stash tidak membaca konstanta PITMASTER_*.
Penyimpanan Reflog
Entri stash disimpan di .git/logs/refs/stash:
0000000000000000000000000000000000000000 abc123... Name <email> 1700000000 +0000 WIP on main: f707601 Add login form
abc123... def456... Name <email> 1700000001 +0000 On main: Work in progress on login
Reflog dibaca dalam urutan kronologis. Daftar stash membalik urutannya (terbaru lebih dulu). Reflog stash ditulis terlepas dari core.logallrefupdates.
Penggunaan CLI
# Save current changes
./vendor/bin/pitmaster stash
./vendor/bin/pitmaster stash push
./vendor/bin/pitmaster stash push "Work in progress on login"
# Apply and remove top entry
./vendor/bin/pitmaster stash pop
# Apply without removing
./vendor/bin/pitmaster stash apply
# List all entries
./vendor/bin/pitmaster stash list
# Remove top entry without applying
./vendor/bin/pitmaster stash drop
Pesan adalah argumen setelah push (atau save); -m tidak didukung. pop, apply, dan drop selalu bekerja pada stash@{0}; CLI tidak memiliki --include-untracked.
Batasan
- Hanya stash seluruh direktori kerja (tanpa
--keep-index); file yang tidak dilacak hanya melaluipush($message, includeUntracked: true) - Tidak ada mode
--patch(pemilihan hunk interaktif) - Pop/apply memulihkan status tree kerja; indeks tidak dipulihkan. Edit yang di-stage kembali sebagai edit yang belum di-stage, dan file yang baru di-stage kembali sebagai file yang tidak dilacak
stash@{n}tidak diterima olehRepository::resolve()- Stash di worktree tertaut menggunakan konfigurasi dan lokasi reflog yang salah (#14)