Bagaimana saya mendorong commit yang diubah ke repositori Git jarak jauh?

663

Ketika saya sudah bekerja sedikit dengan kode sumber saya, saya melakukan komit hal biasa dan kemudian saya mendorong ke repositori jarak jauh. Tapi kemudian saya perhatikan saya lupa mengatur impor saya dalam kode sumber. Jadi saya melakukan perintah amend untuk mengganti komit sebelumnya:

> git commit --amend

Sayangnya komit tidak dapat didorong kembali ke repositori. Ditolak seperti ini:

> git push origin
To //my.remote.repo.com/stuff.git/
 ! [rejected]        master -> master (non-fast forward)
error: failed to push some refs to '//my.remote.repo.com/stuff.git/'

Apa yang harus saya lakukan? (Saya dapat mengakses repositori jarak jauh.)

Spoike
sumber
Bagaimana jika - amend saya hanya mengubah pesan komit? Adakah cara untuk mengedit pesan komit terakhir saja, jika sudah didorong ke remote? Saya melakukan itu pada Github dan mendapat pesan yang sama tentang non fast forward. Lalu saya menerapkan solusi di bawah ini tetapi gabungan baru saja menambahkan lebih banyak pesan komit di atas ..
7
@ FAB: Saya pikir itu adalah FAQ. Sebuah pesan komit di-hash bersamaan dengan komit, jadi menyimpanya mengubah revid (hash). Jika tidak jelas: tidak, Anda tidak bisa. IIRC dapat menyimpan info out-of-band dalam catatan (sehingga Anda dapat membuat anotasi komitmen yang ada tanpa mengubahnya). Untuk melabeli komit tertentu, gunakan tag
lihat
1
Anda akan segera (git1.8.5, Q4 2013) dapat melakukan git push -forcelebih hati-hati .
VonC
3
Ini adalah gaya koboi. Jangan belajar lebih jauh atau tidak mencari cara untuk membatalkan perubahan git sebelumnya. Cukup tambahkan beberapa kode placeholder, maksud saya, Tambahkan beberapa komentar, Bersihkan sedikit kode atau cukup tambahkan beberapa dasbor dasbor .... Sekarang buat komit nyata dan dorong ke remote. Selesai!
nehem
@ user58777 Jika-perubahan Anda hanya untuk mengubah pesan komit dan Anda belum membuat komit lokal tambahan sejak itu, Anda dapat mengatur ulang cabang lokal Anda ke komit jarak jauh yang Anda dorong sebelum mengubah pesan komit.
Scott Ahten

Jawaban:

504

Saya pernah mendorong --forcedan .gitrepositori dan dimarahi oleh Linus BIG TIME . Secara umum ini akan menciptakan banyak masalah bagi orang lain. Jawaban sederhana adalah "Jangan lakukan itu".

Saya melihat orang lain memberikan resep untuk melakukannya, jadi saya tidak akan mengulanginya di sini. Tapi di sini ada tip untuk pulih dari situasi setelah Anda mendorong komit yang diubah dengan --force (atau + master).

  1. Gunakan git refloguntuk menemukan komit lama yang Anda ubah (sebut saja old, dan kami akan memanggil komit baru yang Anda buat dengan mengubah new).
  2. Buat gabungan antara olddan new, merekam pohon new, suka git checkout new && git merge -s ours old.
  3. Gabungkan itu dengan tuanmu git merge master
  4. Perbarui master Anda dengan hasilnya dengan git push . HEAD:master
  5. Dorong hasilnya.

Maka orang-orang yang cukup malang untuk mendasarkan pekerjaan mereka pada komit yang Anda lupakan dengan mengubah dan memaksakan dorongan akan melihat hasil gabungan akan melihat bahwa Anda newlebih menyukai old. Penggabungan mereka nanti tidak akan melihat konflik antara olddan newyang dihasilkan dari amandemen Anda, sehingga mereka tidak harus menderita.

