Praktis menggunakan git reset --soft?

136

Saya telah bekerja dengan git selama lebih dari sebulan. Memang saya telah menggunakan reset untuk pertama kalinya hanya kemarin, tetapi soft reset masih tidak masuk akal bagi saya.

Saya mengerti saya bisa menggunakan soft reset untuk mengedit komit tanpa mengubah indeks atau direktori kerja, seperti yang saya lakukan dengan git commit --amend.

Apakah kedua perintah ini benar-benar sama ( reset --softvs commit --amend)? Adakah alasan untuk menggunakan satu atau yang lain dalam istilah praktis? Dan yang lebih penting, apakah ada kegunaan lain reset --softselain mengubah komit?

AJJ
sumber

Jawaban:

110

git resetadalah tentang bergerak HEAD, dan umumnya ref cabang .
Pertanyaan: bagaimana dengan pohon dan indeks kerja?
Ketika dipekerjakan dengan --soft, bergerak HEAD, paling sering memperbarui ref cabang, dan hanyaHEAD .
Ini berbeda commit --amenddengan:

  • itu tidak membuat komit baru.
  • itu sebenarnya dapat memindahkan HEAD ke komit apa pun (karena commit --amendhanya tentang tidak memindahkan HEAD, sambil mengizinkan untuk mengulang komit saat ini)

Baru saja menemukan contoh penggabungan ini:

  • gabungan klasik
  • gabungan subtree

semua menjadi satu (gurita, karena ada lebih dari dua cabang digabung) melakukan penggabungan.

Tomas "dulu." Carnecky menjelaskan dalam artikelnya "Subtree Octopus merge" :

  • Strategi penggabungan subtree dapat digunakan jika Anda ingin menggabungkan satu proyek menjadi subdirektori dari proyek lain, dan selanjutnya menjaga agar proyek tetap terbaru. Ini adalah alternatif untuk git submodules.
  • Strategi penggabungan gurita dapat digunakan untuk menggabungkan tiga atau lebih cabang. Strategi normal hanya dapat menggabungkan dua cabang dan jika Anda mencoba untuk menggabungkan lebih dari itu, git secara otomatis kembali ke strategi gurita.

Masalahnya adalah Anda hanya dapat memilih satu strategi. Tapi saya ingin menggabungkan keduanya untuk mendapatkan sejarah bersih di mana seluruh repositori diperbarui secara atom ke versi baru.

Saya punya proyek super, sebut saja projectA, dan sub proyek projectB,, yang saya gabungkan menjadi subdirektori dari projectA.

(Itulah bagian menggabungkan subtree)

Saya juga mempertahankan beberapa komitmen lokal.
ProjectAsecara teratur diperbarui, projectBmemiliki versi baru setiap beberapa hari atau minggu dan biasanya tergantung pada versi tertentu projectA.

Ketika saya memutuskan untuk memperbarui kedua proyek, saya tidak hanya menarik dari projectAdan projectB karena itu akan membuat dua komitmen untuk apa yang seharusnya menjadi pembaruan atom dari seluruh proyek .
Sebagai gantinya, saya membuat satu komit gabungan yang menggabungkan projectA, projectBdan komit lokal saya .
Bagian yang sulit di sini adalah bahwa ini adalah gabungan gurita (tiga kepala), tetapi projectBperlu digabungkan dengan strategi subtree . Jadi inilah yang saya lakukan:

# Merge projectA with the default strategy:
git merge projectA/master

# Merge projectB with the subtree strategy:
git merge -s subtree projectB/master

Di sini penulis menggunakan reset --hard, dan kemudian read-treeuntuk mengembalikan apa yang telah dilakukan oleh dua penggabungan pertama ke pohon kerja dan indeks, tetapi di situlah reset --softdapat membantu:
Bagaimana saya mengulangi dua penggabungan , yang telah bekerja, yaitu pohon kerja dan indeks saya adalah baik-baik saja, tetapi tanpa harus merekam kedua komitmen tersebut

