ทำไม God Submodule HEAD ของฉันถูกถอดออกจากอาจารย์?


163

ฉันใช้ Git submodules หลังจากดึงการเปลี่ยนแปลงจากเซิร์ฟเวอร์หลาย ๆ ครั้งหัว submodule ของฉันจะถูกถอดออกจากสาขาหลัก

ทำไมมันเกิดขึ้น

ฉันต้องทำเสมอ:

git branch
git checkout master

ฉันจะแน่ใจได้อย่างไรว่า submodule ของฉันชี้ไปที่สาขาหลักเสมอ


1
คุณอ่านคำตอบนี้หรือไม่? stackoverflow.com/questions/1777854/…
Johnny Z

@bitoiu ฉันดูทรีย่อยและ Google Repo ฉันยังไม่มีวิธีแก้ปัญหาที่สมบูรณ์แบบ :(
om471987

1
ประสบการณ์ของฉันกับ gitsubmodules ในสภาพแวดล้อม CI น่ากลัวบางทีคนอื่นอาจมีประสบการณ์ที่ดีกว่า
bitoiu

@ JohnnyZ ขอบคุณ ฉันเข้าใจว่าการส่งคะแนนไปยังการคอมมิชชันและไม่ใช่หัวของต้นไม้ แต่ทำไมแยกจากสาขา หากคุณมีสาขาหนึ่งสาขาคุณไม่ควรแนบสาขาดังกล่าวโดยค่าเริ่มต้น
om471987

3
อย่าเร็วเกินไปที่จะเลิกเรียนรู้เพียงเพราะคุณได้ยินมาว่าพวกเขาไม่ดี นี่เป็นโซลูชันที่ไม่ดีหากคุณต้องการรวมอย่างต่อเนื่อง แต่เป็นโซลูชันที่สมบูรณ์แบบหากคุณต้องการฝังโค้ดจากโครงการภายนอกและคุณจัดการการดึงทั้งหมดอย่างชัดเจน นี่เป็นวิธีปฏิบัติที่ดีที่สุดหากคุณกำลังผสานรวมกับโมดูลที่ไม่ได้แยกที่ไม่ได้ควบคุมโดยองค์กรของคุณ ปัญหาคือพวกเขาเป็นทางออกที่ดึงดูดในทุกสถานการณ์อื่น ๆ ที่พวกเขาไม่ได้ทำงานได้ดีเลย คำแนะนำที่ดีที่สุดคือการอ่านวิธีการทำงานและประเมินสถานการณ์ของคุณ
Sarah G

คำตอบ:


176

แก้ไข:

ดู@Simba Answerสำหรับวิธีการแก้ไขที่ถูกต้อง

submodule.<name>.updateคือสิ่งที่คุณต้องการเปลี่ยนแปลงให้ดูเอกสาร - ค่าเริ่มต้นcheckout
submodule.<name>.branchระบุสาขาระยะไกลที่จะติดตาม - ค่าเริ่มต้นmaster


คำตอบเดิม:

โดยส่วนตัวแล้วฉันเกลียดคำตอบที่นี่ซึ่งนำไปยังลิงก์ภายนอกซึ่งอาจหยุดทำงานตลอดเวลาและตรวจสอบคำตอบของฉันที่นี่ (ยกเว้นคำถามซ้ำกัน) - นำไปสู่คำถามที่ครอบคลุมหัวข้อระหว่างบรรทัดของเรื่องอื่น แต่เท่ากับโดยรวม: "ฉัน ไม่ตอบอ่านเอกสาร "

กลับไปที่คำถาม: ทำไมมันเกิดขึ้น

สถานการณ์ที่คุณอธิบาย

หลังจากดึงการเปลี่ยนแปลงจากเซิร์ฟเวอร์หลาย ๆ ครั้งหัว submodule ของฉันจะถูกถอดออกจากสาขาหลัก

นี้เป็นกรณีที่พบเมื่อไม่ได้ใช้submodulesบ่อยเกินไปหรือได้เริ่มต้นเพียงกับsubmodules ฉันเชื่อว่าฉันถูกต้องในการระบุว่าเราทุกคน เคยมาถึงจุดที่หัวของsubmoduleของเราถูกถอดออก

  • สาเหตุ: submodule ของคุณไม่ได้ติดตามสาขาที่ถูกต้อง (ต้นแบบเริ่มต้น)
    การแก้ไข: ตรวจสอบให้แน่ใจว่า submodule ของคุณกำลังติดตามสาขาที่ถูกต้อง
$ cd <submodule-path>
# if the master branch already exists locally:
# (From git docs - branch)
# -u <upstream>
# --set-upstream-to=<upstream>
#    Set up <branchname>'s tracking information so <upstream>
#    is considered <branchname>'s upstream branch.
#    If no <branchname> is specified, then it defaults to the current branch.
$ git branch -u <origin>/<branch> <branch>
# else:
$ git checkout -b <branch> --track <origin>/<branch>
  • สาเหตุ: repo หลักของคุณไม่ได้กำหนดค่าให้ติดตามสาขาย่อย
    การแก้ไข: ทำให้ submodule ของคุณติดตามสาขาระยะไกลโดยการเพิ่ม submodules ใหม่ด้วยสองคำสั่งต่อไป
    • <branch>ครั้งแรกที่คุณบอกคอมไพล์ในการติดตามระยะไกลของคุณ
    • คุณบอกคอมไพล์ให้ทำการรีบูทหรือรวมการชำระเงินแทน
    • คุณบอกคอมไพล์ให้อัปเดต submodule ของคุณจากระยะไกล
    $ git submodule add -b <branch> <repository> [<submodule-path>]
    $ git config -f .gitmodules submodule.<submodule-path>.update rebase
    $ git submodule update --remote
  • หากคุณยังไม่ได้เพิ่ม submodule ที่มีอยู่เช่นนี้คุณสามารถแก้ไขได้อย่างง่ายดาย:
    • ก่อนอื่นคุณต้องตรวจสอบให้แน่ใจว่า submodule ของคุณมีสาขาที่ชำระเงินซึ่งคุณต้องการติดตาม
    $ cd <submodule-path>
    $ git checkout <branch>
    $ cd <parent-repo-path>
    # <submodule-path> is here path releative to parent repo root
    # without starting path separator
    $ git config -f .gitmodules submodule.<submodule-path>.branch <branch>
    $ git config -f .gitmodules submodule.<submodule-path>.update <rebase|merge>

ในกรณีทั่วไปคุณได้ทำการแก้ไขโดยในตอนนี้ DETACHED HEAD เนื่องจากเกี่ยวข้องกับหนึ่งในปัญหาการกำหนดค่าด้านบน

แก้ไขถอดหัวเมื่อ .update = checkout

$ cd <submodule-path> # and make modification to your submodule
$ git add .
$ git commit -m"Your modification" # Let's say you forgot to push it to remote.
$ cd <parent-repo-path>
$ git status # you will get
Your branch is up-to-date with '<origin>/<branch>'.
Changes not staged for commit:
    modified:   path/to/submodule (new commits)
# As normally you would commit new commit hash to your parent repo
$ git add -A
$ git commit -m"Updated submodule"
$ git push <origin> <branch>.
$ git status
Your branch is up-to-date with '<origin>/<branch>'.
nothing to commit, working directory clean
# If you now update your submodule
$ git submodule update --remote
Submodule path 'path/to/submodule': checked out 'commit-hash'
$ git status # will show again that (submodule has new commits)
$ cd <submodule-path>
$ git status
HEAD detached at <hash>
# as you see you are DETACHED and you are lucky if you found out now
# since at this point you just asked git to update your submodule
# from remote master which is 1 commit behind your local branch
# since you did not push you submodule chage commit to remote. 
# Here you can fix it simply by. (in submodules path)
$ git checkout <branch>
$ git push <origin>/<branch>
# which will fix the states for both submodule and parent since 
# you told already parent repo which is the submodules commit hash 
# to track so you don't see it anymore as untracked.

แต่ถ้าคุณทำการเปลี่ยนแปลงบางอย่างในเครื่องแล้วสำหรับ submodule และคอมมิชชัน, ผลักมันไปที่รีโมตเมื่อคุณเรียกใช้ 'git checkout' Git จะแจ้งให้คุณทราบ:

$ git checkout <branch>
Warning: you are leaving 1 commit behind, not connected to any of your branches:
If you want to keep it by creating a new branch, this may be a good time to do so with:

ตัวเลือกที่แนะนำในการสร้างสาขาชั่วคราวอาจทำได้ดีแล้วคุณสามารถรวมสาขาเหล่านี้เข้าด้วยกันได้อย่างไรก็ตามฉันจะใช้ส่วนตัวgit cherry-pick <hash>ในกรณีนี้

$ git cherry-pick <hash> # hash which git showed you related to DETACHED HEAD
# if you get 'error: could not apply...' run mergetool and fix conflicts
$ git mergetool
$ git status # since your modifications are staged just remove untracked junk files
$ rm -rf <untracked junk file(s)>
$ git commit # without arguments
# which should open for you commit message from DETACHED HEAD
# just save it or modify the message.
$ git push <origin> <branch>
$ cd <parent-repo-path>
$ git add -A # or just the unstaged submodule
$ git commit -m"Updated <submodule>"
$ git push <origin> <branch>

แม้ว่าจะมีอีกหลายกรณีที่คุณสามารถทำให้ซับโดมิโนของคุณเข้าสู่สถานะถอดออกได้ แต่ฉันหวังว่าคุณจะเข้าใจวิธีแก้ปัญหากรณีของคุณได้อีกเล็กน้อย


2
HEAD git submodule update --remoteแฝดเป็นพฤติกรรมปกติของ โปรดดูคำตอบของซิมบ้าฉันคิดว่านั่นควรเป็นคำตอบที่ถูกต้อง
magomar

78

การเพิ่มbranchตัวเลือกในการ.gitmoduleเป็นที่ไม่เกี่ยวข้องกับพฤติกรรมของแฝด submodules ที่ทั้งหมด คำตอบเก่าจาก @mkungla ไม่ถูกต้องหรือล้าสมัย

จากgit submodule --help, HEAD แฝดเป็นพฤติกรรมปกติgit submodule update --remoteของ

แรกมีไม่จำเป็นต้องระบุสาขาที่จะได้รับการติดตาม origin/masterเป็นสาขาเริ่มต้นที่จะติดตาม

--remote

แทนที่จะใช้ SHA-1 ที่บันทึกไว้ของ superproject เพื่ออัปเดต submodule ให้ใช้สถานะของสาขาการติดตามระยะไกลของ submodule ที่ใช้ระยะไกลเป็นระยะไกลสาขา ( branch.<name>.remote) ผิดนัด originสาขาระยะไกลใช้ค่าเริ่มต้นmaster

ทำไม

เหตุใดจึงต้องเป็นหัวหน้าแฝดหลังupdate? นี้เกิดจากพฤติกรรมการปรับปรุงโมดูลค่าเริ่มต้น:checkout

--เช็คเอาท์

ชำระเงินการกระทำที่บันทึกไว้ในซูเปอร์โปรเจ็กต์บนHEAD ที่ดึงออกมาใน submodule นี่คือการทำงานเริ่มต้นการใช้งานหลักของตัวเลือกนี้คือการแทนที่เมื่อตั้งค่าอื่นที่ไม่ใช่submodule.$name.updatecheckout

เพื่ออธิบายพฤติกรรมการอัพเดทแปลก ๆ นี้เราต้องเข้าใจว่า submodules ทำงานอย่างไร?

อ้างอิงจากเริ่มต้นด้วย Submodules ในหนังสือPro Git

แม้ว่า sbmodule DbConnectorเป็นไดเรกทอรีย่อยในไดเรกทอรีทำงานของคุณ Git เห็นว่าเป็น submodule และไม่ติดตามเนื้อหาเมื่อคุณไม่ได้อยู่ในไดเรกทอรีนั้น แต่ Git มองว่ามันเป็นการกระทำเฉพาะจากแหล่งเก็บข้อมูลนั้น

ซื้อคืนภาคหลักติดตาม submodule กับรัฐที่จุดที่เฉพาะเจาะจงการกระทำ ID ดังนั้นเมื่อคุณอัปเดตโมดูลคุณกำลังอัปเดตรหัสยืนยันเป็นรหัสใหม่

อย่างไร

หากคุณต้องการ submodule รวมกับสาขาที่ห่างไกลโดยอัตโนมัติใช้หรือ--merge--rebase

--ผสาน

ตัวเลือกนี้ใช้ได้สำหรับคำสั่งupdateเท่านั้น รวมการกระทำที่บันทึกไว้ใน superproject เข้าไปในสาขาปัจจุบันของ submodule ถ้าตัวเลือกนี้จะได้รับ HEAD submodule นั้นจะไม่ถูกถอดออก

--rebase

Rebase สาขาปัจจุบันไปที่การบันทึกที่บันทึกไว้ใน superproject ถ้าตัวเลือกนี้จะได้รับ HEAD submodule นั้นจะไม่ถูกถอดออก

สิ่งที่คุณต้องทำคือ

git submodule update --remote --merge
# or
git submodule update --remote --rebase

นามแฝงที่แนะนำ:

git config alias.supdate 'submodule update --remote --merge'

# do submodule update with
git supdate

นอกจากนี้ยังมีตัวเลือกที่จะทำให้--mergeหรือ--rebaseพฤติกรรมเริ่มต้นของgit submodule updateโดยการตั้งค่าsubmodule.$name.updateการหรือmergerebase

นี่คือตัวอย่างที่เกี่ยวกับวิธีการปรับแต่งพฤติกรรมการปรับปรุงการเริ่มต้นของการปรับปรุง submodule .gitmoduleใน

[submodule "bash/plugins/dircolors-solarized"]
    path = bash/plugins/dircolors-solarized
    url = https://github.com/seebi/dircolors-solarized.git
    update = merge # <-- this is what you need to add

หรือกำหนดค่าในบรรทัดคำสั่ง

# replace $name with a real submodule name
git config -f .gitmodules submodule.$name.update merge

อ้างอิง


6
ฉันใช้git submodule update --remote --mergeและมันจะดึงซับโดคูลลงในสถานะเดี่ยว ลอง--rebaseด้วยผลลัพธ์เดียวกัน
Joe Strout

8
@JoeStrout หาก submodule ของคุณถูกถอดออกแล้วให้แก้ไขสถานะที่แยกออกก่อนที่จะทำการอัปเดตด้วยคำสั่งข้างต้น cdลงใน submodule, เช็คเอาต์ submodule ไปยังสาขาเฉพาะด้วย, git checkout master.
สิงโต

2
หรือ - หากเป็นเรื่องยุ่งยากเกินไปสำหรับ submodules (เรียกซ้ำ) หลายอัน - ทำgit submodule foreach --recursive git checkout masterง่าย
stefanct

1
ฉันเข้าใจเพียงบางส่วนถึงคำอธิบาย "วิธีการทำงานของ git" TBH ฉันไม่สนใจที่จะเข้าใจวิธีการใช้งานคอมไพล์ฉันแค่ต้องการใช้มัน ตอนนี้ฉันเข้าใจแล้วว่าฉันสามารถแก้ไข submodules เดี่ยวgit submodule foreach --recursive git checkout masterๆ ได้ แต่ฉันจะป้องกันไม่ให้คอมไพล์ปลดพวกเขาออกได้อย่างไร การตั้งค่าตัวเลือกการกำหนดค่าสำหรับแต่ละ submoduleไม่ใช่ตัวเลือก!
Nicolas

สำหรับฉันแล้วการวิ่งgit submodule update --remote --mergeไม่ได้ออกจาก submodule ในสถานะ HEAD เดี่ยว แต่ทำงานgit submodule updateหลังจากแก้ไข.gitmoduleไฟล์ของฉันตามที่คุณระบุ DID ปล่อย submodule ในสถานะ HEAD เดี่ยว
bweber13

41

ฉันเบื่อที่จะถอดมันออกมาตลอดดังนั้นฉันจึงใช้เชลล์สคริปต์เพื่อสร้างมันขึ้นมาสำหรับโมดูลทั้งหมดของฉัน ฉันคิดว่าทั้งหมด submodules อยู่ใน master: นี่คือสคริปต์:

#!/bin/bash
echo "Good Day Friend, building all submodules while checking out from MASTER branch."

git submodule update 
git submodule foreach git checkout master 
git submodule foreach git pull origin master 

รันจากโมดูลแม่ของคุณ


2
git submodule foreach git pull origin master - นี่คือสิ่งที่ฉันกำลังมองหา .. kudos
csomakk

ง่ายและรัดกุม! ขอบคุณ!
zhekaus

12

ตรวจสอบคำตอบของฉันที่นี่: Git submodules: ระบุสาขา / แท็ก

หากคุณต้องการคุณสามารถเพิ่มบรรทัด "branch = master" ลงในไฟล์. gitmodules ของคุณได้ด้วยตนเอง อ่านลิงค์เพื่อดูว่าฉันหมายถึงอะไร

แก้ไข: เพื่อติดตามโครงการ submodule ที่มีอยู่ที่สาขาทำตามคำแนะนำของ VonC ที่นี่แทน:

Git submodules: ระบุสาขา / แท็ก


14
คำตอบควรจะเป็นแบบอินไลน์ IIRC ที่เชื่อมโยงกับคำตอบคือ Stack Overflow faux pas
Tony Topper

1
@TonyTopper แม้เพียงเชื่อมโยงไปยังคำตอบอื่น ๆ IIRC มีเพียงลิงก์ภายนอกที่ขมวดคิ้วเนื่องจากลิงก์เหล่านี้อาจหายไปจากนั้นลิงก์จะตายและคำตอบก็คือไร้ประโยชน์ แต่ไม่มีอันตรายใด ๆ กับคำตอบ SO พวกเขาจะไม่หายไปไหนเว้นแต่จะหายไป (และสามารถกู้คืนได้ไม่ว่าจะเกิดอะไรขึ้น) นอกจากนี้เขายังได้ตอบคำถามตามที่branch = master" line into your .gitmoduleจริงแล้วคำตอบแบบเต็มก็แก้ปัญหานั้นให้ฉันได้
Mecki

9

วิธีอื่นในการทำให้ submodule ของคุณเพื่อตรวจสอบสาขาคือไปที่.gitmodulesไฟล์ในโฟลเดอร์ root และเพิ่มฟิลด์branchในการกำหนดค่าโมดูลดังต่อไปนี้:

branch = <branch-name-you-want-module-to-checkout>


15
สำหรับฉันมันไม่ทำงาน ฉันตั้งค่าไว้ถูกต้องbranch = my_wanted_branchแล้ว แต่การทำงานgit submodule update --remoteมันยังคงตรวจสอบว่าเป็นหัวเดี่ยว
Andrius

ทำสิ่งนี้แล้ว cd sudmodule & git co thebranche & cd .. จากนั้น git submodule update --remote และใช้งานได้!
pdem

ไม่ใช่หรือว่า '.gitmodules' อยู่ภายใต้การใช้งาน (กำลังอ่าน) เท่านั้นในขณะที่ superproject กำลังถูกโคลนในแบบ submodules-recursive หรือเริ่มต้น submodule? กล่าวอีกนัยหนึ่งที่เก็บข้อมูลของคุณเองคุณจะอัปเดตไฟล์ที่ไม่ได้ทำกำไรจากการอัพเดทการตั้งค่า submodule เสมอไปที่ '.gitmodules' ในความเข้าใจของฉัน '.gitmodules' เป็นเทมเพลตสำหรับการกำหนดค่าที่สร้างขึ้นในขณะที่ repo ถูกโคลนหรือดังนั้น
Na13-c

3

อย่างที่คนอื่น ๆ พูดเหตุผลที่สิ่งนี้เกิดขึ้นก็คือว่าผู้ปกครอง repo มีเพียงการอ้างอิงถึง (SHA1 ของ) ความมุ่งมั่นที่เฉพาะเจาะจงใน submodule - มันไม่รู้อะไรเกี่ยวกับสาขา นี่คือวิธีที่ควรใช้งาน: สาขาที่ได้รับมอบหมายอาจย้ายไปข้างหน้า (หรือข้างหลัง) และถ้า repo แม่ได้อ้างอิงสาขาแล้วมันอาจแตกง่ายเมื่อเกิดขึ้น

อย่างไรก็ตามโดยเฉพาะอย่างยิ่งถ้าคุณกำลังพัฒนาทั้งใน repo หลักและ submodule detached HEADสถานะอาจทำให้สับสนและอาจเป็นอันตราย หากคุณทำคอมมิตในขณะที่อยู่ในdetached HEADสถานะสิ่งเหล่านี้จะห้อยต่องแต่งและคุณอาจสูญเสียงานของคุณได้อย่างง่ายดาย (โดยทั่วไปการกระทำที่มุ่งมั่นสามารถช่วยได้โดยใช้git reflogแต่จะดีกว่ามากในการหลีกเลี่ยงในตอนแรก)

หากคุณเป็นเหมือนฉันแล้วส่วนใหญ่หากมีสาขาในซับโดลูตที่ชี้ไปที่การส่งกำลังออกคุณควรจะตรวจสอบสาขานั้นมากกว่าอยู่ในสถานะ HEAD เดี่ยวที่กระทำเดียวกัน คุณสามารถทำได้โดยเพิ่มนามแฝงต่อไปนี้ในgitconfigไฟล์ของคุณ:

[alias]
    submodule-checkout-branch = "!f() { git submodule -q foreach 'branch=$(git branch --no-column --format=\"%(refname:short)\" --points-at `git rev-parse HEAD` | grep -v \"HEAD detached\" | head -1); if [[ ! -z $branch && -z `git symbolic-ref --short -q HEAD` ]]; then git checkout -q \"$branch\"; fi'; }; f"

ตอนนี้หลังจากทำgit submodule updateคุณเพียงแค่ต้องโทรgit submodule-checkout-branchและ submodule ใด ๆ ที่มีการตรวจสอบที่กระทำซึ่งมีสาขาที่ชี้ไปที่มันจะตรวจสอบสาขาที่ หากคุณไม่ได้มีสาขาท้องถิ่นหลายสาขาที่ชี้ไปที่การกระทำเดียวกันสิ่งนี้มักจะทำในสิ่งที่คุณต้องการ ถ้าไม่อย่างน้อยก็จะทำให้มั่นใจได้ว่าการกระทำใด ๆ ที่คุณทำไปสู่สาขาที่แท้จริงแทนที่จะถูกทิ้งให้ห้อยต่องแต่ง

นอกจากนี้หากคุณได้ตั้งค่าคอมไพล์เพื่ออัปเดต submodules โดยอัตโนมัติเมื่อใช้ ( git config --global submodule.recurse trueดูคำตอบนี้ ) คุณสามารถสร้าง hook post-checkout ที่เรียกนามแฝงนี้โดยอัตโนมัติ:

$ cat .git/hooks/post-checkout 
#!/bin/sh
git submodule-checkout-branch

จากนั้นคุณไม่จำเป็นต้องโทรgit submodule updateหรือgit submodule-checkout-branchทำเพียงแค่git checkoutอัปเดตข้อมูลทั้งหมดเป็นข้อผูกพันและตรวจสอบสาขาที่เกี่ยวข้อง (ถ้ามี)


0

ทางออกที่ง่ายที่สุดคือ:

git clone --recursive git@github.com:name/repo.git

จากนั้น cd ในไดเรกทอรี repo และ:

git submodule update --init
git submodule foreach -q --recursive 'git checkout $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'
git config --global status.submoduleSummary true

อ่านเพิ่มเติม: Git submodules ปฏิบัติที่ดีที่สุด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.