Mengapa saya tidak bisa mendorong subtree Git terbaru ini?

88

Saya menggunakan Git subtree dengan beberapa proyek yang sedang saya kerjakan, untuk berbagi beberapa kode dasar di antara mereka. Kode dasar sering diperbarui, dan peningkatan dapat terjadi di salah satu proyek, dengan semuanya diperbarui, pada akhirnya.

Saya mengalami masalah saat git melaporkan bahwa subtree saya sudah diperbarui, tetapi mendorong ditolak. Sebagai contoh:

#! git subtree pull --prefix=public/shared project-shared master
From github.com:****
* branch            master     -> FETCH_HEAD
Already up-to-date.

Jika saya mendorong, saya akan mendapat pesan bahwa tidak ada yang perlu didorong ... Benar? BAIK? :(

#! git subtree push --prefix=public/shared project-shared master
git push using:  project-shared master
To [email protected]:***
! [rejected]        72a6157733c4e0bf22f72b443e4ad3be0bc555ce -> master (non-fast-forward)
error: failed to push some refs to '[email protected]:***'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Apa alasannya? Mengapa mendorong gagal?

mateusz
sumber
why don't you do what git is tell you: git pull?
AlexWien
8
Git pull tidak ada hubungannya dengan pertanyaan saya. Tapi ya, git pull memberi saya pesan terbaru, karena tidak ada yang baru di repo asal.
mateusz
1
Kedengarannya seperti cabang tempat Anda berada berada di belakang di mana master HEAD berada - misalnya, riwayat ... -> A -> B -> C di mana master jarak jauh HEAD berada di C tetapi cabang Anda saat ini di A (atau keturunan A tidak terhubung ke C). Saya akan melihat sejarah git saya untuk memastikan.
Mark Leighton Fisher
Saya mempunyai masalah yang sama. Saya telah menggunakan --rejoinuntuk mempercepat dorongan subtree. Ketika saya melangkah melalui dorongan subpohon secara manual, berjalan subtree split --rejoin, cabang subpohon yang terpisah hanya memiliki riwayat kembali ke penggabungan kembali terakhir, ketika biasanya berisi seluruh riwayat subpohon. Bagi saya, itulah penyebab langsung kesalahan non-fast-forward. Saya masih tidak yakin mengapa pembagian subpohon menghasilkan riwayat yang terpotong.
hurrymaplelad

Jawaban:

99

Saya menemukan jawabannya di komentar blog ini https://coderwall.com/p/ssxp5q

Jika Anda menemukan masalah "Pembaruan ditolak karena ujung cabang Anda saat ini tertinggal. Gabungkan perubahan jarak jauh (misalnya 'git pull')" saat Anda mendorong (karena alasan apa pun, terutama mengacaukan riwayat git) maka Anda harus menumpuk perintah git sehingga Anda dapat memaksakan dorongan ke heroku. misalnya, diberikan contoh di atas:

git push heroku `git subtree split --prefix pythonapp master`:master --force
Eric Woodruff
sumber
8
Bagaimana cara menjalankan perintah ini di Windows? Saya mendapatkanerror: unknown option 'prefix'
pilau
3
Luar biasa, ini baru saja menyelamatkan hari saya :)
bpipat
1
Meskipun memberikan solusi, itu tidak menjelaskan perilakunya.
klimkin
2
Benar tapi mungkin sebagian besar penggunaan subpohon dengan cara ini adalah salah satu cara mendorong ke heroku. Umumnya heroku tidak dianggap sebagai repo git defacto untuk beberapa pengguna untuk didorong dan ditarik.
Eric Woodruff
11
Saya mengalami kesulitan untuk mengidentifikasi apa hubungan herokudan pythonappjawabannya. Menerjemahkan ini dari bahasa khusus Heroku:git push <your subtree's origin> `git subtree split --prefix=Path/to/subtree master`:master --force
aednichols
39

Di windows, perintah bersarang tidak berfungsi:

git push heroku `git subtree split --prefix pythonapp master`:master --force

Anda bisa menjalankan bit bersarang terlebih dahulu:

git subtree split --prefix pythonapp master

Ini akan (setelah banyak angka) mengembalikan token, mis

157a66d050d7a6188f243243264c765f18bc85fb956

