Mengapa saya harus "git push --set-upstream origin <branch>"?

146

Saya membuat cabang lokal untuk menguji Solaris dan Sun Studio. Saya kemudian mendorong cabang ke hulu. Setelah melakukan perubahan dan mencoba mendorong perubahan:

$ git commit blake2.cpp -m "Add workaround for missing _mm_set_epi64x"
[solaris 7ad22ff] Add workaround for missing _mm_set_epi64x
 1 file changed, 5 insertions(+)
$ git push
fatal: The current branch solaris has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin solaris

Mengapa saya harus melakukan sesuatu yang istimewa untuk ini?

Apakah ada kasus penggunaan yang masuk akal di mana seseorang akan membuat <branch>, mendorong <branch>remote, dan kemudian mengklaim komit <branch>tidak seharusnya untuk <branch>?


Saya mengikuti pertanyaan dan jawaban ini di Stack Overflow: Dorong cabang lokal baru ke repositori Git jarak jauh dan lacak juga . Saya menduga ini adalah contoh lain dari jawaban yang diterima tidak lengkap atau salah. Atau, ini contoh lain dari Git yang mengambil tugas sederhana dan membuatnya sulit.


Inilah tampilan pada mesin yang berbeda. Cabangnya jelas ada, jadi itu dibuat dan didorong:

$ git branch -a
  alignas
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/alignas
  remotes/origin/arm-neon
  remotes/origin/det-sig
  remotes/origin/master
  remotes/origin/solaris
jww
sumber
2
Terima kasih @Alexi. Sayangnya, dup yang dikutip tidak menjelaskan kasus penggunaan konyol yang diwakili secara default. (Itu bukan pertanyaan retoris. Saya benar-benar tertarik dengan alasan desain UX).
jww
1
Perhatikan bahwa ini dapat dikonfigurasi. Jika Anda melakukannya git config --add push.default current, maka git push akan secara otomatis membuat cabang dalam repo jarak jauh jika perlu.
Gogowitsch

Jawaban:

271

TL; DR: git branch --set-upstream-to origin/solaris


Jawaban atas pertanyaan yang Anda ajukan — yang akan saya ulangi sedikit lagi sebagai "apakah saya harus menetapkan upstream" —adalah: tidak, Anda tidak perlu mengatur upstream sama sekali.

Jika Anda tidak memiliki upstream untuk cabang saat ini, Git mengubah perilakunya aktif git push, dan pada perintah lain juga.

Kisah lengkap di sini panjang dan membosankan dan kembali ke sejarah sebelum Git versi 1.5. Untuk mempersingkat banyak, git pushditerapkan dengan buruk. 1 Pada Git versi 2.0, Git sekarang memiliki tombol konfigurasi yang dieja push.defaultsekarang menjadi default simple. Untuk beberapa versi Git sebelum dan sesudah 2.0, setiap kali Anda berlari git push, Git akan memuntahkan banyak suara yang mencoba meyakinkan Anda untuk mengatur push.defaulthanya agar bisa git pushtutup mulut.

Anda tidak menyebutkan versi Git yang Anda jalankan, atau apakah Anda telah mengonfigurasi push.default, jadi kami harus menebak. Dugaan saya adalah bahwa Anda menggunakan Git versi 2-point-something, dan Anda telah mengatur push.defaultuntuk simplemembuatnya tutup mulut. Tepatnya versi Git yang Anda miliki, dan bagaimana jika apa pun yang Anda push.defaulttetapkan, memang penting, karena sejarah yang panjang dan membosankan itu, tetapi pada akhirnya, fakta bahwa Anda mendapatkan keluhan lain dari Git menunjukkan bahwa Git Anda adalah dikonfigurasi untuk menghindari salah satu kesalahan dari masa lalu.

Apa itu hulu?

Sebuah hulu hanya nama cabang lain, biasanya cabang terpencil-pelacakan, terkait dengan (biasa, lokal) cabang.

Setiap cabang memiliki opsi untuk memiliki satu (1) set upstream. Artinya, setiap cabang memiliki hulu, atau tidak memiliki hulu. Tidak ada cabang yang dapat memiliki lebih dari satu hulu.