# Move the HEAD, and just the HEAD, two commits back!
git reset --soft HEAD@{2}

Sekarang, kita dapat melanjutkan solusi Tomas:

# Pretend that we just did an octopus merge with three heads:
echo $(git rev-parse projectA/master) > .git/MERGE_HEAD
echo $(git rev-parse projectB/master) >> .git/MERGE_HEAD

# And finally do the commit:
git commit

Jadi, setiap kali:

  • Anda puas dengan apa yang Anda dapatkan (dalam hal pohon dan indeks kerja)
  • Anda tidak puas dengan semua komitmen yang membawa Anda ke sana:

git reset --soft adalah jawabannya.

VONC
sumber
8
Catatan untuk diri sendiri: contoh sederhana dari squashing dengan git reset --soft: stackoverflow.com/questions/6869705/…
VonC
3
ini juga berguna jika Anda berkomitmen pada cabang yang salah. semua perubahan kembali ke area pementasan dan bergerak bersama Anda saat Anda checkout cabang yang tepat.
smo Seseorang
44

Use Case - Gabungkan serangkaian commit lokal

"Ups. Tiga komitmen itu bisa jadi hanya satu."

Jadi, batalkan komit 3 terakhir (atau apa pun) berkomitmen (tanpa mempengaruhi indeks atau direktori kerja). Kemudian komit semua perubahan sebagai satu.

Misalnya

> git add -A; git commit -m "Start here."
> git add -A; git commit -m "One"
> git add -A; git commit -m "Two"
> git add -A' git commit -m "Three"
> git log --oneline --graph -4 --decorate

> * da883dc (HEAD, master) Three
> * 92d3eb7 Two
> * c6e82d3 One
> * e1e8042 Start here.

> git reset --soft HEAD~3
> git log --oneline --graph -1 --decorate

> * e1e8042 Start here.

Sekarang semua perubahan Anda dipertahankan dan siap untuk dilakukan sebagai satu.

Jawaban singkat untuk pertanyaan Anda

Apakah kedua perintah ini benar-benar sama ( reset --softvs commit --amend)?

  • Tidak.

Adakah alasan untuk menggunakan satu atau yang lain dalam istilah praktis?

  • commit --amend untuk menambah / rm file dari komit terakhir atau untuk mengubah pesannya.
  • reset --soft <commit> untuk menggabungkan beberapa komit berurutan menjadi yang baru.

Dan yang lebih penting, apakah ada kegunaan lain reset --softselain mengubah komit?

  • Lihat jawaban lain :)
Shaun Luttin
sumber
2
Lihat jawaban lain untuk "apakah ada kegunaan lain reset --softselain dari mengubah komit - Tidak"
Antony Hatchkins
Menanggapi komentar terakhir - sebagian setuju, tapi saya akan mengatakan bahwa mengubah satu komit terlalu spesifik. Misalnya, Anda mungkin ingin, begitu fitur ditulis, hancurkan semuanya dan buat komitmen baru yang lebih berguna bagi pengulas. Saya akan mengatakan bahwa soft reset (termasuk dataran git reset) baik ketika Anda (a) ingin menulis ulang sejarah, (b) tidak peduli dengan komitmen lama (sehingga Anda dapat menghindari kerepotan rebase interaktif), dan (c) memiliki lebih dari satu komitmen untuk berubah (jika tidak, commit --amendlebih sederhana).
johncip
18

Saya menggunakannya untuk mengubah lebih dari sekedar komit terakhir .

Katakanlah saya membuat kesalahan dalam komit A dan kemudian membuat komit B. Sekarang saya hanya dapat mengubah B. Jadi saya lakukan git reset --soft HEAD^^, saya memperbaiki dan komit ulang A dan kemudian komit kembali B.

Tentu saja, itu tidak terlalu nyaman untuk komit besar ... tetapi Anda tidak boleh melakukan komit besar ;-)