Gunakan ini dalam perintah yang memuatnya, misalnya:

git push heroku 157a66d050d7a6188f243243264c765f18bc85fb956:master --force
Chris Jordan
sumber
Ini adalah solusi yang jauh lebih bersih. Bekerja untuk saya!
Infinity
Ketika saya menjalankan git subtree split --prefix subreedirectoryname master, saya mendapatkan argumen ambigu yang fatal 'master': revisi yang tidak diketahui atau jalur yang tidak ada di pohon yang berfungsi
Demodave
Ini adalah solusi yang bagus, saya terus mendapatkan git subtreeperintah sebelumnya. Setelah menggunakan solusi Anda, saya dapat menerapkan folder ke heroku alih-alih semua repo saya
pakman198
Solusi hebat dan penghematan waktu
Nisharg Shah
29

Gunakan --ontobendera:

# DOESN'T WORK: git subtree push --prefix=public/shared project-shared master --onto=project-shared/master

[EDIT: sayangnya subtree pushtidak meneruskan --ontoke yang mendasarinya split, jadi operasi harus dilakukan dalam dua perintah! Setelah itu selesai, saya melihat bahwa perintah saya identik dengan yang ada di salah satu jawaban lain, tetapi penjelasannya berbeda jadi saya akan meninggalkannya di sini.]

git push project-shared $(git subtree split --prefix=public/shared --onto=project-shared/master):master

Atau jika Anda tidak menggunakan bash:

git subtree split --prefix=public/shared --onto=project-shared/master
# This will print an ID, say 0123456789abcdef0123456789abcdef,
# which you then use as follows:
git push project-shared 01234567:master

Saya menghabiskan berjam-jam meneliti sumber git-subtree untuk mencari tahu yang satu ini, jadi saya harap Anda menghargainya;)

