Menyimpan hanya melakukan perubahan git - apakah mungkin?

367

Apakah ada cara saya bisa menyembunyikan hanya perubahan bertahap saya? Skenario saya mengalami masalah adalah ketika saya telah mengerjakan beberapa bug pada waktu tertentu, dan memiliki beberapa perubahan yang tidak dipentaskan. Saya ingin dapat mem-stage file-file ini satu per satu, membuat file .patch saya, dan menyimpannya hingga kode disetujui. Dengan cara ini, ketika disetujui saya dapat menyimpan seluruh sesi saya saat ini, pop bug itu dan dorong kode tersebut.

Apakah saya salah tentang hal ini? Apakah saya salah paham bagaimana git dapat bekerja dengan cara lain untuk menyederhanakan proses saya?

MrDuk
sumber
Ya, Anda mungkin melakukan kesalahan untuk masuk ke situasi ini. Masih merupakan pertanyaan yang bermanfaat. Anda harus menyimpan atau bercabang sebelum memulai perbaikan selanjutnya. The, tangential, answer stackoverflow.com/a/50692885 mungkin merupakan cara yang lebih baik untuk menangani ini di git. Bermain-main dengan simpanan sering melakukan hal-hal aneh di area kerja saya jika saya menarik komitmen dari hulu.
Samuel Åslund

Jawaban:

472

Ya, Ini mungkin dengan STASH GANDA

  1. Panggung semua file yang harus Anda simpanan.
  2. Lari git stash --keep-index. Perintah ini akan membuat simpanan dengan SEMUA perubahan Anda ( bertahap dan tidak bertahap ), tetapi akan meninggalkan perubahan bertahap di direktori kerja Anda (masih dalam keadaan bertahap).
  3. Lari git stash push -m "good stash"
  4. Sekarang file HANYA milik Anda "good stash"telah dipentaskan .

Sekarang jika Anda memerlukan file yang belum dipentaskan sebelum disimpan, cukup terapkan simpanan pertama ( yang dibuat dengan--keep-index ) dan sekarang Anda dapat menghapus file yang Anda simpan "good stash".

Nikmati

Bartłomiej Semańczyk
sumber
Ini menyembunyikan perubahan dalam submodul meskipun mereka tidak dipentaskan. Apakah ada jalan keluarnya?
rluks
1
ini entah bagaimana meninggalkan semua file baru (bahkan dipentaskan).
Aurimas
8
@Aurimas, untuk menyimpan file baru, Anda perlu menggunakan -usakelar.
Gyromite
2
ketika Anda menerapkan kembali simpanan pertama dan mendapatkan kembali semuanya sementara Anda mungkin hanya tertarik pada git stash apply --indexopsi unstages perubahan Anda gunakan . Ini akan mencoba untuk menjaga status un (bertahap) Anda. Lebih mudah untuk menghapus perubahan yang tidak diinginkan dari pohon yang bekerja sekarang.
otomo
Meskipun saya tidak perlu melakukan persis apa yang dikatakan jawaban ini, mengetahui tentang bendera --keep-index sangat membantu.
Aaron Krauss
128

Dengan git terbaru, Anda dapat menggunakan --patchopsi

git stash push --patch  

git stash save --patch   # for older git versions

Dan git akan meminta Anda untuk setiap perubahan pada file Anda untuk ditambahkan atau tidak menjadi simpanan.
Anda hanya menjawab yataun


Alias UPD untuk DOUBLE STASH :

git config --global alias.stash-staged '!bash -c "git stash --keep-index; git stash push -m "staged" --keep-index; git stash pop stash@{1}"'

Sekarang Anda dapat mengatur file Anda dan kemudian jalankan git stash-staged.
Akibatnya file yang dipentaskan Anda akan disimpan ke simpanan .

Jika Anda tidak ingin menyimpan file yang dipentaskan dan ingin memindahkannya ke simpanan. Kemudian Anda dapat menambahkan alias lain dan menjalankan git move-staged:

