Tại sao Gm Submodule Head của tôi tách ra khỏi chủ?


162

Tôi đang sử dụng mô đun con Git. Sau khi lấy các thay đổi từ máy chủ, nhiều lần đầu mô đun con của tôi bị tách ra khỏi nhánh chính.

Tại sao nó xảy ra?

Tôi phải luôn luôn làm:

git branch
git checkout master

Làm thế nào tôi có thể chắc chắn rằng mô hình con của tôi luôn luôn trỏ đến nhánh chính?


1
Bạn đã đọc câu trả lời này? stackoverflow.com/questions/1777854/ Mạnh
Johnny Z

@bitoiu Tôi đã xem cây con và Google Repo. Tôi chưa có giải pháp hoàn hảo nào :(
om471987

1
trải nghiệm của tôi với gitsubmodules, trong môi trường CI là khủng khiếp, có thể một số người khác có trải nghiệm tốt hơn.
bitoiu

@JohnnyZ Cảm ơn. Tôi hiểu rằng mô hình con trỏ đến một cam kết và không phải đầu cây. Nhưng tại sao lại tách ra khỏi chi nhánh. Nếu bạn có một nhánh thì không nên gắn nó vào nhánh đó
om471987

3
Đừng quá nhanh chóng để loại bỏ các mô hình con chỉ vì bạn nghe rằng chúng xấu. Chúng là một giải pháp kém nếu bạn muốn tích hợp liên tục, nhưng chúng là một giải pháp gần như hoàn hảo nếu bạn muốn nhúng mã từ một dự án bên ngoài và bạn quản lý rõ ràng tất cả các lần kéo. Đây thường là cách thực hành tốt nhất nếu bạn đang tích hợp với một mô-đun không rẽ nhánh mà không được kiểm soát bởi tổ chức của bạn. Vấn đề là chúng là một giải pháp hấp dẫn trong tất cả các loại tình huống khác mà chúng không hoạt động tốt lắm. Lời khuyên tốt nhất là đọc lên cách họ làm việc và đánh giá kịch bản của bạn.
Sarah G

Câu trả lời:


176

BIÊN TẬP:

Xem @Simba Trả lời cho giải pháp hợp lệ

submodule.<name>.updatelà những gì bạn muốn thay đổi, hãy xem tài liệu - mặc địnhcheckout
submodule.<name>.branch chỉ định nhánh từ xa sẽ được theo dõi - mặc địnhmaster


TRẢ LỜI:

Cá nhân tôi ghét câu trả lời ở đây liên kết trực tiếp với các liên kết bên ngoài có thể ngừng hoạt động theo thời gian và kiểm tra câu trả lời của tôi ở đây (Trừ khi câu hỏi bị trùng lặp) - chuyển đến câu hỏi bao gồm chủ đề giữa các dòng của chủ đề khác, nhưng tổng thể bằng: "Tôi không trả lời, đọc tài liệu. "

Vì vậy, trở lại câu hỏi: Tại sao nó xảy ra?

Tình huống bạn mô tả

Sau khi lấy các thay đổi từ máy chủ, nhiều lần đầu mô đun con của tôi bị tách ra khỏi nhánh chính.

Đây là một trường hợp phổ biến khi một người không sử dụng các mô hình con quá thường xuyên hoặc chỉ bắt đầu với các mô hình con . Tôi tin rằng tôi đã đúng khi tuyên bố rằng tất cả chúng ta đã ở đó tại một thời điểm mà phần đầu của mô hình con của chúng ta bị tách ra.

  • Nguyên nhân: Mô hình con của bạn không theo dõi nhánh chính xác (chủ mặc định).
    Giải pháp: Hãy chắc chắn rằng mô hình con của bạn đang theo dõi nhánh chính xác
$ 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>
  • Nguyên nhân: Repo cha của bạn không được cấu hình để theo dõi nhánh con.
    Giải pháp: Làm cho mô hình con của bạn theo dõi nhánh từ xa của nó bằng cách thêm các mô hình con mới với hai lệnh sau.
    • Đầu tiên bạn nói với git để theo dõi từ xa của bạn <branch>.
    • bạn bảo git thực hiện rebase hoặc hợp nhất thay vì thanh toán
    • bạn nói với git để cập nhật mô hình con của bạn từ xa.
    $ git submodule add -b <branch> <repository> [<submodule-path>]
    $ git config -f .gitmodules submodule.<submodule-path>.update rebase
    $ git submodule update --remote
  • Nếu bạn chưa thêm mô hình con hiện tại của mình như thế này, bạn có thể dễ dàng khắc phục điều đó:
    • Trước tiên, bạn muốn đảm bảo rằng mô hình con của bạn có nhánh được kiểm tra mà bạn muốn được theo dõi.
    $ 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>

Trong các trường hợp phổ biến, hiện tại bạn đã sửa lỗi ĐẦU XÁC ĐỊNH của mình vì nó có liên quan đến một trong các vấn đề cấu hình ở trên.

sửa lỗi DETACHED Head khi .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.

Nhưng nếu bạn đã quản lý để thực hiện một số thay đổi cục bộ cho mô hình con và được cam kết, hãy đẩy chúng vào điều khiển từ xa sau đó khi bạn thực hiện 'kiểm tra git', Git thông báo cho bạn:

$ 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:

Tùy chọn được đề xuất để tạo một nhánh tạm thời có thể tốt, và sau đó bạn chỉ có thể hợp nhất các nhánh này, v.v. Tuy nhiên, cá nhân tôi sẽ chỉ sử dụng git cherry-pick <hash>trong trường hợp này.

$ 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>

Mặc dù có một số trường hợp nữa bạn có thể đưa các mô hình con của mình vào trạng thái ĐẦU XÁC ĐỊNH, tôi hy vọng rằng bây giờ bạn hiểu thêm một chút về cách gỡ lỗi trường hợp cụ thể của bạn.


2
ĐẦU tách ra là hành vi mặc định của git submodule update --remote. Xin hãy xem câu trả lời của Simba, tôi nghĩ đó nên là câu trả lời đúng.
magomar

77

Thêm một branchlựa chọn trong .gitmoduleKHÔNG liên quan đến hành vi vô tư của các môđun con chút nào. Câu trả lời cũ từ @mkungla là không chính xác, hoặc lỗi thời.

Từ git submodule --help, ĐẦU tách ra là hành vi mặc định của git submodule update --remote.

Đầu tiên, không cần chỉ định một chi nhánh sẽ được theo dõi . origin/masterlà nhánh mặc định được theo dõi.

--Xa xôi

Thay vì sử dụng SHA-1 được ghi lại của siêu dự án để cập nhật mô hình con, hãy sử dụng trạng thái của nhánh theo dõi từ xa của mô hình con. Điều khiển từ xa được sử dụng là remote của chi nhánh ( branch.<name>.remote), mặc định làorigin . Các chi nhánh từ xa được sử dụng mặc định đểmaster .

Tại sao

Vậy tại sao ĐẦU bị tách ra sau update? Điều này được gây ra bởi hành vi cập nhật mô-đun mặc định :checkout .

--Thủ tục thanh toán

Kiểm tra các cam kết được ghi trong siêu dự án trên một đầu tách rời trong mô hình con. Đây là hành vi mặc định , công dụng chính của tùy chọn này là ghi đè submodule.$name.updatekhi được đặt thành giá trị khác checkout.

Để giải thích hành vi cập nhật kỳ lạ này, chúng ta cần hiểu làm thế nào các mô đun con hoạt động?

Trích dẫn từ Bắt đầu với Submodules trong cuốn sách Pro Git

Mặc dù sbmodule DbConnectorlà thư mục con trong thư mục làm việc của bạn, Git xem nó như một mô hình con và không theo dõi nội dung của nó khi bạn không ở trong thư mục đó. Thay vào đó, Git xem nó như một cam kết cụ thể từ kho lưu trữ đó .

Repo chính theo dõi mô hình con với trạng thái của nó tại một điểm cụ thể , id xác nhận . Vì vậy, khi bạn cập nhật các mô-đun, bạn đang cập nhật id xác nhận sang một mô-đun mới.

Làm sao

Nếu bạn muốn mô hình con được hợp nhất với nhánh từ xa tự động, hãy sử dụng --mergehoặc --rebase.

-

Tùy chọn này chỉ hợp lệ cho lệnh cập nhật . Hợp nhất các cam kết được ghi trong siêu dự án vào nhánh hiện tại của mô hình con. Nếu tùy chọn này được đưa ra, ĐẦU của mô hình con sẽ không bị tách ra .

- cơ sở

Khởi động lại nhánh hiện tại vào cam kết được ghi trong siêu dự án. Nếu tùy chọn này được đưa ra, ĐẦU của mô hình con sẽ không bị tách ra .

Tất cả bạn cần làm là,

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

Bí danh được đề xuất:

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

# do submodule update with
git supdate

Ngoài ra còn có một tùy chọn để thực hiện --mergehoặc --rebasenhư hành vi mặc định của git submodule update, bằng cách đặt submodule.$name.updatethành mergehoặc rebase.

Dưới đây là một ví dụ về cách định cấu hình hành vi cập nhật mặc định của cập nhật mô hình con trong .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

Hoặc cấu hình nó trong dòng lệnh,

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

Người giới thiệu


6
Tôi sử dụng git submodule update --remote --merge, và nó kéo xuống mô hình con trong trạng thái tách rời. Cũng đã thử --rebasevới kết quả tương tự.
Joe Strout

8
@JoeStrout Nếu mô hình con của bạn đã được tách ra, hãy sửa trạng thái tách ra trước khi bạn thực hiện cập nhật với các lệnh trên. cdvào mô hình con, kiểm tra mô hình con đến một nhánh cụ thể với , git checkout master.
Simba

2
Hoặc - nếu điều này là quá nhiều rắc rối cho nhiều mô hình con (đệ quy) - chỉ cần làm git submodule foreach --recursive git checkout master.
stefanct

1
Tôi chỉ hiểu một phần mô tả "cách hoạt động của git". TBH Tôi không thực sự quan tâm đến việc hiểu cách thức hoạt động của git, tôi chỉ muốn sử dụng nó. Bây giờ tôi hiểu rằng tôi có thể cố định các mô đun con tách rời với git submodule foreach --recursive git checkout master. Nhưng làm thế nào tôi có thể ngăn git luôn tách chúng ra? Đặt tùy chọn cấu hình cho mỗi mô hình con không phải là một tùy chọn!
Nicolas

Đối với tôi, việc chạy git submodule update --remote --mergekhông để lại mô hình con ở trạng thái CHÍNH tách rời, nhưng chạy git submodule updatesau khi chỉnh sửa .gitmoduletệp của tôi khi bạn chỉ ra DID để lại mô hình con ở trạng thái CHÍNH tách rời.
bweber13

41

Tôi cảm thấy mệt mỏi vì nó luôn bị tách ra vì vậy tôi chỉ sử dụng một kịch bản shell để xây dựng nó cho tất cả các mô-đun của mình. tôi giả sử tất cả các mô hình con là trên master: đây là kịch bản:

#!/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 

thực hiện nó từ mô-đun cha của bạn


2
git Subodule foreach git pull origin master - đây là thứ tôi đang tìm kiếm .. kudos
csomakk

đơn giản và súc tích! Cảm ơn!
zhekaus

12

Kiểm tra câu trả lời của tôi ở đây: Các mô đun con Git: Chỉ định một nhánh / thẻ

Nếu bạn muốn, bạn có thể thêm dòng "nhánh = master" vào tệp .gitmodules của mình theo cách thủ công. Đọc các liên kết để xem những gì tôi có ý nghĩa.

EDIT: Để theo dõi một dự án mô hình con hiện có tại một chi nhánh, hãy làm theo hướng dẫn của VonC tại đây:

Các mô đun con Git: Chỉ định một nhánh / thẻ


14
Câu trả lời được cho là nội tuyến; IIRC liên kết đến câu trả lời là một trò chơi giả mạo Stack Overflow.
Tony Topper

1
@TonyTopper Ngay cả khi chỉ liên kết với một câu trả lời SO khác? IIRC chỉ các liên kết bên ngoài được tán thành vì những liên kết này có thể biến mất và sau đó liên kết đã chết và câu trả lời là, tốt, vô dụng. Tuy nhiên, không có mối nguy hiểm nào với câu trả lời SO, chúng sẽ không bao giờ biến mất, trừ khi SO biến mất (và có thể được khôi phục cho dù điều gì xảy ra). Ngoài ra anh ấy đã trả lời câu hỏi, branch = master" line into your .gitmodulethực tế là câu trả lời đầy đủ, đã giải quyết vấn đề đó cho tôi.
Mecki

9

Một cách khác để tạo mô hình con của bạn để kiểm tra nhánh là đi đến .gitmodulestệp trong thư mục gốc và thêm trường branchtrong cấu hình mô-đun như sau:

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


15
Đối với tôi điều này không hoạt động. Tôi đã thiết lập chính xác branch = my_wanted_branch. Nhưng chạy git submodule update --remotenó vẫn kiểm tra như tách ra khỏi đầu.
Andrius

Làm điều này, sau đó cd sudmodule & git co thebranche & cd .., sau đó git subodule update --remote và nó hoạt động!
pdem

Không phải đó là cách '.gitmodules' đang được sử dụng tích cực (đang được đọc) chỉ trong khi siêu dự án đang được nhân bản theo cách đệ quy hoặc mô hình con được khởi tạo? Nói cách khác, kho lưu trữ của riêng bạn, bạn cập nhật tệp không phải lúc nào cũng thu được lợi nhuận từ các bản cập nhật cấu hình mô đun con được đặt thành '.gitmodules'. Theo hiểu biết của tôi, '.gitmodules' là một mẫu cho cấu hình được tạo trong khi repo đang được sao chép hoặc hơn thế.
Na13-c

3

Như những người khác đã nói, lý do điều này xảy ra là repo gốc chỉ chứa một tham chiếu đến (SHA1 của) một cam kết cụ thể trong mô hình con - nó không biết gì về các nhánh. Đây là cách nó hoạt động: chi nhánh tại cam kết đó có thể đã di chuyển về phía trước (hoặc ngược lại) và nếu repo cha đã tham chiếu chi nhánh thì nó có thể dễ dàng bị phá vỡ khi điều đó xảy ra.

Tuy nhiên, đặc biệt nếu bạn đang tích cực phát triển trong cả repo cha và mô hình con, detached HEADtrạng thái có thể gây nhầm lẫn và có khả năng nguy hiểm. Nếu bạn thực hiện các cam kết trong mô hình con trong khi nó ở detached HEADtrạng thái, chúng sẽ trở nên lơ lửng và bạn có thể dễ dàng mất công việc của mình. (Cam kết độc hại thường có thể được cứu bằng cách sử dụng git reflog, nhưng tốt hơn hết là tránh chúng ngay từ đầu.)

Nếu bạn giống tôi, thì hầu hết thời gian nếu có một nhánh trong mô hình con trỏ đến cam kết được kiểm tra, bạn thà kiểm tra nhánh đó hơn là ở trạng thái CHÍNH tách ra ở cùng một cam kết. Bạn có thể làm điều này bằng cách thêm bí danh sau vào gitconfigtệp của mình :

[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"

Bây giờ, sau khi thực hiện, git submodule updatebạn chỉ cần gọi git submodule-checkout-branchvà bất kỳ mô hình con nào được kiểm tra tại một cam kết có một nhánh trỏ đến nó sẽ kiểm tra nhánh đó. Nếu bạn không thường xuyên có nhiều chi nhánh địa phương tất cả chỉ vào cùng một cam kết, thì điều này thường sẽ làm những gì bạn muốn; nếu không, thì ít nhất nó sẽ đảm bảo rằng bất kỳ cam kết nào bạn thực hiện trên một nhánh thực tế thay vì bị treo lủng lẳng.

Hơn nữa, nếu bạn đã thiết lập git để tự động cập nhật các mô hình con khi thanh toán (bằng cách sử dụng git config --global submodule.recurse true, hãy xem câu trả lời này ), bạn có thể thực hiện một móc nối sau thanh toán tự động gọi bí danh này:

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

Sau đó, bạn không cần phải gọi một trong hai git submodule updatehoặc git submodule-checkout-branch, chỉ cần làm git checkoutsẽ cập nhật tất cả các môđun con đến cam kết của mình và kiểm tra các ngành tương ứng (nếu có).


0

Giải pháp đơn giản nhất là:

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

Sau đó, cd trong thư mục repo và:

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

Đọc thêm: Thực hành tốt nhất Git .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.