João Pimentel Ferreira
sumber
17
Saya sangat menyadari apa yang terjadi ketika Anda memaksakan komit yang diamandemen (dengan menghancurkan sejarah). Untungnya, saya adalah satu-satunya pengembang di proyek ini dengan repo jarak jauh berada di drive jaringan jadi itu bukan masalah besar. Saya tidak pernah berpikir untuk menggabungkan amandit commit, jadi saya akan membenarkan hal ini.
Spoike
62
Di perusahaan kami, kami secara paksa mendorong-paksa ... pada cabang fitur yang dikembangkan oleh individu.
Ondra Žižka
2
Memarahi dari Linus adalah karena Anda menghapus sejarah dengan opsi kekuatan, bukan karena Anda tidak harus melakukannya. Solusi GabrielleV berfungsi dengan baik, karena itu tidak mengubah sejarah.
user411279
2
Tolong, karena penulis (gitster) dari jawaban ini tampaknya tidak ada lagi, dapatkah ada yang membantu untuk memperjelas item nomor 1: temukan commit lama. Jika Anda tidak memiliki cadangan, di mana Anda akan menemukannya? Mengubah dan memaksakan dorongan tidak akan dihancurkan? Mungkin dia merujuk untuk mendapatkannya dari teman / kolaborator yang masih memilikinya di pohon?
Dr Beco
2
Dr Breco dapat Anda gunakan git refloguntuk menemukannya
Simon Zyx
269

Anda melihat fitur keamanan Git. Git menolak untuk memperbarui cabang jarak jauh dengan cabang Anda, karena komit cabang Anda bukan keturunan langsung dari komit kepala saat ini dari cabang yang Anda dorong.

Jika ini tidak terjadi, maka dua orang yang mendorong ke repositori yang sama pada waktu yang sama tidak akan tahu bahwa ada komit baru yang datang pada saat yang sama dan siapa pun yang mendorong terakhir akan kehilangan pekerjaan pendorong sebelumnya tanpa salah satu dari keduanya. mereka menyadari hal ini.

Jika Anda tahu bahwa Anda adalah satu-satunya orang yang mendorong dan Anda ingin mendorong komit yang diubah atau mendorong komit yang memutar kembali cabang, Anda dapat 'memaksa' Git untuk memperbarui cabang jarak jauh dengan menggunakan -fsakelar.

git push -f origin master

Bahkan ini mungkin tidak berfungsi karena Git memungkinkan repositori jarak jauh untuk menolak dorongan tidak cepat di ujung dengan menggunakan variabel konfigurasi receive.denynonfastforwards. Jika demikian, alasan penolakan akan terlihat seperti ini (perhatikan bagian 'jauh ditolak'):

 ! [remote rejected] master -> master (non-fast forward)

Untuk menyiasatinya, Anda harus mengubah konfigurasi repositori jarak jauh atau sebagai peretasan kotor yang dapat Anda hapus dan buat kembali cabang dengan demikian:

git push origin :master
git push origin master

Secara umum parameter terakhir yang git pushmenggunakan format <local_ref>:<remote_ref>, di mana local_refadalah nama cabang pada repositori lokal dan remote_refadalah nama cabang pada repositori jarak jauh. Pasangan perintah ini menggunakan dua singkatan. :mastermemiliki local_ref nol yang berarti mendorong cabang null ke sisi jarak jauh master, yaitu menghapus cabang jarak jauh. Nama cabang tanpa :berarti mendorong cabang lokal dengan nama yang diberikan ke cabang jauh dengan nama yang sama. masterdalam situasi ini adalah kependekan dari master:master.

CB Bailey
sumber
2
ini tidak bekerja dengan github, itu memberi saya pesan berikut: master [ditolak jauh] (penghapusan cabang saat ini dilarang)
vedang
Saya tidak ingin memaksakan dorongan (yang saya tahu akan menyelesaikan masalah), tetapi sekarang saya kira saya tidak punya pilihan.
vedang
1
Ini adalah satu-satunya solusi yang berfungsi untuk repo saya yang dihosting dengan assembla.
Justin
1
menghapus cabang master jarak jauh akan mengosongkan ruang dalam repo jarak jauh?
Mr_and_Mrs_D
1
@Mr_and_Mrs_D: Tidak segera, tetapi setelah satu git gckali reflog telah kadaluarsa benda-benda lama akan dipangkas. Tidak seorang pun yang mengkloning repositori akan mendapatkan objek apa pun yang tidak lagi dapat dijangkau segera setelah cabang diperbarui.
CB Bailey
211

Kata-kata kasar: Fakta bahwa tidak ada yang memposting jawaban sederhana di sini menunjukkan permusuhan pengguna yang ditunjukkan oleh Git CLI.

Ngomong-ngomong, cara "jelas" untuk melakukan ini, dengan asumsi Anda belum mencoba untuk memaksa, adalah menarik terlebih dahulu. Ini menarik perubahan yang telah Anda ubah (dan tidak lagi dimiliki) sehingga Anda memilikinya lagi.