Simon
sumber
3
git commit --fixup HEAD^^ git rebase --autosquash HEAD~Xberfungsi dengan baik juga.
Niklas
3
git rebase --interactive HEAD^^di mana Anda memilih untuk mengedit komit A dan B. Dengan cara ini menyimpan pesan komit dari A dan B, jika diperlukan Anda masih dapat memodifikasinya juga.
Paul Pladijs
2
Bagaimana saya komit ulang B setelah saya mengatur ulang ke A?
northben
1
Cara lain yang mungkin kurang berhasil: periksa git log Anda, dapatkan komit hash B, lalu git reset A, buat dan tambahkan perubahan git commit --amend,, git cherry-pick <B-commit-hash>.
naught101
15

Penggunaan potensial lainnya adalah sebagai alternatif untuk menyembunyikan (yang beberapa orang tidak suka, lihat misalnya https://codingkilledthecat.wordpress.com/2012/04/27/git-stash-pop-considered-harmful/ ).

Sebagai contoh, jika saya sedang mengerjakan cabang dan perlu memperbaiki sesuatu dengan segera pada master, saya bisa melakukan:

git commit -am "In progress."

kemudian checkout master dan lakukan perbaikan. Setelah selesai, saya kembali ke cabang saya dan melakukannya

git reset --soft HEAD~1

untuk terus bekerja di tempat saya tinggalkan.

deltacrux
sumber
3
Perbarui (sekarang saya mengerti git lebih baik): --softsebenarnya tidak perlu di sini, kecuali Anda benar-benar peduli tentang perubahan akan segera dipentaskan. Saya sekarang hanya menggunakan git reset HEAD~ketika melakukan ini. Jika saya memiliki beberapa perubahan yang dipentaskan ketika saya perlu berganti cabang, dan ingin tetap seperti itu, maka saya lakukan git commit -m "staged changes", kemudian git commit -am "unstaged changes", kemudian git reset HEAD~diikuti oleh git reset --soft HEAD~untuk sepenuhnya memulihkan kondisi kerja. Meskipun, sejujurnya saya melakukan kedua hal ini jauh lebih sedikit sekarang yang saya tahu tentang git-worktree:)
deltacrux
7

Anda dapat menggunakan git reset --softuntuk mengubah versi yang ingin Anda miliki sebagai induk untuk perubahan yang Anda miliki di indeks dan pohon kerja Anda. Kasus-kasus di mana ini berguna jarang terjadi. Kadang-kadang Anda mungkin memutuskan bahwa perubahan yang Anda miliki di pohon kerja Anda harus menjadi milik cabang yang berbeda. Atau Anda dapat menggunakan ini sebagai cara sederhana untuk memecah beberapa commit menjadi satu (mirip dengan squash / fold).

Lihat jawaban ini oleh VonC untuk contoh praktis: Hancurkan dua komit pertama di Git?

Johannes Rudolph
sumber
Itu adalah pertanyaan bagus yang belum pernah saya temukan sebelumnya. Tapi saya tidak yakin bisa melihat bagaimana cara melakukan perubahan ke cabang lain menggunakan reset lunak. Jadi saya punya checkout Cabang, kemudian saya gunakan git reset --soft anotherBranchdan kemudian komit di sana? Tetapi Anda tidak benar-benar mengubah cabang ckeckout, jadi apakah Anda akan berkomitmen ke Cabang atau ke Cabang lain ?
AJJ
Ketika melakukan ini, penting Anda tidak menggunakan checkout git karena itu akan mengubah pohon Anda. Pikirkan git reset --soft sebagai cara memanipulasi revisi HEAD poin.
Johannes Rudolph
7

