Perbarui submit Git ke komit terbaru asal

853

Saya punya proyek dengan submission Git. Itu dari ssh: // ... URL, dan sedang dalam komit A. Komit B telah didorong ke URL itu, dan saya ingin submodule untuk mengambil komit, dan mengubahnya.

Sekarang, pemahaman saya adalah yang git submodule updateseharusnya melakukan ini, tetapi tidak. Itu tidak melakukan apa-apa (tidak ada output, kode keluar sukses). Ini sebuah contoh:

$ mkdir foo
$ cd foo
$ git init .
Initialized empty Git repository in /.../foo/.git/
$ git submodule add ssh://user@host/git/mod mod
Cloning into mod...
user@host's password: hunter2
remote: Counting objects: 131, done.
remote: Compressing objects: 100% (115/115), done.
remote: Total 131 (delta 54), reused 0 (delta 0)
Receiving objects: 100% (131/131), 16.16 KiB, done.
Resolving deltas: 100% (54/54), done.
$ git commit -m "Hello world."
[master (root-commit) 565b235] Hello world.
 2 files changed, 4 insertions(+), 0 deletions(-)
 create mode 100644 .gitmodules
 create mode 160000 mod
# At this point, ssh://user@host/git/mod changes; submodule needs to change too.
$ git submodule init
Submodule 'mod' (ssh://user@host/git/mod) registered for path 'mod'
$ git submodule update
$ git submodule sync
Synchronizing submodule url for 'mod'
$ git submodule update
$ man git-submodule 
$ git submodule update --rebase
$ git submodule update
$ echo $?
0
$ git status
# On branch master
nothing to commit (working directory clean)
$ git submodule update mod
$ ...

Saya juga sudah mencoba git fetch mod, yang tampaknya melakukan pengambilan (tetapi tidak mungkin, karena itu tidak meminta kata sandi!), Tetapi git logdan git showmenolak keberadaan komitmen baru. Sejauh ini saya baru saja rmmembuka modul dan menambahkannya kembali, tetapi pada prinsipnya keduanya salah dan membosankan dalam praktiknya.

Thanatos
sumber
5
Jawaban David Z sepertinya merupakan cara yang lebih baik untuk melakukan ini - sekarang Git memiliki fungsionalitas yang Anda perlukan melalui --remoteopsi, mungkin akan berguna untuk menandai bahwa sebagai jawaban yang diterima daripada pendekatan "dengan tangan" dalam jawaban Jason?
Mark Amery
1
Saya sangat setuju dengan @MarkAmery. Sementara Jason memberikan solusi yang berfungsi, itu bukan cara yang dimaksudkan untuk melakukannya, karena ia meninggalkan komit submodule pada pengidentifikasi komit yang salah. Yang baru jelas --remotemerupakan solusi yang lebih baik pada titik waktu ini, dan karena pertanyaan ini telah dikaitkan dengan dari Github Gist tentang submodul, saya merasa akan lebih baik bagi pembaca yang datang untuk melihat jawaban baru.
MutantOctopus
Sentuhan yang bagus dengan hunter2kata sandi: o)
lfarroco

Jawaban:

1458

The git submodule updateperintah sebenarnya memberitahu Git bahwa Anda ingin submodul Anda untuk setiap memeriksa komit sudah ditentukan dalam indeks superproject tersebut. Jika Anda ingin memperbarui submodules Anda ke komit terbaru yang tersedia dari jarak jauh mereka, Anda harus melakukan ini secara langsung di submodules.

Jadi dalam ringkasan:

# Get the submodule initially
git submodule add ssh://bla submodule_dir
git submodule init

# Time passes, submodule upstream is updated
# and you now want to update

# Change to the submodule directory
cd submodule_dir

# Checkout desired branch
git checkout master

# Update
git pull

# Get back to your project root
cd ..

# Now the submodules are in the state you want, so
git commit -am "Pulled down update to submodule_dir"

Atau, jika Anda orang yang sibuk:

git submodule foreach git pull origin master
Jason
sumber
335
git submodule foreach git pull
Mathias Bynens
87
@Nicklas Dalam hal itu, gunakan git submodule foreach git pull origin master.
Mathias Bynens
54
Pada titik ini, dengan semua koreksi pada koreksi ini, saya membutuhkan seseorang untuk menulis posting blog yang jelas dan mengarahkan saya ke sana. Silahkan.
Suz
25
perbaikan kecil untuk pendekatan 'foreach' - Anda mungkin ingin menambahkan - rekursif di sana jika Anda memiliki submodul dalam submodul. sehingga: git submodule foreach --recursive git pull origin master.
orion elenzil
4
@Abdull -aSakelar untuk git commit"Beri tahu [s] perintah untuk secara otomatis mengatur file yang telah dimodifikasi dan dihapus, tetapi file baru yang belum Anda beri tahu tentang Git tidak terpengaruh."
godfrzero
473

Git 1.8.2 menampilkan opsi baru --remote,, yang akan mengaktifkan perilaku ini dengan tepat. Lari

git submodule update --remote --merge

akan mengambil perubahan terbaru dari hulu di setiap submodule, menggabungkannya, dan memeriksa revisi terbaru dari submodule. Seperti yang dijelaskan dalam dokumentasi :

--terpencil

Opsi ini hanya valid untuk perintah pembaruan. Alih-alih menggunakan SHA-1 rekaman superproyek untuk memperbarui submodule, gunakan status cabang pelacak jarak jauh submodule.

Ini sama dengan menjalankan git pulldi setiap submodule, yang umumnya persis seperti yang Anda inginkan.

David Z
sumber
4
"setara dengan berjalan git pulldi setiap submodule" Untuk memperjelas, tidak ada perbedaan (dari perspektif pengguna) antara jawaban Anda dan git submodule foreach git pull?
Dennis
3
@ Dennis itu pada dasarnya melakukan hal yang sama, tapi saya tidak yakin apakah fungsinya persis sama. Mungkin ada beberapa perbedaan kecil yang tidak saya ketahui, misalnya dalam cara kedua perintah merespons beberapa pengaturan konfigurasi.
David Z
5
Saya berharap saya dapat mengunggulkan 10.000X ini. Mengapa ini tidak ditampilkan di dokumentasi git di mana saja? Pengawasan besar.
serraosays
4
Bagi saya mereka sebenarnya berbeda cukup signifikan; foreach git pullhanya memeriksanya, tetapi tidak memperbarui pointer repo utama untuk menunjuk ke komit yang lebih baru dari submodule. Hanya dengan --remoteitu membuatnya menunjukkan komit terbaru.
Ela782
5
mengapa opsi --merge? Apa bedanya?
mFeinstein
127

Di direktori induk proyek Anda, jalankan:

git submodule update --init

Atau jika Anda menjalankan submodul rekursif:

git submodule update --init --recursive

Kadang-kadang ini masih tidak berhasil, karena entah bagaimana Anda memiliki perubahan lokal di direktori submodule lokal saat submodule sedang diperbarui.

Sebagian besar waktu perubahan lokal mungkin bukan yang ingin Anda komit. Ini bisa terjadi karena penghapusan file di submodule Anda, dll. Jika demikian, lakukan reset di direktori submodule lokal Anda dan di direktori induk proyek Anda, jalankan lagi:

git submodule update --init --recursive
pinux
sumber
5
ini jawaban yang benar. dapatkah saya mendorongnya ke repositori jarak jauh saya?
MonsterMMORPG
Ini berfungsi untuk submodula baru! Saya bisa memperbarui yang lain tetapi folder submodules baru akan tetap kosong sampai saya menjalankan perintah ini.
Alexis Wilke
1
Itu tidak menarik perubahan untuk submodul yang ada
Sergey G.
73

Proyek utama Anda menunjuk ke komit tertentu yang harus diserahkan pada submodule. git submodule updatemencoba memeriksa komit di setiap submodule yang telah diinisialisasi. Submodule benar-benar repositori independen - hanya membuat komit baru di submodule dan mendorong itu tidak cukup. Anda juga perlu secara eksplisit menambahkan versi baru dari submodule di proyek utama.