Hulu harus , tetapi tidak harus, cabang yang valid (apakah seperti pelacakan jarak jauh atau seperti lokal ). Artinya, jika cabang B saat ini memiliki U hulu , harus bekerja. Jika tidak berfungsi — jika mengeluh bahwa U tidak ada — maka sebagian besar Git bertindak seolah-olah hulu tidak disetel sama sekali. Beberapa perintah, seperti , akan menampilkan pengaturan hulu tetapi menandainya sebagai "hilang".origin/Bmastergit rev-parse U git branch -vv

Apa bagusnya sebuah hulu?

Jika Anda push.defaultdiatur ke simpleatau upstream, pengaturan hulu akan membuat git push, digunakan tanpa argumen tambahan, hanya berfungsi.

Itu saja — hanya itu gunanya git push. Tapi itu cukup signifikan, karena git pushmerupakan salah satu tempat di mana kesalahan ketik yang sederhana menyebabkan sakit kepala utama.

Jika Anda push.defaultdiatur ke nothing,, matchingatau current, mengatur upstream tidak melakukan apa pun untuk git push.

(Semua ini mengasumsikan versi Git Anda setidaknya 2.0.)

Hulu mempengaruhi git fetch

Jika Anda menjalankan git fetchtanpa argumen tambahan, Git mencari tahu dari jarak jauh mana dengan berkonsultasi dengan hulu cabang saat ini. Jika upstream adalah cabang pelacakan jarak jauh, Git mengambil dari jarak jauh itu. (Jika upstream tidak disetel atau cabang lokal, Git mencoba mengambil origin.)

Hulu mempengaruhi git mergedan git rebasejuga

Jika Anda menjalankan git mergeatau git rebasetanpa argumen tambahan, Git menggunakan hulu cabang saat ini. Jadi itu memperpendek penggunaan dua perintah ini.

Hulu mempengaruhi git pull

Anda seharusnya tidak pernah menggunakan 2git pull , tetapi jika Anda melakukannya, git pullgunakan pengaturan hulu untuk mencari tahu dari mana remote untuk mengambil, dan kemudian cabang mana untuk bergabung atau rebase dengan. Yaitu, git pullmelakukan hal yang sama dengan git fetch— karena itu benar-benar berjalan git fetch — dan kemudian melakukan hal yang sama dengan git mergeatau git rebase, karena itu benar-benar berjalan git merge atau git rebase.

(Anda biasanya hanya perlu melakukan dua langkah ini secara manual, setidaknya sampai Anda cukup mengenal Git sehingga ketika salah satu langkah gagal, yang nantinya akan, Anda mengenali apa yang salah dan tahu apa yang harus dilakukan tentang hal itu.)

Hulu mempengaruhi git status

Ini mungkin sebenarnya yang paling penting. Setelah Anda memiliki set upstream, git statusdapat melaporkan perbedaan antara cabang Anda saat ini dan upstreamnya, dalam hal komitmen.

Jika, seperti halnya kasus normal, Anda berada di cabang Bdengan hulu yang diatur ke , dan Anda menjalankan , Anda akan segera melihat apakah Anda memiliki komit yang dapat Anda dorong, dan / atau komit yang dapat Anda gabungkan atau rebond ke.origin/Bgit status

Ini karena git statusmenjalankan:

  • git rev-list --count @{u}..HEAD: berapa banyak komitmen yang Anda miliki Byang tidak aktif ?origin/B
  • git rev-list --count HEAD..@{u}: berapa banyak komitmen yang Anda miliki yang tidak aktif ?origin/BB

Mengatur upstream memberi Anda semua hal ini.

Kenapa mastersudah memiliki set upstream?

Saat Anda pertama kali mengkloning dari jarak jauh, menggunakan:

$ git clone git://some.host/path/to/repo.git

atau serupa, langkah terakhir yang dilakukan Git adalah, pada dasarnya git checkout master,. Ini memeriksa cabang lokal masterAnda — hanya saja Anda tidak memiliki cabang lokal master.