git config --global alias.move-staged '!bash -c "git stash-staged;git commit -m "temp"; git stash; git reset --hard HEAD^; git stash pop"'
Eugen Konkov
sumber
17
Secara teknis tidak menjawab pertanyaan - tetapi teknik yang sangat bagus yang mencapai penyimpanan selektif.
alexreardon
6
Setuju, ini tidak apa-apa, tetapi ide di sini dengan pertanyaannya adalah saya telah melakukan semua pekerjaan ini untuk melakukan perubahan yang ingin saya lakukan sesuatu (yang semula untuk melakukan, tetapi sekarang ingin menyimpan), tidak ingin hanya ulangi lagi.
Steven Lu
4
tidak bekerja untuk file yang baru dibuat (hanya bekerja pada file yang dimodifikasi)
Derek Liang
@DerekLiang: File yang baru dibuat tidak dilacak sama sekali. Anda mungkin harus memeriksa -u|--include-untrackedopsigit-stash
Eugen Konkov
2
Dari dokumen : " save : Opsi ini tidak digunakan untuk git stash push . Opsi ini berbeda dengan 'stash push' karena tidak dapat menggunakan pathspecs, dan argumen non-opsi apa pun yang membentuk pesan."
Borjovsky
53

Saya membuat skrip yang menyimpan hanya apa yang saat ini dipentaskan dan meninggalkan yang lainnya. Ini luar biasa ketika saya mulai membuat terlalu banyak perubahan yang tidak terkait. Cukup tahap apa yang tidak terkait dengan komit yang diinginkan dan simpanan saja.

(Terima kasih kepada Bartłomiej untuk titik awalnya)

#!/bin/bash

#Stash everything temporarily.  Keep staged files, discard everything else after stashing.
git stash --keep-index

#Stash everything that remains (only the staged files should remain)  This is the stash we want to keep, so give it a name.
git stash save "$1"

#Apply the original stash to get us back to where we started.
git stash apply stash@{1}

#Create a temporary patch to reverse the originally staged changes and apply it
git stash show -p | git apply -R

#Delete the temporary stash
git stash drop stash@{1}
Joe
sumber
7
Saya akan menambahkan bahwa Anda dapat mengubah skrip menjadi perintah git dengan mengikuti thediscoblog.com/blog/2014/03/29/custom-git-commands-in-3-steps
Petr Bela
3
Ini bagus! Saya telah men-tweak untuk meminta pengguna untuk deskripsi simpanan jika mereka tidak memasukkan satu di baris perintah: gist.github.com/brookinc/e2589a8c5ca33f804e4868f6bfc18282
brookinc
1
Ini tidak bekerja sama sekali dengan git 2.23.0.
lutut
Terima kasih, saya membarui dan mengubahnya menjadi alias di sini: stackoverflow.com/a/60875067/430128 .
Raman
35

TL; DR Cukup tambahkan -- $(git diff --staged --name-only)untuk <pathspec>parameter git Anda

Berikut ini adalah kalimat sederhana:

git stash -- $(git diff --staged --name-only)

Dan untuk menambahkan pesan cukup:

git stash push -m "My work in progress" -- $(git diff --staged --name-only)

Diuji pada v2.17.1 dan v2.21.0.windows.1

Keterbatasan:

  • Perlu diketahui bahwa ini akan menyimpan setiap hal, jika Anda tidak memiliki file yang dipentaskan.
  • Juga jika Anda memiliki file yang hanya sebagian dipentaskan (yaitu hanya beberapa baris yang diubah, dipentaskan sementara beberapa baris yang diubah tidak), maka seluruh file akan disimpan (termasuk baris yang tidak dipentaskan).
Somo S.
sumber
6
Saya pikir ini adalah pilihan terbaik pada situasi yang dijelaskan: mudah dimengerti dan tidak ada ilmu hitam yang terlibat!
Luis
1
Ini cukup rapi. Saya akhirnya membuat alias dari situ!
Kalpesh Panchal
simpan suara mereka lewat Som
Somo S.
@KalpeshPanchal dapatkah Anda membagikan alias Anda? Saya tidak yakin bagaimana cara menghindarinya, jadi itu tidak menafsirkannya dengan benar.
Igor Nadj
2
@IgorNadj Tentu! Ini dia: github.com/panchalkalpesh/git-aliases/commit/…
Kalpesh Panchal
15

Untuk mencapai hal yang sama ...

  1. Panggung saja file yang ingin Anda kerjakan.
  2. git commit -m 'temp'
  3. git add .
  4. git stash
  5. git reset HEAD~1

Ledakan. File yang tidak Anda inginkan disimpan. Semua file yang Anda inginkan sudah siap untuk Anda.

Michael
sumber
3
Ini adalah jawaban terbaik dan termudah untuk diingat
Kevin
9

Dalam skenario ini, saya lebih suka membuat cabang baru untuk setiap masalah. Saya menggunakan awalan temp / jadi saya tahu bahwa saya bisa menghapus cabang ini nanti.