Jadi, dalam kasus Anda, Anda harus menemukan komit yang tepat dalam submodule - anggaplah itulah ujung dari master:

cd mod
git checkout master
git pull origin master

Sekarang kembali ke proyek utama, tahapan submodule dan komit bahwa:

cd ..
git add mod
git commit -m "Updating the submodule 'mod' to the latest version"

Sekarang dorong versi baru proyek utama Anda:

git push origin master

Mulai saat ini, jika ada orang lain memperbarui proyek utama mereka, maka git submodule updatebagi mereka akan memperbarui submodule, dengan asumsi itu sudah diinisialisasi.

Mark Longair
sumber
24

Sepertinya dua skenario berbeda digabungkan dalam diskusi ini:

skenario 1

Menggunakan pointer repositori orang tua saya untuk submodul, saya ingin memeriksa komit di setiap submodul yang ditunjuk repositori induk, mungkin setelah terlebih dahulu mengulangi semua submodul dan memperbarui / menariknya dari jarak jauh.

Ini, sebagaimana ditunjukkan, dilakukan dengan

git submodule foreach git pull origin BRANCH
git submodule update

Skenario 2, yang saya pikir adalah tujuan OP

Hal-hal baru telah terjadi dalam satu atau lebih submodul, dan saya ingin 1) menarik perubahan ini dan 2) memperbarui repositori induk untuk menunjuk ke komit HEAD (terbaru) dari submodul ini / ini.

Ini akan dilakukan oleh

git submodule foreach git pull origin BRANCH
git add module_1_name
git add module_2_name
......
git add module_n_name
git push origin BRANCH

Tidak terlalu praktis, karena Anda harus meng-hardcode n paths ke semua n submodules dalam skrip misalnya untuk memperbarui pointer komit repositori induk.

Akan lebih baik untuk memiliki iterasi otomatis melalui setiap submodule, memperbarui pointer repositori induk (menggunakan git add) untuk menunjuk ke kepala submodule (s).

Untuk ini, saya membuat skrip Bash kecil ini:

git-update-submodules.sh

#!/bin/bash

APP_PATH=$1
shift

if [ -z $APP_PATH ]; then
  echo "Missing 1st argument: should be path to folder of a git repo";
  exit 1;
fi

BRANCH=$1
shift

if [ -z $BRANCH ]; then
  echo "Missing 2nd argument (branch name)";
  exit 1;
fi

echo "Working in: $APP_PATH"
cd $APP_PATH

git checkout $BRANCH && git pull --ff origin $BRANCH

git submodule sync
git submodule init
git submodule update
git submodule foreach "(git checkout $BRANCH && git pull --ff origin $BRANCH && git push origin $BRANCH) || true"

for i in $(git submodule foreach --quiet 'echo $path')
do
  echo "Adding $i to root repo"
  git add "$i"
done

git commit -m "Updated $BRANCH branch of deployment repo to point to latest head of submodules"
git push origin $BRANCH

Untuk menjalankannya, jalankan

git-update-submodules.sh /path/to/base/repo BRANCH_NAME

Elaborasi

Pertama-tama, saya berasumsi bahwa cabang dengan nama $ CABANG (argumen kedua) ada di semua repositori. Jangan ragu untuk membuat ini lebih kompleks.

Beberapa bagian pertama adalah beberapa memeriksa bahwa argumen ada di sana. Kemudian saya menarik hal-hal terbaru repositori induk (saya lebih suka menggunakan - f (fast-forwarding) setiap kali saya hanya melakukan menarik. Saya telah rebase off, BTW).

git checkout $BRANCH && git pull --ff origin $BRANCH

Maka beberapa submodule yang diinisialisasi, mungkin diperlukan, jika submodul baru telah ditambahkan atau belum diinisialisasi:

git submodule sync
git submodule init
git submodule update

Lalu saya memperbarui / menarik semua submodul:

git submodule foreach "(git checkout $BRANCH && git pull --ff origin $BRANCH && git push origin $BRANCH) || true"