Di sisi lain, Anda tidak memiliki cabang terpencil-pelacakan bernama origin/master, karena Anda hanya kloning itu.

Git menebak bahwa Anda harus berarti: "membuat saya lokal baru masteryang menunjuk ke yang sama komit sebagai remote tracking origin/master, dan, sementara Anda berada di itu, mengatur hulu untuk masterke origin/master."

Ini terjadi untuk setiap cabang Anda git checkoutyang belum Anda miliki. Git menciptakan cabang dan membuatnya "melacak" (miliki sebagai upstream) cabang pelacakan jarak jauh yang sesuai.

Tetapi ini tidak berfungsi untuk cabang baru , yaitu cabang yang belum memiliki cabang pelacak jarak jauh .

Jika Anda membuat cabang baru :

$ git checkout -b solaris

masih ada, belum origin/solaris. Lokal Anda solaris tidak dapat melacak cabang pelacakan jarak jauh origin/solariskarena tidak ada.

Saat Anda pertama kali mendorong cabang baru:

$ git push origin solaris

yang menciptakan solaris pada origin, dan karenanya juga menciptakan origin/solarisdalam repositori Git Anda sendiri. Tapi sudah terlambat: Anda sudah memiliki lokal solarisyang tidak memiliki hulu . 3

Bukankah seharusnya Git hanya menetapkan itu, sekarang, sebagai upstream secara otomatis?

Mungkin. Lihat "diimplementasikan dengan buruk" dan catatan kaki 1. Sulit untuk berubah sekarang : Ada jutaan 4 skrip yang menggunakan Git dan beberapa mungkin tergantung pada perilaku saat ini. Mengubah perilaku memerlukan rilis besar baru, nag-ware untuk memaksa Anda untuk mengatur beberapa bidang konfigurasi, dan sebagainya. Singkatnya, Git adalah korban dari kesuksesannya sendiri: kesalahan apa pun yang ada di dalamnya, hari ini, hanya dapat diperbaiki jika perubahan itu sebagian besar tidak terlihat, jelas jauh lebih baik, atau dilakukan secara perlahan seiring waktu.

Faktanya adalah, tidak hari ini, kecuali Anda menggunakan --set-upstreamatau -uselama git push. Itulah yang disampaikan pesan itu kepada Anda.

Anda tidak harus melakukannya seperti itu. Yah, seperti yang kami sebutkan di atas, Anda tidak harus melakukannya sama sekali, tetapi katakanlah Anda menginginkan hulu. Anda telah membuat cabang solarispada origin, melalui dorongan sebelumnya, dan seperti yang git branchditunjukkan output Anda, Anda sudah memiliki origin/solaris dalam repositori lokal Anda.

Anda hanya tidak mengaturnya sebagai hulu untuk solaris.

Untuk mengaturnya sekarang, daripada selama dorongan pertama, gunakan git branch --set-upstream-to. The --set-upstream-tosub-perintah mengambil nama dari setiap cabang yang ada, seperti origin/solaris, dan menetapkan hulu cabang saat ini untuk cabang lainnya.

Itu saja — itu saja yang dilakukannya — tetapi semua implikasi itu disebutkan di atas. Ini berarti Anda hanya bisa berlari git fetch, melihat sekeliling, lalu berlari git mergeatau git rebasejika perlu, kemudian membuat komitmen baru dan berlari git push, tanpa banyak kerumitan-sekitar tambahan.


1 Agar adil, tidak jelas saat itu bahwa implementasi awal rawan kesalahan. Itu hanya menjadi jelas ketika setiap pengguna baru melakukan kesalahan yang sama setiap saat. Sekarang "kurang miskin", yang tidak berarti "hebat".

2 "Tidak pernah" sedikit kuat, tetapi saya menemukan bahwa pemula Git memahami banyak hal dengan lebih baik ketika saya memisahkan langkah-langkahnya, terutama ketika saya bisa menunjukkan kepada mereka apa yang git fetchsebenarnya dilakukan, dan mereka kemudian dapat melihat apa yang akan git mergeatau git rebaseakan dilakukan selanjutnya.