git checkout -b temp/bug1

Tahap file yang memperbaiki bug1 dan komit.

git checkout -b temp/bug2

Anda kemudian dapat memilih komitmen dari cabang masing-masing sesuai kebutuhan dan mengirimkan permintaan tarik.

Shamp
sumber
2
Walaupun suara simpanan yang mewah itu bagus untuk diketahui, dalam praktiknya ini sepertinya pendekatan yang cenderung tidak saya sukai.
ryanjdillon
1
Gunakan "git cherry-pick tmpCommit" untuk mendapatkan komit sementara kembali dengan komit gabungan atau "git merge tmpCommit" + "git atur ulang kepala HEAD ^" untuk mendapatkan perubahan tanpa komit.
Samuel Åslund
1
Karena jawaban ini menunjukkan kadang-kadang lebih baik untuk bertanya langsung apa yang ingin Anda capai daripada bagaimana mencapainya dengan teknik yang diberikan. Cabang sementara dan cherry-pick berguna dalam situasi yang rumit.
Guney Ozsan
jika Anda mementaskan sebagian file, Anda perlu menyimpan perubahan sebelum kembali ke cabang semula dan memunculkannya lagi
jan-glx
6

Mengapa Anda tidak melakukan perubahan untuk bug tertentu dan membuat tambalan dari komit itu dan pendahulunya?

# hackhackhack, fix two unrelated bugs
git add -p                   # add hunks of first bug
git commit -m 'fix bug #123' # create commit #1
git add -p                   # add hunks of second bug
git commit -m 'fix bug #321' # create commit #2

Kemudian, untuk membuat tambalan yang sesuai, gunakan git format-patch:

git format-patch HEAD^^

Ini akan membuat dua file: 0001-fix-bug-123.patchdan0002-fix-bug-321.patch

Atau Anda dapat membuat cabang terpisah untuk setiap bug, sehingga Anda dapat menggabungkan atau mengganti perbaikan bug secara terpisah, atau bahkan menghapusnya, jika mereka tidak berhasil.

rajutan
sumber
2

git stash --keep-index adalah solusi yang baik ... kecuali itu tidak bekerja dengan benar di jalur yang telah dihapus, yang telah diperbaiki di Git 2.23 (Q3 2019)

Lihat komit b932f6a (16 Jul 2019) oleh Thomas Gummerer ( tgummerer) .
(Digabung oleh Junio ​​C Hamano - gitster- di commit f8aee85 , 25 Jul 2019)

stash: memperbaiki penanganan file yang dihapus dengan --keep-index

git stash push --keep-index seharusnya menyimpan semua perubahan yang telah ditambahkan ke indeks, baik dalam indeks dan pada disk.

Saat ini ini tidak berperilaku benar ketika file dihapus dari indeks.
Alih-alih tetap menghapusnya di disk, ** - keep-index saat ini mengembalikan file. **

Perbaiki perilaku itu dengan menggunakan ' git checkout' dalam mode tanpa-hamparan yang dengan setia dapat mengembalikan indeks dan pohon kerja.
Ini juga menyederhanakan kode.

Perhatikan bahwa ini akan menimpa file yang tidak dilacak jika file yang tidak dilacak memiliki nama yang sama dengan file yang telah dihapus dalam indeks.

VONC
sumber
2

Menyimpan hanya indeks (perubahan bertahap) di Git lebih sulit dari yang seharusnya. Saya telah menemukan jawaban @ Joe untuk bekerja dengan baik, dan mengubah sedikit variasi menjadi alias ini:

stash-index = "!f() { \
  git stash push --quiet --keep-index -m \"temp for stash-index\" && \
  git stash push \"$@\" && \
  git stash pop --quiet stash@{1} && \
  git stash show -p | git apply -R; }; f"

Ini mendorong baik perubahan bertahap dan tidak bertahap menjadi simpanan sementara, meninggalkan perubahan bertahap. Itu kemudian mendorong perubahan bertahap ke simpanan, yang merupakan simpanan yang ingin kita pertahankan. Argumen diteruskan ke alias, seperti --message "whatever"akan ditambahkan ke perintah simpanan ini. Akhirnya, muncul simpanan sementara untuk mengembalikan keadaan semula dan menghapus simpanan sementara, dan akhirnya "menghapus" perubahan simpanan dari direktori kerja melalui aplikasi tambalan terbalik.