subtree pushdimulai dengan menjalankan subtree split, yang menulis ulang riwayat komit Anda ke dalam format yang harus siap untuk diterapkan. Cara melakukannya adalah, ini public/shared/menghapus bagian depan jalur mana pun yang memilikinya, dan menghapus informasi apa pun tentang file yang tidak memilikinya. , atau menggabungkan komit yang identik dengan induk, juga diciutkan. [EDIT: Juga, saya telah menemukan beberapa deteksi squash, jadi sekarang saya berpikir itu hanya jika Anda menarik non-squash, dan kemudian komit gabungan sederhana runtuh yang dijelaskan dalam jawaban lain berhasil memilih jalur non-squash dan buang jalur squashed.]) Hasilnya adalah, hal-hal yang coba didorong akhirnya berisi pekerjaan yang dilakukan seseorang pada repositori host saat ini yang Anda dorong,Itu berarti bahkan jika Anda menarik non-squashed, semua komit sub-repositori upstream akan diabaikan karena mereka memberi nama file dengan jalur kosongnya. (Komitmen yang tidak menyentuh file apa pun di bawahpublic/shared/

Namun, jika Anda menggunakan --onto, maka semua komit upstream dicatat sebagai OK untuk digunakan secara verbatim, jadi ketika proses penulisan ulang menemukan mereka sebagai salah satu induk gabungan yang ingin ditulis ulang, itu akan membuat mereka tetap bukannya mencoba menulis ulang mereka dengan cara biasa.

entheh
sumber
2
Saya merekomendasikan jawaban ini sebagai jawaban, yang telah membantu saya dua kali. Dan deskripsi adalah apa yang saya butuhkan untuk memahami cara kerja pemisahan dan dorongan ke tempat yang tepat. Terima kasih.
Kamil Wozniak
2
Apa itu "bersama-proyek"?
Colin D
1
@ColinD "project-shared" adalah nama remote. Itu adalah nama yang akan Anda pilih saat mengonfigurasi tempat untuk mengambil dan mendorong. Anda bisa mendapatkan daftar remote dengan git remote -v.
Entheh
@entheh jam yang Anda habiskan untuk ini hanya membuat hari saya. Terima kasih!
Juni
1
Saya menemukan bug ini selalu terjadi ketika sub repositori Anda memiliki direktori yang namanya sama dengan awalan, misalnya Anda menambahkan sub repositori sebagai "libxxx" ke dalam repositori utama, dan sub repositori Anda sendiri juga memiliki sub direktori bernama "libxxx ". Dalam kasus ini git akan salah memperlakukan komit di sub repositori sebagai komit di repositori utama dan mencoba untuk memisahkannya. The --ontopilihan membantu git untuk mengidentifikasi komit yang sudah di sub repositori.
Cosyn
14

Untuk aplikasi jenis "GitHub pages", di mana Anda menerapkan subpohon "dist" ke cabang gh-pages, solusinya mungkin terlihat seperti ini

git push origin `git subtree split --prefix dist master`:gh-pages --force

Saya menyebutkan ini karena tampilannya sedikit berbeda dari contoh heroku yang diberikan di atas. Anda dapat melihat bahwa folder "dist" saya ada di cabang master repo saya, dan kemudian saya mendorongnya sebagai subtree ke cabang gh-pages yang juga ada di origin.

Colin D
sumber
3

Saya telah mengalami masalah ini sebelumnya juga, dan inilah cara saya menyelesaikannya.

Apa yang saya temukan adalah saya memiliki cabang yang tidak terhubung ke cabang master lokal. Cabang ini ada dan hanya tergantung di kehampaan. Dalam kasus Anda, itu mungkin disebut project-shared. Dengan asumsi hal ini terjadi dan ketika Anda melakukannya, git branchAnda dapat melihat project-sharedcabang lokal , maka Anda dapat ' menambahkan ' komit baru ke project-sharedcabang yang ada dengan melakukan:

git subtree split --prefix=public/shared --onto public-shared --branch public-shared

Cara saya memahaminya adalah git subtreeakan mulai membuat cabang baru dari --onto, dalam hal ini public-sharedcabang lokalnya . Kemudian cabang berarti membuat cabang, yang menggantikan public-sharedcabang lama .

Ini akan menyimpan semua SHA public-sharedcabang sebelumnya. Terakhir, Anda dapat melakukan

git push project-shared project-shared:master

Dengan asumsi bahwa Anda memiliki project-sharedremote juga; ini akan mendorong penggantung lokal diproject-shared cabang kosong ke mastercabang remote project-shared.

snw
sumber
3

Ini karena keterbatasan algoritma asli. Saat menangani komit gabungan, algoritme asli menggunakan kriteria yang disederhanakan untuk memotong orang tua yang tidak terkait. Secara khusus, ia memeriksa, jika ada induk, yang memiliki pohon yang sama. Jika orang tua seperti itu ditemukan, itu akan menciutkan komit gabungan dan menggunakan komit induk sebagai gantinya, dengan asumsi bahwa orang tua lain memiliki perubahan yang tidak terkait dengan sub-pohon. Dalam beberapa kasus, hal ini akan mengakibatkan hilangnya bagian sejarah, yang memiliki perubahan aktual pada sub-pohon. Secara khusus itu akan menjatuhkan urutan komit, yang akan menyentuh sub-pohon, tetapi menghasilkan nilai sub-pohon yang sama.

Mari kita lihat contoh (yang dapat Anda gandakan dengan mudah) untuk lebih memahami cara kerjanya. Pertimbangkan sejarah berikut (format barisnya adalah: commit [tree] subject):

% git log --graph --decorate --pretty=oneline --pretty="%h [%t] %s"
*   E [z] Merge branch 'master' into side-branch
|\
| * D [z] add dir/file2.txt
* | C [y] Revert "change dir/file1.txt"
* | B [x] change dir/file1.txt
|/
*   A [w] add dir/file1.txt

Dalam contoh ini, kita akan membagi dir. Komit Ddan Ememiliki pohon yang sama z, karena kita memiliki komit C, yang dibatalkan komit B, jadi B-Curutan tidak melakukan apa dirpun meskipun ada perubahan padanya.

Sekarang mari lakukan pemisahan. Pertama kita berpisah saat komit C.

% git log `git subtree split -P dir C` ...
* C' [y'] Revert "change dir/file1.txt"
* B' [x'] change dir/file1.txt
* A' [w'] add dir/file1.txt

Selanjutnya kita berpisah saat komit E.

% git log `git subtree split -P dir E` ...
* D' [z'] add dir/file2.txt
* A' [w'] add dir/file1.txt