3 Jika Anda menjalankan yang pertama git push sebagai git push -u origin solaris—yaitu, jika Anda menambahkan -ubendera — Git akan ditetapkan origin/solarissebagai hulu untuk cabang Anda saat ini jika (dan hanya jika) dorongan berhasil. Jadi, Anda harus memasok -upada dorongan pertama . Bahkan, Anda dapat memasoknya pada sembarang dorongan kemudian, dan itu akan mengatur atau mengubah hulu pada saat itu. Tapi saya pikir git branch --set-upstream-tolebih mudah, jika Anda lupa.

4 Diukur dengan metode Austin Powers / Dr Evil dengan hanya mengatakan "satu MILLLL-YUN".

torek
sumber
2
Jika case umum adalah {create branch / push branch / use branch}, bukankah seharusnya hasil dari Push cabang lokal baru ke repositori Git jarak jauh dan melacaknya juga menjadi sesuatu yang benar-benar berfungsi? Dan jika seseorang ingin {membuat cabang / push cabang / tidak menggunakan cabang}, maka tidakkah mereka harus melakukan sesuatu yang istimewa, seperti --set-upstream /dev/null? Mengapa beban didorong ke kasus umum? Saya benar-benar tidak mengerti beberapa keputusan teknis dan kegunaan ini.
jww
1
@VonC: benar, itu titik git push -u, tapi itu benar-benar tampak seperti git push -uharus menjadi default, atau setidaknya default jika tidak ada hulu belum , dan harus ada git push --no-set-upstreamketika tidak ada saat ini merupakan hulu dan Anda ingin menyimpan seperti itu (untuk alasan apa pun yang tidak bisa dipahami :-)).
torek
2
"Kau terus mengajukan pertanyaan seperti ini karena, kurasa, kau menganggap Git sebagai" sangat menjengkelkan "." Harap simpan spekulasi semacam ini untuk diri sendiri. Saya menemukan pertanyaan ini karena saya juga terus bertanya pada diri sendiri pertanyaan-pertanyaan semacam ini. Saya bukan desainer UX terbaik di dunia, tetapi bahkan saya menyadari bahwa perilaku default dalam skenario khusus ini bisa lebih baik.
Steven Byks
4
@torek - Terima kasih. Jawaban Anda sebaliknya fantastis; dipikirkan dengan baik, terstruktur dengan baik, dan sangat informatif. :-)
Steven Byks
6
Perhatikan bahwa ini dapat dikonfigurasi. Jika Anda melakukannya git config --add push.default current, maka git push akan secara otomatis membuat cabang dalam repo jarak jauh jika perlu.
Gogowitsch
31

Perbedaan antara
git push origin <branch>
dan
git push --set-upstream origin <branch>
keduanya mendorong dengan baik ke repositori jarak jauh, tetapi saat Anda menariknya Anda melihat perbedaannya.

Jika Anda melakukannya:
git push origin <branch>
saat menarik, Anda harus melakukan:
git pull origin <branch>

Tetapi jika Anda melakukannya:
git push --set-upstream origin <branch>
maka, saat menarik, Anda hanya perlu melakukan:
git pull

Jadi menambahkan di --set-upstreammemungkinkan untuk tidak harus menentukan cabang mana yang ingin Anda tarik dari setiap waktu yang Anda lakukan git pull.

Adam
sumber
perbedaan antara dua versi "git push" yang saya tidak tahu mengapa saya ingin / perlu menggunakannya. Tak berarti!
Frank Puck
17

Perintah pada dasarnya penuh adalah seperti git push <remote> <local_ref>:<remote_ref>. Jika Anda menjalankannya saja git push, git tidak tahu apa yang harus dilakukan kecuali Anda telah membuat konfigurasi yang membantu git untuk membuat keputusan. Dalam repo git, kita dapat mengatur beberapa remote. Kami juga dapat mendorong ref lokal ke ref jarak jauh. Perintah penuh adalah cara paling mudah untuk melakukan push. Jika Anda ingin mengetik lebih sedikit kata, Anda harus mengkonfigurasi terlebih dahulu, seperti --set-upstream.

ElpieKay
sumber