Setelah Anda menyelesaikan konflik apa pun, Anda dapat mendorong lagi.

Begitu:

git pull

Jika Anda mendapatkan kesalahan saat menarik, mungkin ada sesuatu yang salah dalam konfigurasi repositori lokal Anda (saya punya ref yang salah di bagian cabang .git / config).

Dan kemudian

git push

Mungkin Anda akan mendapatkan komitmen ekstra dengan subjek yang bercerita tentang "Penggabungan Trivial".

GabrieleV
sumber
2
Ya, saya menulis tentang ini, lihat stackoverflow.com/questions/253055/… ;)
Spoike
10
Ini tidak benar-benar berfungsi seperti yang saya harapkan. Itu menciptakan dua komitmen baru. Yang merupakan replika dari yang lama, tetapi dengan perubahan yang diubah. Dan satu menggabungkan komit dengan diff kosong. Masih membiarkan komit lama tidak berubah, mengungkapkan data sensitif yang mungkin saya coba ubah. Saya percaya git push -fatau git resetsatu-satunya cara untuk pergi ke sini.
lutut
40
Sementara secara teknis menjawab masalah, itu tidak benar-benar mengatasi masalah tersebut. Seperti yang Anda katakan, itu akan menghasilkan komit tambahan, tetapi alasan utama orang mengubah komit adalah untuk menghindari membuat yang baru. Jadi, jika poster itu mengikuti instruksi Anda, dia tidak akan mendapatkan hasil yang diinginkan. Akan lebih masuk akal untuk tidak mengubah komit sejak awal.
Dan Jones
102

Jawaban singkat: Jangan memaksakan komitmen yang diubah ke repo publik.

Jawaban panjang: Beberapa perintah Git, seperti git commit --amenddangit rebase , sebenarnya menulis ulang grafik sejarah. Ini baik-baik saja selama Anda belum mempublikasikan perubahan Anda, tetapi begitu Anda melakukannya, Anda benar-benar tidak boleh berkutat dengan sejarah, karena jika seseorang sudah mendapatkan perubahan Anda, maka ketika mereka mencoba menariknya lagi, itu mungkin gagal . Alih-alih mengubah komit, Anda harus membuat komit baru dengan perubahan.

Namun, jika Anda benar-benar ingin mendorong komit yang diubah, Anda dapat melakukannya seperti ini:

$ git push origin +master:master

Tanda terkemuka +akan memaksa dorongan untuk terjadi, bahkan jika itu tidak menghasilkan komitmen "maju cepat". (Komitmen maju cepat terjadi ketika perubahan yang Anda dorong adalah keturunan langsung dari perubahan yang sudah ada dalam repo publik.)

mipadi
sumber
5
Bagaimana ini berbeda (lebih baik atau lebih buruk) dari git push -f? Terima kasih!
bentford
11
@ Benford: Pada dasarnya sama dengan git push -f.
mipadi
54

Ini adalah cara yang sangat sederhana dan bersih untuk mendorong perubahan Anda setelah Anda membuat commit --amend:

git reset --soft HEAD^
git stash
git push -f origin master
git stash pop
git commit -a
git push origin master

Yang melakukan hal berikut:

  • Setel ulang komit cabang ke komit orang tua.
  • Simpan komit terakhir ini.
  • Dorong paksa ke jarak jauh. Remote sekarang tidak memiliki komit terakhir.
  • Pop simpanan Anda.
  • Berkomitmen dengan bersih.
  • Dorong ke jarak jauh.

Ingatlah untuk mengubah "asal" dan "master" jika menerapkan ini ke cabang lain atau jarak jauh.

Faiza
sumber
3
2 komentar: - pastikan untuk mengubah nama cabang jika Anda bekerja pada yang lain - saya harus menggunakan git addsebelum komit saya untuk memasukkan perubahan.
SylvainB
1
Pada Windows CMD, perintah pertama harus melarikan diri: git reset --soft "HEAD^". Sisanya bekerja dengan baik.
MrMister
2
"cara yang sangat sederhana dan bersih .." cit. Prosedur ini termasuk dorongan paksa. Mengingat semua kritik dalam Jawaban di atas, saya tidak yakin apakah prosedur ini benar-benar bersih.
Na13-c
24