Ya, kami kehilangan dua komitmen. Ini menghasilkan kesalahan saat mencoba mendorong pemisahan kedua, karena tidak memiliki dua commit tersebut, yang sudah masuk ke asalnya.

Biasanya Anda dapat mentolerir kesalahan ini dengan menggunakan push --force, karena komit yang dihapus umumnya tidak akan memiliki informasi penting di dalamnya. Dalam jangka panjang, bug perlu diperbaiki, sehingga riwayat terpisah benar-benar memiliki semua komitmen, yang menyentuh dir, seperti yang diharapkan. Saya mengharapkan perbaikan untuk menyertakan analisis yang lebih dalam tentang komitmen orang tua untuk dependensi tersembunyi.

Sebagai referensi, berikut adalah bagian dari kode asli, yang bertanggung jawab atas perilaku tersebut.

copy_or_skip()
  ...
  for parent in $newparents; do
      ptree=$(toptree_for_commit $parent) || exit $?
      [ -z "$ptree" ] && continue
      if [ "$ptree" = "$tree" ]; then
          # an identical parent could be used in place of this rev.
          identical="$parent"
      else
          nonidentical="$parent"
      fi
  ...
  if [ -n "$identical" ]; then
      echo $identical
  else
      copy_commit $rev $tree "$p" || exit $?
  fi
klimkin
sumber
apakah bug telah diperbaiki pada 2020-05-09 (Sab)? @klimkin
halfmoonhalf
bug sudah diperbaiki. lihat: contrib / subtree: perbaiki bug "subpree split" skipped-merge. github.com/git/git/commit/...
halfmoonhalf
0

Jawaban Eric Woodruff tidak membantu saya, tetapi yang berikut berhasil:

Saya biasanya 'git subtree pull' dengan opsi '--squash'. Tampaknya hal ini membuat segalanya lebih sulit untuk didamaikan, jadi saya perlu melakukan tarikan subtree tanpa menekan kali ini, menyelesaikan beberapa konflik dan kemudian mendorong.

Saya harus menambahkan bahwa tarikan yang terjepit tidak mengungkapkan konflik apa pun, memberi tahu saya bahwa semuanya baik-baik saja.

Zsolt Szatmari
sumber
0

Solusi [sederhana] lainnya adalah memajukan head dari remote dengan membuat commit lain jika Anda bisa. Setelah Anda menarik kepala lanjutan ini ke subpohon lokal maka Anda akan dapat mendorongnya lagi.

9 rawa
sumber
1
Ini persis bagaimana saya memecahkan masalah yang sama dengan repositori jarak jauh yang tidak sinkron dengan subpohon lokal.
Aidas Bendoraitis
0

Anda dapat memaksa mendorong perubahan lokal ke repo subpohon jarak jauh

git push subtree_remote_address.git `git subtree split --prefix=subtree_folder`:refs/heads/branch --force
FunkyKat
sumber
0

PowerShell cepat untuk pengguna windows berdasarkan solusi Chris Jordan

$id = git subtree split --prefix pythonapp master
Write-Host "Id is: $id"
Invoke-Expression "git push heroku $id`:master --force"
Rtype
sumber
0

Jadi ini yang saya tulis berdasarkan apa yang dikatakan @entheh.

for /f "delims=" %%b in ('git subtree split --prefix [name-of-your-directory-on-the-file-system-you-setup] -b [name-of-your-subtree-branch]') do @set token=%%b
git push [alias-for-your-subtree] %token%:[name-of-your-subtree-branch] --force

pause
Demodave
sumber
0

Punya masalah ini juga. Inilah yang saya lakukan, berdasarkan jawaban teratas:

Diberikan:

#! git subtree push --prefix=public/shared project-shared master
git push using:  project-shared master
To [email protected]:***
! [rejected]        72a6157733c4e0bf22f72b443e4ad3be0bc555ce -> master (non-fast-forward)
error: failed to push some refs to '[email protected]:***'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Masukkan ini:

git push <origin> 72a6157733c4e0bf22f72b443e4ad3be0bc555ce:<branch> --force

Perhatikan bahwa token yang dikeluarkan oleh 'git subtree push' digunakan dalam 'git push'.

Amos
sumber