Salah satu kemungkinan penggunaan adalah ketika Anda ingin melanjutkan pekerjaan Anda pada mesin yang berbeda. Ini akan bekerja seperti ini:

  1. Periksa cabang baru dengan nama simpanan,

    git checkout -b <branchname>_stash
    
  2. Dorong cabang simpanan Anda ke atas,

    git push -u origin <branchname>_stash
    
  3. Beralih ke mesin Anda yang lain.

  4. Tarik simpanan dan cabang yang ada,

    git checkout <branchname>_stash; git checkout <branchname>
    
  5. Anda harus berada di cabang yang ada sekarang. Gabungkan perubahan dari cabang simpanan,

    git merge <branchname>_stash
    
  6. Setel ulang cabang Anda yang ada menjadi 1 sebelum penggabungan,

    git reset --soft HEAD^
    
  7. Hapus cabang simpanan Anda,

    git branch -d <branchname>_stash
    
  8. Hapus juga cabang simpanan Anda dari asal,

    git push origin :<branchname>_stash
    
  9. Terus bekerja dengan perubahan Anda seolah-olah Anda telah menyimpannya secara normal.

Saya pikir, di masa depan, GitHub dan rekan. harus menawarkan fungsionalitas "simpanan jauh" ini dalam beberapa langkah lebih sedikit.

llaughlin
sumber
2
Saya ingin menunjukkan bahwa simpanan pertama dan pop pada mesin pertama Anda benar-benar tidak perlu, Anda bisa membuat cabang baru langsung dari copy pekerjaan yang kotor, komit, dan kemudian mendorong perubahan ke remote.
7

Salah satu penggunaan praktis adalah jika Anda telah berkomitmen pada repo lokal Anda (mis. Git commit -m) maka Anda dapat membalikkan komit terakhir dengan melakukan reset git --soft HEAD ~ 1

Juga untuk pengetahuan Anda, jika Anda telah melakukan perubahan Anda sudah (yaitu dengan git add.) Maka Anda dapat membalikkan pementasan dengan melakukan git reset - dicampur KEPALA atau saya sudah umum juga baru saja digunakangit reset

terakhir, git reset --hard menghapus semuanya termasuk perubahan lokal Anda. Kepala ~ setelah memberi tahu Anda berapa banyak komit untuk pergi dari atas.

j2emanue
sumber
1
git reset --soft HEAD ~1memberi saya, fatal: Cannot do soft reset with paths.saya pikir kita perlu menghapus ruang setelah KEPALA sehinggagit reset --soft HEAD~1
Andrew Lohr
6

Alasan bagus untuk menggunakan ' git reset --soft <sha1>' adalah untuk bergerak HEADdalam repo telanjang.

Jika Anda mencoba menggunakan opsi --mixedatau --hard, Anda akan mendapatkan kesalahan karena Anda mencoba untuk memodifikasi dan bekerja pohon dan / atau indeks yang tidak ada.

Catatan: Anda harus melakukan ini langsung dari repo telanjang.

Catatan Lagi: Anda perlu memastikan cabang yang ingin Anda atur ulang di bare repo adalah cabang aktif. Jika tidak, ikuti jawaban VonC tentang cara memperbarui cabang aktif dalam repo kosong ketika Anda memiliki akses langsung ke repo.

Hazok
sumber
1
Seperti yang disebutkan dalam jawaban Anda sebelumnya ( stackoverflow.com/questions/4624881/… ), ini benar ketika Anda memiliki akses langsung ke repo telanjang (yang Anda sebutkan di sini). +1 sekalipun.
VonC
@VonC Ya, benar sekali dan terima kasih telah menambahkan catatan! Saya terus lupa menambahkan bahwa karena saya berasumsi pengaturan ulang dilakukan langsung dari repo. Juga, saya berasumsi bahwa cabang yang orang ingin atur ulang dalam bare repo adalah cabang aktif mereka. Jika cabang bukan cabang aktif mereka, maka kebutuhan untuk memperbarui per jawaban Anda ( stackoverflow.com/questions/3301956/… ) tentang cara memperbarui cabang aktif untuk repo kosong. Saya akan memperbarui jawabannya dengan info cabang aktif juga. Terima kasih lagi!!!
Hazok
2

SourceTree adalah git GUI yang memiliki antarmuka yang cukup nyaman untuk pementasan bit yang Anda inginkan. Itu tidak memiliki sesuatu yang mirip untuk mengubah revisi yang tepat.