Untuk masalah sebaliknya dari menyembunyikan hanya perubahan yang tidak dipentaskan (alias stash-working) lihat jawaban ini .

Raman
sumber
1

Apakah benar-benar perlu untuk mengerjakan beberapa bug sekaligus? Dan dengan "sekaligus," maksud saya "memiliki file yang diedit untuk beberapa bug pada saat yang sama." Karena kecuali Anda benar-benar membutuhkan itu, saya hanya akan mengerjakan satu bug pada satu waktu di lingkungan Anda. Dengan begitu Anda dapat menggunakan cabang & rebase lokal, yang menurut saya jauh lebih mudah daripada mengelola simpanan / panggung yang kompleks.

Katakanlah master ada di commit B. Sekarang kerjakan bug # 1.

git checkout -b bug1

Sekarang Anda menggunakan bug1 cabang. Buat beberapa perubahan, komit, tunggu ulasan kode. Ini bersifat lokal, jadi Anda tidak memengaruhi orang lain, dan itu seharusnya cukup mudah untuk membuat tambalan dari git diffs.

A-B < master
   \
    C < bug1

Sekarang Anda sedang mengerjakan bug2. Pergi kembali ke master dengan git checkout master. Buat cabang baru git checkout -b bug2,. Buat perubahan, komit, tunggu ulasan kode.

    D < bug2
   /
A-B < master
   \
    C < bug1

Mari kita berpura-pura bahwa orang lain melakukan E & F pada master saat Anda sedang menunggu peninjauan.

    D < bug2
   /
A-B-E-F < master
   \
    C < bug1

Ketika kode Anda disetujui, Anda dapat mengaktifkannya kembali untuk menguasainya dengan langkah-langkah berikut:

git checkout bug1
git rebase master
git checkout master
git merge bug1

Ini akan menghasilkan sebagai berikut:

    D < bug2
   /
A-B-E-F-C' < master, bug1

Kemudian Anda dapat mendorong, menghapus cabang bug1 lokal Anda, dan pergilah. Satu bug pada satu waktu di ruang kerja Anda, tetapi dengan menggunakan cabang lokal repositori Anda dapat menangani beberapa bug. Dan ini menghindari tarian panggung / simpanan yang rumit.

Jawab pertanyaan ctote di komentar:

Nah, Anda dapat kembali menyimpan untuk setiap bug, dan hanya bekerja dengan satu bug pada satu waktu. Atleast yang menyelamatkan Anda dari masalah pementasan. Tetapi setelah mencoba ini, saya pribadi merasa merepotkan. Stash agak berantakan dalam grafik log git. Dan yang lebih penting, jika Anda mengacaukan sesuatu, Anda tidak dapat kembali. Jika Anda memiliki direktori kerja yang kotor dan Anda menghapus simpanan, Anda tidak dapat "membatalkan" pop itu. Jauh lebih sulit untuk mengacaukan komitmen yang sudah ada.

Jadi git rebase -i.

Ketika Anda rebase satu cabang ke cabang lain, Anda bisa melakukannya secara interaktif (flag -i). Ketika Anda melakukan ini, Anda memiliki opsi untuk memilih apa yang ingin Anda lakukan dengan setiap komit. Pro Git adalah buku yang luar biasa yang juga online dalam format HTML, dan memiliki bagian yang bagus tentang rebasing & squashing:

http://git-scm.com/book/ch6-4.html

Saya akan mencuri contoh kata demi kata mereka untuk kenyamanan. Berpura-puralah Anda memiliki riwayat komit berikut, dan Anda ingin rebase & squash bug1 ke master:

    F < bug2
   /
A-B-G-H < master
   \
    C-D-E < bug1

Inilah yang akan Anda lihat ketika Anda mengetik git rebase -i master bug1

pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

Untuk menekan semua komit dari cabang ke dalam komit tunggal, pertahankan komit pertama sebagai "pilih" dan ganti semua entri "pilih" selanjutnya dengan "squash" atau cukup "s". Anda akan mendapatkan kesempatan untuk mengubah pesan komit juga.

pick f7f3f6d changed my name a bit
s 310154e updated README formatting and added blame
s a5f4a0d added cat-file
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit

Jadi ya, meremasnya agak menyebalkan, tapi saya masih akan merekomendasikannya karena terlalu banyak menggunakan simpanan.