Saya telah menyelesaikannya dengan membuang komit yang diubah lokal saya dan menambahkan perubahan baru di atas:

# Rewind to commit before conflicting
git reset --soft HEAD~1

# Pull the remote version
git pull

# Add the new commit on top
git add ...
git commit
git push
bara
sumber
2
Ini adalah versi paling sederhana!
mknaf
Menambahkan komit 'perubahan' lain lebih baik daripada mengotak-atik riwayat penulisan ulang. Saya setuju dengan @mknaf
sdkks
8

Saya memiliki masalah yang sama.

  • Secara tidak sengaja mengubah komit terakhir yang sudah didorong
  • Dilakukan banyak perubahan secara lokal, dilakukan sekitar lima kali
  • Mencoba mendorong, mendapat kesalahan, panik, bergabung jauh, mendapat banyak file-bukan, didorong, gagal, dll.

Sebagai seorang pemula Git, saya pikir itu FUBAR lengkap .

Solusi: Agak seperti @bara menyarankan + membuat cabang cadangan lokal

# Rewind to commit just before the pushed-and-amended one.
# Replace <hash> with the needed hash.
# --soft means: leave all the changes there, so nothing is lost.
git reset --soft <hash>

# Create new branch, just for a backup, still having all changes in it.
# The branch was feature/1234, new one - feature/1234-gone-bad
git checkout -b feature/1234-gone-bad

# Commit all the changes (all the mess) not to lose it & not to carry around
git commit -a -m "feature/1234 backup"

# Switch back to the original branch
git checkout feature/1234

# Pull the from remote (named 'origin'), thus 'repairing' our main problem
git pull origin/feature/1234

# Now you have a clean-and-non-diverged branch and a backup of the local changes.
# Check the needed files from the backup branch
git checkout feature/1234-gone-bad -- the/path/to/file.php

Mungkin itu bukan solusi yang cepat dan bersih, dan saya kehilangan sejarah (1 melakukan alih-alih 5), tapi itu menyelamatkan pekerjaan sehari.

davisca
sumber
6

Jika Anda belum mendorong kode ke cabang jarak jauh Anda (GitHub / Bitbucket), Anda dapat mengubah pesan komit pada baris perintah seperti di bawah ini.

 git commit --amend -m "Your new message"

Jika Anda bekerja pada cabang tertentu, lakukan ini:

git commit --amend -m "BRANCH-NAME: new message"

Jika Anda sudah mendorong kode dengan pesan yang salah, maka Anda harus berhati-hati saat mengubah pesan. yaitu setelah Anda mengubah pesan komit dan mencoba mendorongnya lagi Anda akhirnya memiliki masalah. Untuk membuatnya lancar ikuti langkah-langkah berikut.

Harap baca seluruh jawaban sebelum melakukannya

git commit --amend -m "BRANCH-NAME : your new message"

git push -f origin BRANCH-NAME                # Not a best practice. Read below why?

Catatan penting: Saat Anda menggunakan push paksa secara langsung, Anda mungkin berakhir dengan masalah kode yang sedang dikerjakan pengembang lain di cabang yang sama. Jadi untuk menghindari konflik tersebut, Anda perlu menarik kode dari cabang Anda sebelum memaksa :

 git commit --amend -m "BRANCH-NAME : your new message"
 git pull origin BRANCH-NAME
 git push -f origin BRANCH-NAME

Ini adalah praktik terbaik saat mengubah pesan komit, jika sudah didorong.

Prabhakar Undurthi
sumber
1
Jika Anda berhasil menarik kembali komit dalam contoh terakhir Anda, mengapa Anda perlu memaksakan dorongan? Bukankah dorongan standar sudah cukup? Terima kasih
Thomas
Pertanyaan yang diajukan oleh Thomas sebenarnya sangat valid. Saya sendiri tidak perlu memaksakan dorongan berikut tarikan.
Na13-c
Tolong jangan menyebutnya "praktik terbaik" karena ada cara untuk mengatasinya --force, lihat jawaban yang diterima
Farid
5

Jika Anda tahu tidak ada yang menarik komit yang tidak diubah, gunakan --force-with-leaseopsi git push.

Di TortoiseGit, Anda dapat melakukan hal yang sama di bawah opsi "Push ..." "Force: May discard" dan memeriksa "perubahan yang diketahui".