Perhatikan beberapa hal: Pertama-tama, saya menggunakan beberapa perintah Git &&- artinya perintah sebelumnya harus dijalankan tanpa kesalahan.

Setelah kemungkinan tarikan yang berhasil (jika hal-hal baru ditemukan pada remote), saya melakukan push untuk memastikan bahwa kemungkinan gabungan-komit tidak tertinggal pada klien. Sekali lagi, itu hanya terjadi jika tarikan benar-benar membawa barang baru.

Akhirnya, final || truememastikan bahwa skrip berlanjut pada kesalahan. Untuk membuat ini bekerja, semua yang ada di iterasi harus dibungkus dengan tanda kutip ganda dan perintah Git dibungkus dengan tanda kurung (prioritas operator).

Bagian favorit saya:

for i in $(git submodule foreach --quiet 'echo $path')
do
  echo "Adding $i to root repo"
  git add "$i"
done

Iterate semua submodules - with --quiet, yang menghilangkan output 'Entering MODULE_PATH'. Menggunakan 'echo $path'(harus dalam tanda kutip tunggal), path ke submodule ditulis ke output.

Daftar jalur submodule relatif ini ditangkap dalam array ( $(...)) - akhirnya iterate ini dan lakukan git add $iuntuk memperbarui repositori induk.

Akhirnya, komit dengan beberapa pesan yang menjelaskan bahwa repositori induk telah diperbarui. Komit ini akan diabaikan secara default, jika tidak ada yang dilakukan. Dorong ini ke asal, dan Anda selesai.

Saya memiliki skrip menjalankan ini dalam pekerjaan Jenkins yang berantai ke penyebaran otomatis terjadwal setelah itu, dan itu berfungsi seperti pesona.

Saya harap ini akan membantu seseorang.

Frederik Struck-Schøning
sumber
2
! @ # $% SO Kami menggunakan skrip yang mirip dengan skrip Anda; satu catatan: Alih-alih `` `git submodule foreach --quiet 'echo $ path'` `` kita menggunakan `` `git submodule foreach --recursive --quiet pwd` `` di dalam for loop. The pwdperintah mencetak tepat 'absolut path' untuk setiap hadir submodule; --recursivememastikan kami mengunjungi semua submodul, termasuk submodul-dalam-submodul -... yang mungkin ada dalam proyek besar. Kedua metode menyebabkan masalah dengan direktori yang mencakup spasi, misalnya /c/Users/Ger/Project\ Files/...kebijakan untuk tidak pernah menggunakan spasi putih di mana pun dalam proyek kami.
Ger Hobbelt
2
Ini bagus, dan Anda benar bahwa ada kesalahpahaman dalam beberapa jawaban tentang apa pertanyaannya, tetapi seperti yang ditunjukkan oleh jawaban David Z yang sangat baik, skrip Anda tidak perlu karena fungsi telah dibangun ke dalam Git sejak pertengahan 2013 ketika mereka menambahkan --remoteopsi. git submodule update --remoteberperilaku kira-kira seperti yang dilakukan skrip Anda.
Mark Amery
@GerHobbelt Terima kasih. Anda benar, kami hanya memiliki 1 level submodules, jadi saya tidak pernah berpikir untuk membuatnya rekursif. Saya tidak akan memperbarui skrip, sebelum saya punya kesempatan untuk memverifikasi itu berfungsi seperti yang diharapkan, tapi pasti skrip saya akan memakai sub-sub-modul. Mengenai spasi di folder, ini pasti terdengar seperti sesuatu yang harus dihindari! : S
Frederik Struck-Schøning
@MarkAmery Terima kasih atas tanggapan Anda. Saya melihat 1 masalah, namun: tidak dengan argumen dapat menentukan cabang untuk submodula. Dari manual git: The remote branch used defaults to master, but the branch name may be overridden by setting the submodule.<name>.branch option in either .gitmodules or .git/config (with .git/config taking precedence).Saya tidak ingin mengedit .gitmodules atau .git / config setiap kali saya ingin melakukan ini ke cabang lain selain master. Tapi mungkin saya melewatkan sesuatu? Selain itu, metode ini tampaknya menerapkan penggabungan rekursif (sehingga tidak ada kemungkinan maju cepat).
Frederik Struck-Schøning
Hal terakhir: Saya mencoba metode @ DavidZ, dan sepertinya tidak melakukan hal yang tepat, saya mulai melakukannya (dan op yang bertanya tentang): Menambahkan komit HEAD dari submodul ke induk (yaitu "memperbarui pointer" ). Namun, tampaknya melakukan pekerjaan satu-satunya dengan sangat baik (dan lebih cepat) untuk mengambil dan menggabungkan perubahan terbaru di semua submodul. Sayangnya, secara default hanya dari cabang master (kecuali Anda mengedit file .gitmodules (lihat di atas)).
Frederik Struck-Schøning
19