Jadi git reset --soft HEAD~1jauh lebih bermanfaat daripada commit --amenddalam skenario ini. Saya dapat membatalkan komit, mendapatkan semua perubahan kembali ke area pementasan, dan melanjutkan tweak bit yang dipentaskan menggunakan SourceTree.

Sungguh, menurut saya itu commit --amendadalah perintah yang lebih berlebihan dari keduanya, tetapi git adalah git dan tidak menghindar dari perintah serupa yang melakukan hal yang sedikit berbeda.

Roman Starkov
sumber
1

Walaupun saya benar-benar menyukai jawaban di utas ini, saya menggunakan git reset --softskenario yang sedikit berbeda, namun tetap sangat praktis.

Saya menggunakan IDE untuk pengembangan yang memiliki alat diff yang baik untuk menampilkan perubahan (bertahap dan tidak bertahap) setelah komit terakhir saya. Sekarang, sebagian besar tugas saya melibatkan banyak komitmen. Sebagai contoh, katakanlah saya membuat 5 komitmen untuk menyelesaikan tugas tertentu. Saya menggunakan alat diff di IDE selama setiap komit tambahan dari 1-5 untuk melihat perubahan saya dari komit terakhir. Saya merasa ini cara yang sangat membantu untuk meninjau perubahan saya sebelum melakukan.

Tetapi pada akhir tugas saya, ketika saya ingin melihat semua perubahan saya bersama-sama (dari sebelum komit 1), untuk melakukan review kode diri sebelum membuat permintaan tarik, saya hanya akan melihat perubahan dari komit saya sebelumnya (setelah komit 4) dan tidak berubah dari semua komitmen dari tugas saya saat ini.

Jadi saya gunakan git reset --soft HEAD~4untuk kembali 4 komitmen. Ini memungkinkan saya melihat semua perubahan bersama. Ketika saya yakin akan perubahan saya, saya bisa melakukannya git reset HEAD@{1}dan mendorongnya ke jarak jauh dengan percaya diri.

Coder megah
sumber
1
... lalu lakukan git add --patchdan git commitberulang kali untuk membangun seri komit yang telah Anda buat jika Anda tahu apa yang Anda lakukan selama ini. Komit pertama Anda seperti catatan di meja Anda atau draf memo pertama, mereka ada di sana untuk mengatur pemikiran Anda, bukan untuk publikasi.
jthill
Hei, aku tidak mengerti apa yang kamu sarankan.
Swanky Coder
1
Hanya saja alih-alih melakukan git reset @{1}untuk memulihkan seri draf pertama Anda, Anda dapat membangun seri untuk publikasi dari git add -pdan git commit.
jthill
Ya benar. Satu cara lain adalah (yang biasanya saya ikuti) untuk rebase di hulu / master dan squash komit menjadi satu.
Swanky Coder
1

Kasus penggunaan lain adalah ketika Anda ingin mengganti cabang lain dengan Anda dalam permintaan tarik, misalnya, katakanlah Anda memiliki perangkat lunak dengan fitur A, B, C dalam pengembangan.

Anda mengembangkan dengan versi berikutnya dan Anda:

  • Fitur yang dihapus B

  • Fitur yang ditambahkan D

Dalam prosesnya, kembangkan hotfix baru saja ditambahkan untuk fitur B.

Anda dapat menggabungkan pengembangan menjadi yang berikutnya, tetapi itu kadang-kadang bisa berantakan, tetapi Anda juga dapat menggunakan git reset --soft origin/developdan membuat komit dengan perubahan Anda dan cabang dapat digabung tanpa konflik dan menyimpan perubahan Anda.

Ternyata itu git reset --softadalah perintah yang berguna. Saya pribadi banyak menggunakannya untuk menekan komit yang tidak memiliki "pekerjaan selesai" seperti "WIP" jadi ketika saya membuka permintaan tarik, semua komit saya bisa dimengerti.

cnexans
sumber