Force (Dapat membuang perubahan yang diketahui) memungkinkan repositori jarak jauh menerima dorongan non-maju lebih aman. Ini dapat menyebabkan repositori jarak jauh kehilangan komit; gunakan dengan hati-hati. Ini dapat mencegah dari kehilangan perubahan yang tidak diketahui dari orang lain pada remote. Ia memeriksa apakah cabang server menunjuk komit yang sama dengan cabang pelacakan jarak jauh (perubahan yang diketahui). Jika ya, push force akan dilakukan. Kalau tidak, akan ditolak. Karena git tidak memiliki tag pelacakan jarak jauh, tag tidak dapat ditimpa menggunakan opsi ini.

ShawnFeatherly
sumber
4

Anda mendapatkan kesalahan ini karena remote Git sudah memiliki file komit ini. Anda harus memaksa mendorong cabang agar ini berfungsi:

git push -f origin branch_name

Pastikan juga Anda menarik kode dari jarak jauh karena orang lain di tim Anda mungkin telah mendorong ke cabang yang sama.

git pull origin branch_name

Ini adalah salah satu kasus di mana kita harus memaksakan komit ke remote.

Praveen Dhawan
sumber
Mengapa tidak jawaban ini merupakan akuntansi untuk komentar besar yang diajukan dalam jawaban sebelumnya?
Na13-c
2

Ini adalah cara yang sangat sederhana dan bersih untuk mendorong perubahan Anda setelah Anda membuat git add "your files"dan git commit --amend:

git push origin master -f

atau:

git push origin master --force
kasar
sumber
Saya dengar itu buruk, dan saya yakin itu. Ada alasan (bagus) bagi git untuk gagal secara default (dan membutuhkan --force), saya yakin.
Rolf
1

Saya harus memperbaiki masalah ini dengan menarik dari repo jarak jauh dan menangani konflik gabungan yang muncul, komit dan kemudian dorong. Tapi saya merasa ada cara yang lebih baik.

Spoike
sumber
Tidak juga. Masalahnya mungkin Anda belum memperbarui salinan lokal Anda dari repo jarak jauh. Git tidak akan mendorongnya karena Anda mungkin harus berurusan dengan penggabungan secara manual. Di balasan saya yang lain, saya memiliki perintah (dan penjelasan) yang akan memaksa push - tapi waspadalah yang dapat menghapus perubahan di remote.
mipadi
1

Saya terus melakukan apa yang diperintahkan Git agar saya lakukan. Begitu:

  • Tidak dapat mendorong karena komit yang diamandemen.
  • Saya melakukan tarikan seperti yang disarankan.
  • Penggabungan gagal. jadi saya memperbaikinya secara manual.
  • Buat komit baru (berlabel "gabungan") dan dorong.
  • Tampaknya berhasil!

Catatan: Komit yang diamandemen adalah yang terbaru.

Rolf
sumber
1
Saya akan menurunkan suara, jika saya memiliki lebih banyak poin reputasi, jadi saya hanya akan bertanya di sini dengan sopan, apa yang Anda menderita? Orang yang diamandemen? Yang, yang menarik dan bekerja di cabang dengan komit diubah? Sebelum amandemen, atau sesudahnya? Saya baru saja membersihkan setiap modifikasi saya karena saya salah paham dengan Anda ... Untungnya tidak banyak ...
Bartis Áron
1

Berikut ini berfungsi untuk saya ketika mengganti Pengarang dan Pengalih komit.

git push -f origin master

Git cukup pintar untuk mengetahui bahwa ini adalah komitmen dari delta identik yang hanya berbeda di bagian informasi meta.

Kepala daerah dan kepala daerah menunjuk ke komit yang bersangkutan.

Ahli Psikis
sumber
0

Di sini, Bagaimana saya memperbaiki suntingan di komit sebelumnya:

  1. Simpan pekerjaan Anda sejauh ini.
  2. Simpan perubahan Anda untuk saat ini jika dilakukan: git stashSekarang copy pekerjaan Anda bersih pada keadaan komit terakhir Anda.
  3. Lakukan pengeditan dan perbaikan.
  4. Komit perubahan dalam mode "ubah" :git commit --all --amend
  5. Editor Anda akan muncul meminta pesan log (secara default, pesan log lama). Simpan dan keluar dari editor jika Anda senang.

    Perubahan baru ditambahkan ke komit lama. Lihat sendiri git logdan dengangit diff HEAD^

  6. Terapkan ulang perubahan simpanan Anda, jika dilakukan: git stash apply

Harshal Wani
sumber