Mengembalikan bagian dari komit dengan git

144

Saya ingin mengembalikan komit tertentu di git. Sayangnya, organisasi kami masih menggunakan CVS sebagai standar, jadi ketika saya komit kembali ke CVS, banyak komitmen git digulung menjadi satu. Dalam hal ini saya ingin memilih komit asli git, tetapi itu tidak mungkin.

Apakah ada pendekatan yang mirip dengan git add --patchitu yang akan memungkinkan saya mengedit selisih untuk memutuskan bagian mana dari komit untuk dikembalikan?

skiphoppy
sumber
Lebih banyak solusi di sini , tetapi berfokus pada membatasi pengembalian sebagian ke file tertentu.
ntc2

Jawaban:

226

Gunakan opsi --no-commit( -n) untukgit revert , lalu hapus tahapan perubahan, lalu gunakan git add --patch:

$ git revert -n $bad_commit    # Revert the commit, but don't commit the changes
$ git reset HEAD .             # Unstage the changes
$ git add --patch .            # Add whatever changes you want
$ git commit                   # Commit those changes

Catatan: File yang Anda tambahkan menggunakan git add --patch adalah file yang ingin Anda kembalikan, bukan file yang ingin Anda simpan.

mipadi
sumber
13
Mungkin perlu menambahkan perintah yang diperlukan akhir, bagi mereka yang tidak begitu terbiasa dengan git: setelah melakukan, git reset --harduntuk membuang perubahan lain yang Anda tidak ingin kembalikan.
tremby
15
git reset --hardberbahaya bagi pemula, karena mungkin kehilangan suntingan yang diinginkan. Alih-alih terbiasa git status, ini mengisyaratkan untuk git checkout -- FILE..mengembalikan sesuatu dengan lebih aman.
Tino
Benar-benar kelalaian yang menganga git; git revertseharusnya hanya mengambil --patchargumen.
Kaz
@ Ka: git revertdigunakan untuk mengembalikan seluruh komit. Anda dapat menggunakan git checkout -puntuk secara interaktif memilih bit untuk dikembalikan.
mipadi
1
Saya juga ingin menambahkan (mungkin) jelas, yang pertama kali menyelamatkan pekerjaan Anda . Baik commitpertama, atau stash, lalu coba revert.
Felipe Alvarez
39

Saya berhasil menggunakan yang berikut ini.

Pertama, kembalikan komit penuh (masukkan ke dalam indeks) tetapi jangan komit.

git revert -n <sha1>  # -n is short for --no-commit

Kemudian secara interaktif hapus perubahan BAIK yang dikembalikan dari indeks

git reset -p          # -p is short for --patch  

Kemudian komit kebalikan dari perubahan buruk

git commit -m "Partially revert <sha1>..."

Akhirnya perubahan GOOD yang dikembalikan (yang telah dipentaskan oleh perintah reset) masih berada di pohon kerja. Mereka perlu dibersihkan. Jika tidak ada perubahan tanpa komitmen lainnya yang tersisa di pohon kerja, ini dapat dilakukan oleh

git reset --hard
pengguna1338062
sumber
5
Apakah ini bukan alternatif yang unggul untuk jawaban yang diterima (yang menggunakan reset HEAD .), karena tidak memerlukan pembersihan akhir dir bekerja?
Steven Lu
2
Jawaban ini lebih unggul, karena reset -plebih pendek daripada reset HEADdiikuti oleh add -p. Tapi itu masih membutuhkan pembersihan, karena bakhil "baik" yang telah diatur ulang masih dalam direktori kerja setelah komit.
Chiel ten Brinke
Jawaban ini tidak unggul karena secara interaktif menghapus perubahan yang Anda inginkan sering membingungkan dan rawan kesalahan --- terutama jika salah satu dari mereka memerlukan suntingan.
Kaz
5

Secara pribadi, saya lebih suka versi ini, yang menggunakan kembali pesan komit yang dibuat secara otomatis dan memberi pengguna kesempatan untuk mengedit dan menempelkan kata "Sebagian" sebelum akhirnya melakukan.

# generate a revert commit
# note the hash printed to console on success
git revert --no-edit <hash to revert>

# undo that commit, but not its changes to the working tree
# (reset index to commit-before-last; that is, one graph entry up from HEAD)
git reset HEAD~1

# interactively add reversions
git add -p

# commit with pre-filled message
git commit -c <hash from revert commit, printed to console after first command>

# reset the rest of the current directory's working tree to match git
# this will reapply the excluded parts of the reversion to the working tree
# you may need to change the paths to be checked out
# be careful not to accidentally overwrite unsaved work
git checkout -- .
jeffcook2150
sumber
4

Larutan:

git revert --no-commit <commit hash>
git reset -p        # every time choose 'y' if you want keep the change, otherwise choose 'n'
git commit -m "Revert ..."
git checkout -- .   # Don't forget to use it.
Krzysztof Kaczmarski
sumber
Ini akan membantu orang jika Anda mengatakan bagaimana perbedaannya dari solusi yang diterima
CharlesB
1
@ Krzysztof Mengapa checkout pada akhirnya penting dan mengapa solusi ini berbeda dari yang ada pada user1338062?
martin
3

Alternatif lain (jika versi file Anda saat ini tidak terlalu jauh dari versi yang ingin Anda kembalikan) adalah untuk mendapatkan hash dari commit segera sebelum yang ingin Anda kembalikan sebagian (dari git log). Maka perintah Anda menjadi:

$ git checkout -p <hash_preceding_commit_to_revert> -- file/you/want/to/fix.ext

Ini memang mengubah file di pohon kerja Anda tetapi tidak membuat komitmen, jadi jika Anda benar-benar menyelesaikannya, Anda bisa mulai lagi dengan git reset --hard -- file/you/want/to/fix.ext .

Walf
sumber
1

Anda dapat menggunakan git-revert -n, dan kemudian gunakan add --patch untuk memilih bakhil.

William Pursell
sumber