Polos dan sederhana, untuk mengambil submodul:

git submodule update --init --recursive

Dan sekarang lanjutkan memperbarui mereka ke cabang utama terbaru (misalnya):

git submodule foreach git pull origin master
Daniel Andrei Mincă
sumber
12

Catatan, sementara bentuk modern dari memperbarui submodule akan:

git submodule update --recursive --remote --merge --force

Bentuk yang lebih tua adalah:

git submodule foreach --quiet git pull --quiet origin

Kecuali ... bentuk kedua ini tidak benar-benar "tenang".

Lihat commit a282f5a (12 Apr 2019) oleh Nguyễn Thái Ngọc Duy ( pclouds) .
(Digabung oleh Junio ​​C Hamano - gitster- di commit f1c9f6c , 25 Apr 2019)

submodule foreach: perbaiki " <command> --quiet" tidak dihormati

Robin melaporkan itu

git submodule foreach --quiet git pull --quiet origin

tidak benar-benar tenang lagi.
Itu harus tenang sebelum fc1b924 ( submodule: port submodulesubcommand ' foreach' dari shell ke C, 2018-05-10, Git v2.19.0-rc0) karena parseopttidak dapat memakan pilihan saat itu.

" git pull" berperilaku seolah --quiettidak diberikan.

Ini terjadi karena parseoptdi submodule--helperakan mencoba mengurai kedua --quietopsi seolah-olah mereka adalah opsi foreach, bukan git-pull.
Opsi yang diuraikan dihapus dari baris perintah. Jadi ketika kita menarik nanti, kita jalankan saja ini

git pull origin

Saat memanggil pembantu submodule, menambahkan " --" di depan " git pull" akan berhenti parseoptuntuk opsi parsing yang bukan miliknya submodule--helper foreach.

PARSE_OPT_KEEP_UNKNOWNdihapus sebagai tindakan keamanan. parseoptseharusnya tidak melihat opsi yang tidak dikenal atau ada yang salah. Ada juga beberapa pembaruan string penggunaan sementara saya melihat mereka.

Sementara itu, saya juga menambahkan " --" ke sub-perintah lain yang lolos " $@" ke submodule--helper. " $@" dalam kasus ini adalah jalur dan kecil kemungkinannya --something-like-this.
Tapi intinya masih berdiri, git-submoduletelah diuraikan dan diklasifikasikan apa saja opsi, apa jalan.
submodule--helperseharusnya tidak pernah menganggap jalur yang dilewati git-submodulemenjadi opsi meskipun terlihat seperti satu.


Dan Git 2.23 (Q3 2019) memperbaiki masalah lain: " git submodule foreach" tidak melindungi opsi baris perintah yang diteruskan ke perintah untuk dijalankan di setiap submodule dengan benar, ketika opsi " --recursive" sedang digunakan.

Lihat komit 30db18b (24 Jun 2019) oleh Morian Sonnet ( momoson) .
(Digabung oleh Junio ​​C Hamano - gitster- di commit 968eecb , 09 Jul 2019)

submodule foreach: memperbaiki rekursi opsi

Panggilan:

git submodule foreach --recursive <subcommand> --<option>