Mike Monkiewicz
sumber
1
Terima kasih untuk posting rinci! Ini menyelesaikan banyak masalah saya dengan pasti - satu-satunya masalah yang saya lihat adalah tim kami saat ini telah meminta kami menyimpan semua pengiriman ke satu komit. :(
MrDuk
1
Jika mereka tidak membutuhkan atau menginginkan riwayat kerja Anda di repo produksi tidak masalah: buat pelacakan master history Anda lebih sedikit dengan menerapkan diff daripada menggabungkan cabang. Anda dapat mengetahui cara mempertahankan cabang master yang didekorasi yang memiliki sejarah gabungan yang sebenarnya dan melakukan pekerjaan nyata Anda dari itu, dengan cara itu akan mudah untuk mengotomatisasi menghasilkan diff yang benar.
jthill
2
Catatan yang git checkout master; git checkout -b bug2bisa disingkat menjadi git checkout -b bug2 master. Hal yang sama berlaku untuk git checkout bug1; git rebase master; git checkout master; git merge bug1, yang identik dengan git rebase master bug1; git push . bug1:master(memang, pushtriknya tidak jelas)
knittl
1
Saya memberikan langkah-langkah untuk menyimpan di atas dalam jawaban utama sehingga saya bisa menggunakan format mewah
Mike Monkiewicz
6
Saya telah menurunkan suara karena ini tidak menjawab pertanyaan awal. Saya berada di cabang yang mengerjakan sesuatu, dan saya baru saja membuat perubahan yang menurut saya harus dilakukan ke cabang integrasi secara terpisah. Yang ingin saya lakukan adalah tahap yang mengubah dan menyimpannya sehingga saya dapat beralih ke cabang lain dan melakukan secara terpisah, alih-alih cabang "sedang dalam proses" saat ini. (Peringatan, git mengomel di depan.) Tidak masuk akal bahwa ini sangat sulit dilakukan; Saya harus membayangkan bahwa ini adalah kejadian umum . (Bekerja di satu cabang dan menemukan perubahan cepat yang perlu dilakukan dan lupa untuk beralih dulu.)
jpmc26
0

Dari komentar Anda kepada Mike Monkiewicz, saya sarankan untuk menggunakan model yang lebih sederhana: Gunakan cabang pengembangan reguler, tetapi gunakan opsi squash penggabungan untuk mendapatkan satu komit di cabang master Anda:

git checkout -b bug1    # create the development branch
* hack hack hack *      # do some work
git commit
* hack hack hack *
git commit
* hack hack hack *
git commit
* hack hack hack *
git commit
git checkout master     # go back to the master branch
git merge --squash bug1 # merge the work back
git commit              # commit the merge (don't forget
                        #    to change the default commit message)
git branch -D bug1      # remove the development branch

Keuntungan dari prosedur ini adalah Anda dapat menggunakan alur kerja normal git.

Rudi
sumber
Saya tidak dapat melihat bagaimana jawaban ini dapat membantu. Itu tidak terkait dengan pertanyaan awal.
frapen
0

TL; DR ;git stash-staged

Setelah membuat alias:

git config --global alias.stash-staged '!bash -c "git stash -- \$(git diff --staged --name-only)"'

Berikut git diffmengembalikan daftar --stagedfile --name-only
Dan kemudian kami melewati daftar ini sebagai pathspecuntuk git stashcommad.

Dari man git stash:

git stash [--] [<pathspec>...]

<pathspec>...
   The new stash entry records the modified states only for the files
   that match the pathspec. The index entries and working tree
   files are then rolled back to the state in HEAD only for these
   files, too, leaving files that do not match the pathspec intact.

Eugen Konkov
sumber
-1

Untuk memangkas perubahan yang tidak disengaja, terutama penghapusan beberapa file, lakukan hal berikut:

git add <stuff to keep> && git stash --keep-index && git stash drop

dengan kata lain, simpel sampah itu dan buang semuanya bersama simpanan itu.

Diuji dalam git versi 2.17.1

wmax
sumber
downvote tanpa komentar tidak membantu saya atau pembaca berikutnya ... zaenks grumpy anon. Meskipun saya dapat membayangkan satu masalah dengan one-liner ini: Seseorang harus sangat berhati-hati untuk tidak lupa menambahkan semua perubahan yang diinginkan ke dalam indeks, jika tidak, perubahan-perubahan penting tersebut juga akan dihapus. Tetapi sekali lagi, penggunaan alat cli yang tidak hati-hati dapat sangat berbahaya bagi waktu dan pekerjaan seseorang yang berharga dalam kasus terburuk.
wmax