Làm thế nào để tôi hủy bỏ mô hình con git (đưa tất cả mã trở lại vào lõi)?
Như trong cách "nên" tôi, như trong "Thủ tục tốt nhất" ...
git submodule deinit asubmodule ; git rm asubmodule
là đủ, như được minh họa trong câu trả lời của tôi dưới đây
Làm thế nào để tôi hủy bỏ mô hình con git (đưa tất cả mã trở lại vào lõi)?
Như trong cách "nên" tôi, như trong "Thủ tục tốt nhất" ...
git submodule deinit asubmodule ; git rm asubmodule
là đủ, như được minh họa trong câu trả lời của tôi dưới đây
Câu trả lời:
Nếu tất cả những gì bạn muốn là đưa mã mô đun con của bạn vào kho lưu trữ chính, bạn chỉ cần xóa mô hình con và thêm lại các tệp vào repo chính:
git rm --cached submodule_path # delete reference to submodule HEAD (no trailing slash)
git rm .gitmodules # if you have more than one submodules,
# you need to edit this file instead of deleting!
rm -rf submodule_path/.git # make sure you have backup!!
git add submodule_path # will add files instead of commit reference
git commit -m "remove submodule"
Nếu bạn cũng muốn lưu giữ lịch sử của mô hình con, bạn có thể thực hiện một mẹo nhỏ: "hợp nhất" mô hình con vào kho lưu trữ chính để kết quả sẽ giống như trước đây, ngoại trừ các tệp mô hình con hiện đang ở trong kho chính.
Trong mô-đun chính, bạn sẽ cần phải làm như sau:
# Fetch the submodule commits into the main repository
git remote add submodule_origin git://url/to/submodule/origin
git fetch submodule_origin
# Start a fake merge (won't change any files, won't commit anything)
git merge -s ours --no-commit submodule_origin/master
# Do the same as in the first solution
git rm --cached submodule_path # delete reference to submodule HEAD
git rm .gitmodules # if you have more than one submodules,
# you need to edit this file instead of deleting!
rm -rf submodule_path/.git # make sure you have backup!!
git add submodule_path # will add files instead of commit reference
# Commit and cleanup
git commit -m "removed submodule"
git remote rm submodule_origin
Kho lưu trữ kết quả sẽ trông hơi kỳ lạ: sẽ có nhiều hơn một cam kết ban đầu. Nhưng nó sẽ không gây ra vấn đề gì cho git.
Trong giải pháp thứ hai này, bạn sẽ có một lợi thế lớn là bạn vẫn có thể chạy git blame hoặc git log trên các tệp ban đầu trong các mô hình con. Trong thực tế, những gì bạn đã làm ở đây là đổi tên nhiều tệp trong một kho lưu trữ và git sẽ tự động phát hiện điều này. Nếu bạn vẫn gặp vấn đề với nhật ký git, hãy thử một số tùy chọn (--follow, -M, -C) để phát hiện đổi tên / sao chép tốt hơn.
git merge
đảm bảo rằng sẽ có một "cam kết trước" cho mỗi tệp (tại một trong hai "bên" của hợp nhất).
mkdir foo && git mv !(foo) foo && git commit
.
--allow-unrelated-histories
để buộc hợp nhất tại hợp nhất giả như tôi đang nhận fatal: refusing to merge unrelated histories
, thêm ở đây: github.com/git/git/blob/master/Documentation/RelNotes/iêu
Kể từ git 1.8.5 (tháng 11 năm 2013 ) ( không lưu giữ lịch sử của mô hình con ):
mv yoursubmodule yoursubmodule_tmp
git submodule deinit yourSubmodule
git rm yourSubmodule
mv yoursubmodule_tmp yoursubmodule
git add yoursubmodule
Răng se:
deinit
do đó, mv
đầu tiên ),.gitmodules
cho bạnrm
),rm
).Khi việc xóa mô hình con hoàn thành ( deinit
và git rm
), bạn có thể đổi tên thư mục trở lại tên ban đầu và thêm nó vào git repo như một thư mục thông thường.
Lưu ý: nếu mô hình con được tạo bởi một Git cũ (<1.8), bạn có thể cần phải xóa .git
thư mục lồng trong chính mô hình con, như nhận xét của Simon East
Nếu bạn cần giữ lịch sử của mô hình con, hãy xem câu trả lời của jsears , sử dụng .git filter-branch
deinit
một mình làm sạch cây làm việc từ mô hình con của bạn?
Tôi đã tạo một tập lệnh sẽ dịch một mô hình con sang một thư mục đơn giản, trong khi vẫn giữ lại tất cả lịch sử tập tin. Nó không bị các git log --follow <file>
vấn đề mà các giải pháp khác phải chịu. Đây cũng là một cách gọi một dòng rất dễ thực hiện tất cả công việc cho bạn. G'luck.
Nó được xây dựng dựa trên công trình tuyệt vời của Lucas Jenß, được mô tả trong bài đăng trên blog của anh ấy " Tích hợp một mô hình con vào kho lưu trữ mẹ ", nhưng tự động hóa toàn bộ quá trình và dọn sạch một vài trường hợp góc khác.
Mã mới nhất sẽ được duy trì với các lỗi trên github tại https://github.com/jeremysears/scripts/blob/master/bin/git-submodule-rewrite , nhưng vì lợi ích của giao thức trả lời stackoverflow thích hợp, tôi đã đưa vào giải pháp trong toàn bộ dưới đây.
Sử dụng:
$ git-submodule-rewrite <submodule-name>
git-subodule-viết lại:
#!/usr/bin/env bash
# This script builds on the excellent work by Lucas Jenß, described in his blog
# post "Integrating a submodule into the parent repository", but automates the
# entire process and cleans up a few other corner cases.
# https://x3ro.de/2013/09/01/Integrating-a-submodule-into-the-parent-repository.html
function usage(){
echo "Merge a submodule into a repo, retaining file history."
echo "Usage: $0 <submodule-name>"
echo ""
echo "options:"
echo " -h, --help Print this message"
echo " -v, --verbose Display verbose output"
}
function abort {
echo "$(tput setaf 1)$1$(tput sgr0)"
exit 1
}
function request_confirmation {
read -p "$(tput setaf 4)$1 (y/n) $(tput sgr0)"
[ "$REPLY" == "y" ] || abort "Aborted!"
}
function warn() {
cat << EOF
This script will convert your "${sub}" git submodule into
a simple subdirectory in the parent repository while retaining all
contents and file history.
The script will:
* delete the ${sub} submodule configuration from .gitmodules and
.git/config and commit it.
* rewrite the entire history of the ${sub} submodule so that all
paths are prefixed by ${path}.
This ensures that git log will correctly follow the original file
history.
* merge the submodule into its parent repository and commit it.
NOTE: This script might completely garble your repository, so PLEASE apply
this only to a fresh clone of the repository where it does not matter if
the repo is destroyed. It would be wise to keep a backup clone of your
repository, so that you can reconstitute it if need be. You have been
warned. Use at your own risk.
EOF
request_confirmation "Do you want to proceed?"
}
function git_version_lte() {
OP_VERSION=$(printf "%03d%03d%03d%03d" $(echo "$1" | tr '.' '\n' | head -n 4))
GIT_VERSION=$(git version)
GIT_VERSION=$(printf "%03d%03d%03d%03d" $(echo "${GIT_VERSION#git version}" | tr '.' '\n' | head -n 4))
echo -e "${GIT_VERSION}\n${OP_VERSION}" | sort | head -n1
[ ${OP_VERSION} -le ${GIT_VERSION} ]
}
function main() {
warn
if [ "${verbose}" == "true" ]; then
set -x
fi
# Remove submodule and commit
git config -f .gitmodules --remove-section "submodule.${sub}"
if git config -f .git/config --get "submodule.${sub}.url"; then
git config -f .git/config --remove-section "submodule.${sub}"
fi
rm -rf "${path}"
git add -A .
git commit -m "Remove submodule ${sub}"
rm -rf ".git/modules/${sub}"
# Rewrite submodule history
local tmpdir="$(mktemp -d -t submodule-rewrite-XXXXXX)"
git clone "${url}" "${tmpdir}"
pushd "${tmpdir}"
local tab="$(printf '\t')"
local filter="git ls-files -s | sed \"s/${tab}/${tab}${path}\//\" | GIT_INDEX_FILE=\${GIT_INDEX_FILE}.new git update-index --index-info && mv \${GIT_INDEX_FILE}.new \${GIT_INDEX_FILE}"
git filter-branch --index-filter "${filter}" HEAD
popd
# Merge in rewritten submodule history
git remote add "${sub}" "${tmpdir}"
git fetch "${sub}"
if git_version_lte 2.8.4
then
# Previous to git 2.9.0 the parameter would yield an error
ALLOW_UNRELATED_HISTORIES=""
else
# From git 2.9.0 this parameter is required
ALLOW_UNRELATED_HISTORIES="--allow-unrelated-histories"
fi
git merge -s ours --no-commit ${ALLOW_UNRELATED_HISTORIES} "${sub}/master"
rm -rf tmpdir
# Add submodule content
git clone "${url}" "${path}"
rm -rf "${path}/.git"
git add "${path}"
git commit -m "Merge submodule contents for ${sub}"
git config -f .git/config --remove-section "remote.${sub}"
set +x
echo "$(tput setaf 2)Submodule merge complete. Push changes after review.$(tput sgr0)"
}
set -euo pipefail
declare verbose=false
while [ $# -gt 0 ]; do
case "$1" in
(-h|--help)
usage
exit 0
;;
(-v|--verbose)
verbose=true
;;
(*)
break
;;
esac
shift
done
declare sub="${1:-}"
if [ -z "${sub}" ]; then
>&2 echo "Error: No submodule specified"
usage
exit 1
fi
shift
if [ -n "${1:-}" ]; then
>&2 echo "Error: Unknown option: ${1:-}"
usage
exit 1
fi
if ! [ -d ".git" ]; then
>&2 echo "Error: No git repository found. Must be run from the root of a git repository"
usage
exit 1
fi
declare path="$(git config -f .gitmodules --get "submodule.${sub}.path")"
declare url="$(git config -f .gitmodules --get "submodule.${sub}.url")"
if [ -z "${path}" ]; then
>&2 echo "Error: Submodule not found: ${sub}"
usage
exit 1
fi
if ! [ -d "${path}" ]; then
>&2 echo "Error: Submodule path not found: ${path}"
usage
exit 1
fi
main
curl https://raw.githubusercontent.com/jeremysears/scripts/master/bin/git-submodule-rewrite > git-submodule-rewrite.sh
và./git-submodule-rewrite.sh <submodule-name>
git rm --cached the_submodule_path
.gitmodules
tệp hoặc nếu đó là mô hình con duy nhất, hãy xóa tệp.git add the_submodule_path
Tôi chưa tìm thấy cách nào dễ dàng hơn. Bạn có thể nén 3-5 thành một bước thông qua git commit -a
- vấn đề của hương vị.
.gitmodules
thay thế .submodules
?
.gitmodules
không.submodules
.git
thư mục của mô hình con trước khi git add
hoạt động trên thư mục mô hình con
Rất nhiều câu trả lời ở đây nhưng tất cả chúng dường như quá phức tạp và có khả năng không làm những gì bạn muốn. Tôi chắc chắn rằng hầu hết mọi người muốn giữ lịch sử của họ.
Trong ví dụ này, repo chính sẽ là git@site.com:main/main.git
và repo mô đun con sẽ là git@site.com:main/child.git
. Điều này giả định rằng mô hình con được đặt trong thư mục gốc của repo cha. Điều chỉnh các hướng dẫn khi cần thiết.
Bắt đầu bằng cách nhân bản repo cha và loại bỏ mô hình con cũ.
git clone git@site.com:main/main.git
git submodule deinit child
git rm child
git add --all
git commit -m "remove child submodule"
Bây giờ chúng tôi sẽ thêm repos con ngược dòng vào repo chính.
git remote add upstream git@site.com:main/child.git
git fetch upstream
git checkout -b merge-prep upstream/master
Bước tiếp theo giả định rằng bạn muốn di chuyển các tệp trên nhánh hợp nhất chuẩn bị vào cùng vị trí với mô hình con ở trên mặc dù bạn có thể dễ dàng thay đổi vị trí bằng cách thay đổi đường dẫn tệp.
mkdir child
di chuyển tất cả các thư mục và tệp ngoại trừ thư mục .git vào thư mục con.
git add --all
git commit -m "merge prep"
Bây giờ bạn có thể chỉ cần hợp nhất các tập tin của bạn trở lại vào nhánh chính.
git checkout master
git merge merge-prep # --allow-unrelated-histories merge-prep flag may be required
Nhìn xung quanh và đảm bảo mọi thứ đều ổn trước khi chạy git push
Một điều bạn phải nhớ bây giờ là nhật ký git không theo mặc định theo dõi các tệp đã di chuyển, tuy nhiên bằng cách chạy, git log --follow filename
bạn có thể xem toàn bộ lịch sử các tệp của mình.
git merge merge-prep
và nhận được lỗi fatal: refusing to merge unrelated histories
. Cách giải quyết là đây : git merge --allow-unrelated-histories merge-prep
.
child
thư mục, vì vậy bạn không phải di chuyển chúng sau này? Tôi có cùng tên tệp trong một mô hình con và repo chính ... vì vậy tôi chỉ nhận được một xung đột hợp nhất vì nó đang cố gắng hợp nhất hai tệp lại với nhau.
Điều đó xảy ra với chúng tôi rằng chúng tôi đã tạo ra 2 kho lưu trữ cho 2 dự án được ghép nối đến mức không có ý nghĩa gì để tách chúng ra, vì vậy chúng tôi đã hợp nhất chúng.
Tôi sẽ chỉ cho bạn cách hợp nhất các nhánh chính trong mỗi lần đầu tiên và sau đó tôi sẽ giải thích cách bạn có thể mở rộng điều này đến mọi nhánh bạn có, hy vọng nó sẽ giúp bạn.
Nếu bạn có mô hình con hoạt động và bạn muốn chuyển đổi nó thành một thư mục tại chỗ, bạn có thể làm:
git clone project_uri project_name
Ở đây chúng tôi làm một bản sao sạch để làm việc. Đối với quá trình này, bạn không cần phải khởi tạo hoặc cập nhật các mô hình con, vì vậy chỉ cần bỏ qua nó.
cd project_name
vim .gitmodules
Chỉnh sửa .gitmodules
với trình chỉnh sửa yêu thích của bạn (hoặc Vim) để xóa mô hình con bạn dự định thay thế. Các dòng bạn cần loại bỏ sẽ trông giống như thế này:
[submodule "lib/asi-http-request"]
path = lib/asi-http-request
url = https://github.com/pokeb/asi-http-request.git
Sau khi lưu tệp,
git rm --cached directory_of_submodule
git commit -am "Removed submodule_name as submodule"
rm -rf directory_of_submodule
Ở đây chúng tôi loại bỏ hoàn toàn mối quan hệ mô hình con để chúng tôi có thể tạo mang lại repo khác cho dự án tại chỗ.
git remote add -f submodule_origin submodule_uri
git fetch submodel_origin/master
Ở đây chúng tôi lấy kho lưu trữ mô hình con để hợp nhất.
git merge -s ours --no-commit submodule_origin/master
Ở đây chúng tôi bắt đầu một hoạt động hợp nhất của 2 kho lưu trữ, nhưng dừng lại trước khi cam kết.
git read-tree --prefix=directory_of_submodule/ -u submodule_origin/master
Ở đây chúng tôi gửi nội dung của master trong mô hình con đến thư mục chứa nó trước khi đặt tiền tố tên thư mục
git commit -am "submodule_name is now part of main project"
Ở đây chúng tôi hoàn thành thủ tục thực hiện một cam kết về các thay đổi trong hợp nhất.
Sau khi hoàn thành việc này, bạn có thể đẩy và bắt đầu lại với bất kỳ nhánh nào khác để hợp nhất, chỉ cần kiểm tra nhánh trong kho lưu trữ của bạn sẽ nhận được các thay đổi và thay đổi nhánh bạn mang trong các hoạt động hợp nhất và cây đọc.
directory_of_submodule
git log original_path_of_file_in_submodule
tức là đường dẫn được đăng ký trong git repo cho tệp (không còn tồn tại trên hệ thống tệp) mặc dù tệp mô hình con hiện đang sống tạisubmodule_path/new_path_of_file
Câu trả lời tốt nhất cho điều này tôi đã tìm thấy ở đây:
http://x3ro.de/2013/09/01/Integrating-a-submodule-into-the-parent-reposeective.html
Bài viết này giải thích các thủ tục rất tốt.
Đây là phiên bản cải tiến một chút (IMHO) của câu trả lời của @ gyim. Anh ta đang thực hiện một loạt các thay đổi nguy hiểm trong bản sao làm việc chính, nơi tôi nghĩ việc vận hành trên các bản sao riêng biệt và sau đó hợp nhất chúng lại với nhau dễ dàng hơn nhiều.
Trong một thư mục riêng (để dễ dàng dọn dẹp và thử lại), hãy kiểm tra cả repo hàng đầu và subrepo.
git clone ../main_repo main.tmp
git clone ../main_repo/sub_repo sub.tmp
Đầu tiên chỉnh sửa subrepo để di chuyển tất cả các tệp vào thư mục con mong muốn
cd sub.tmp
mkdir sub_repo_path
git mv `ls | grep -v sub_repo_path` sub_repo_path/
git commit -m "Moved entire subrepo into sub_repo_path"
Ghi chú của TRỤ
SUBREPO_HEAD=`git reflog | awk '{ print $1; exit; }'`
Bây giờ xóa subrepo khỏi repo chính
cd ../main.tmp
rmdir sub_repo_path
vi .gitmodules # remove config for submodule
git add -A
git commit -m "Removed submodule sub_repo_path in preparation for merge"
Và cuối cùng, chỉ cần hợp nhất chúng
git fetch ../sub.tmp
# remove --allow-unrelated-histories if using git older than 2.9.0
git merge --allow-unrelated-histories $SUBREPO_HEAD
Và thực hiện! An toàn và không có bất kỳ phép thuật.
subrepo
trong đó?
git merge $SUBREPO_HEAD fatal: refusing to merge unrelated histories
Tôi có nên sử dụng git merge $SUBREPO_HEAD --allow-unrelated-histories
trong trường hợp này không? Hoặc nó nên làm việc mà không có và tôi đã phạm sai lầm?
Để khi nào
git rm [-r] --cached submodule_path
trả lại
fatal: pathspec 'emr/normalizers/' did not match any files
Bối cảnh: Tôi đã thực hiện rm -r .git*
trong các thư mục mô hình con của mình trước khi nhận ra rằng chúng cần được hủy đăng ký trong dự án chính mà tôi vừa thêm chúng. Tôi đã nhận được lỗi ở trên khi hủy đăng ký một số, nhưng không phải tất cả trong số họ. Dù sao, tôi đã sửa chúng bằng cách chạy, (dĩ nhiên là sau rm -r .git*
)
mv submodule_path submodule_path.temp
git add -A .
git commit -m "De-submodulization phase 1/2"
mv submodule_path.temp submodule_path
git add -A .
git commit -m "De-submodulization phase 2/2"
Lưu ý rằng điều này không bảo tồn lịch sử.
Dựa trên câu trả lời của VonC , tôi đã tạo ra một tập lệnh bash đơn giản thực hiện điều này. Các add
cuối cùng phải sử dụng ký tự đại diện nếu không nó sẽ khôi phục trở lại trước rm
cho submodule riêng của mình. Điều quan trọng là thêm nội dung của thư mục mô đun con, và không đặt tên chính thư mục đó trong add
lệnh.
Trong một tệp có tên git-integrate-submodule
:
#!/usr/bin/env bash
mv "$1" "${1}_"
git submodule deinit "$1"
git rm "$1"
mv "${1}_" "$1"
git add "$1/**"
Tôi thấy thuận tiện hơn khi (cũng?) Lấy dữ liệu cam kết cục bộ từ mô hình con, bởi vì nếu không tôi sẽ mất chúng. (Không thể đẩy chúng khi tôi không truy cập vào điều khiển từ xa đó). Vì vậy, tôi đã thêm mô hình con / .git là remote_origin2, tìm nạp nó cam kết và hợp nhất từ nhánh đó. Không chắc chắn nếu tôi vẫn cần mô hình con từ xa như nguồn gốc, vì tôi chưa đủ quen thuộc với git.
Đây là những gì tôi thấy tốt nhất và đơn giản nhất.
Trong repo mô đun con, từ CHÍNH bạn muốn hợp nhất thành repo chính:
git checkout -b "mergeMe"
mkdir "foo/bar/myLib/"
(đường dẫn giống hệt như nơi bạn muốn các tệp trên repo chính)git mv * "foo/bar/myLib/"
(di chuyển tất cả vào con đường)git commit -m "ready to merge into main"
Quay lại repo chính sau khi loại bỏ mô hình con và xóa đường dẫn "foo / bar / myLib":
git merge --allow-unrelated-histories SubmoduleOriginRemote/mergeMe
bùng nổ xong
lịch sử được bảo tồn
đừng lo lắng
Lưu ý điều này gần giống với một số câu trả lời khác. Nhưng điều này giả định rằng bạn sở hữu repo mô đun con. Ngoài ra, điều này giúp dễ dàng có được các thay đổi ngược dòng trong tương lai cho mô hình con.
git submodule deinit
, xem câu trả lời của tôi bên dưới