mengarah ke kesalahan yang menyatakan bahwa opsi --<option>tidak diketahui submodule--helper.
Itu tentu saja, ketika <option>bukan pilihan yang valid untuk git submodule foreach.

Alasan untuk ini adalah, bahwa panggilan di atas diterjemahkan secara internal menjadi panggilan ke submodule - helper:

git submodule--helper foreach --recursive \
    -- <subcommand> --<option>

Panggilan ini dimulai dengan mengeksekusi sub-perintah dengan opsi di dalam submodule tingkat pertama dan melanjutkan dengan memanggil iterasi submodule foreachpanggilan berikutnya

git --super-prefix <submodulepath> submodule--helper \
   foreach --recursive <subcommand> --<option>

di dalam submodule tingkat pertama. Perhatikan bahwa tanda hubung ganda di depan sub-perintah tidak ada.

Masalah ini mulai muncul hanya baru-baru ini, karena PARSE_OPT_KEEP_UNKNOWNbendera untuk parsing argumen git submodule foreachtelah dihapus dalam komit a282f5a .
Oleh karena itu, opsi yang tidak diketahui dikeluhkan sekarang, karena parsing argumen tidak diakhiri dengan baik oleh tanda hubung ganda.

Komit ini memperbaiki masalah dengan menambahkan tanda hubung ganda di depan subperintah selama rekursi.

VONC
sumber
7
git pull --recurse-submodules

Ini akan menarik semua komitmen terbaru.


sumber
4

Dalam kasus saya, saya ingin gitmemperbarui ke yang terbaru dan pada saat yang sama mengisi kembali file yang hilang.

Berikut ini memulihkan file yang hilang (terima kasih --forceyang sepertinya tidak disebutkan di sini), tetapi tidak menarik komitmen baru:

git submodule update --init --recursive --force

Ini berhasil:

git submodule update --recursive --remote --merge --force

noseratio
sumber
3

@Jason benar dengan cara tetapi tidak sepenuhnya.

memperbarui

Perbarui submodules terdaftar, yaitu klon submodula hilang dan checkout komit yang ditentukan dalam indeks repositori yang mengandung. Ini akan membuat HEAD submodules terlepas kecuali --rebase atau --merge ditentukan atau submodule kunci. $ Name.update diatur untuk rebase atau digabung.

Jadi, git submodule updateapakah checkout, tetapi itu adalah untuk komit dalam indeks repositori yang mengandung. Ia belum tahu tentang komitmen baru di hulu sama sekali. Jadi pergi ke submodule Anda, dapatkan komit yang Anda inginkan dan komit keadaan submodule yang diperbarui di repositori utama dan kemudian lakukan git submodule update.

manojlds
sumber
1
Tampaknya jika saya memindahkan submodule ke komit yang berbeda, dan kemudian menjalankan git submodule update, pembaruan akan memindahkan submodule ke komit yang ditentukan dalam KEPALA superproyek saat ini. (Apa pun komit terbaru dalam proyek super mengatakan proyek harus di - perilaku ini, setelah penjelasan di posting Jason, tampaknya logis bagi saya) Itu juga tampaknya mengambil, tetapi hanya dalam kasus bahwa proyek berada pada komit yang salah , yang menambah kebingungan saya.
Thanatos
2

Berikut ini adalah one-liner yang luar biasa untuk memperbarui semuanya ke master terbaru:

git submodule foreach 'git fetch origin --tags; git checkout master; git pull' && git pull && git submodule update --init --recursive

Terima kasih kepada Mark Jaquith

dustinrwh
sumber
2

Jika Anda tidak tahu cabang host, buat ini:

git submodule foreach git pull origin $(git rev-parse --abbrev-ref HEAD)

Ini akan mendapatkan cabang dari repositori Git utama dan kemudian untuk setiap submodule akan menarik cabang yang sama.

NickUnuchek
sumber
0

Jika Anda ingin checkout mastercabang untuk setiap submodule - Anda dapat menggunakan perintah berikut untuk tujuan itu:

git submodule foreach git checkout master
Mohsin Mahmood
sumber