Як скасувати підмодуль підмодуля git (повернути весь код до основної)?
Як у тому, як "повинен" я, як у "Кращій процедурі" ...
git submodule deinit asubmodule ; git rm asubmodule
Достатньо простого , як показано у моїй відповіді нижче
Як скасувати підмодуль підмодуля git (повернути весь код до основної)?
Як у тому, як "повинен" я, як у "Кращій процедурі" ...
git submodule deinit asubmodule ; git rm asubmodule
Достатньо простого , як показано у моїй відповіді нижче
Відповіді:
Якщо все, що вам потрібно, - це помістити код підмодуля в основне сховище, вам просто потрібно видалити підмодуль і повторно додати файли в основне репо:
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"
Якщо ви також хочете зберегти історію підмодуля, ви можете зробити невеликий трюк: "об'єднати" підмодуль у основне сховище, щоб результат був таким, як був раніше, за винятком того, що файли підмодуля зараз у основне сховище.
У головному модулі вам потрібно зробити наступне:
# 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
Отримане сховище виглядатиме дещо дивно: буде більше одного початкового введення. Але це не викличе жодних проблем з git.
У цьому другому рішенні ви матимете велику перевагу в тому, що ви все ще можете запускати git blame або git log на файли, які спочатку були в підмодулях. Насправді те, що ви тут зробили, - це перейменувати багато файлів у одному сховищі, і git повинен автоматично виявити це. Якщо у вас все ще виникають проблеми з журналом git, спробуйте кілька варіантів (- нижче, -M, -C), які краще перейменувати / виявити копію.
git merge
гарантується, що для кожного файлу (на одній із двох "сторін" об'єднання) буде "попередня фіксація".
mkdir foo && git mv !(foo) foo && git commit
.
--allow-unrelated-histories
щоб примусити злиття під час фальшивого злиття, як я отримував fatal: refusing to merge unrelated histories
, докладніше тут: github.com/git/git/blob/master/Documentation/RelNotes/…
З моменту git 1.8.5 (листопад 2013 р. ) ( Без збереження історії підмодуля ):
mv yoursubmodule yoursubmodule_tmp
git submodule deinit yourSubmodule
git rm yourSubmodule
mv yoursubmodule_tmp yoursubmodule
git add yoursubmodule
Що буде:
deinit
звідси mv
перший ),.gitmodules
для вас ( rm
),rm
).Після того, як видалення підмодуля буде завершено ( deinit
і git rm
), ви можете перейменувати папку назад у її початкове ім'я та додати її до git repo як звичайну папку.
Примітка: якщо подмодуль був створений старої Git (<1.8), можливо , буде потрібно видалити вкладену .git
папку всередині самого подмодуль, так прокоментував на Саймона Сході
Якщо вам потрібно зберегти історію субмодуля см jsears «и відповідь , який використовує git filter-branch
.
deinit
один очистив робоче дерево від вашого підмодуля?
Я створив сценарій, який переведе підмодуль у простий каталог, зберігаючи всю історію файлів. Він не страждає від git log --follow <file>
питань, від яких страждають інші рішення. Це також дуже простий однолінійний виклик, який робить всю роботу за вас. G'luck.
Він ґрунтується на чудовій роботі Лукаса Дженса, описаній у своїй публікації в блозі " Інтеграція підмодуля у батьківське сховище ", але автоматизує весь процес та очищує декілька інших кутових випадків.
Останній код підтримуватиметься з виправленнями помилок у github за адресою https://github.com/jeremysears/scripts/blob/master/bin/git-submodule-rewrite , але заради правильного протоколу відповіді stackoverflow я включив рішення в повному обсязі нижче.
Використання:
$ git-submodule-rewrite <submodule-name>
git-submodule-rewrite:
#!/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
and./git-submodule-rewrite.sh <submodule-name>
git rm --cached the_submodule_path
.gitmodules
файлу, або, якщо це єдиний підмодуль, видаліть файл.git add the_submodule_path
Простішого шляху я ще не знайшов. Ви можете стиснути 3-5 за один крок за допомогою git commit -a
смаку.
.gitmodules
замість цього .submodules
?
.gitmodules
НЕ.submodules
.git
каталог підмодулю, перш ніж git add
працювати над папкою підмодуля
Тут багато відповідей, але всі вони здаються надмірно складними і, ймовірно, не роблять того, що ви хочете. Я впевнений, що більшість людей хоче зберегти свою історію.
У цьому прикладі основним репо буде git@site.com:main/main.git
і ремоду підмодуля git@site.com:main/child.git
. Це передбачає, що підмодуль знаходиться в кореневому каталозі батьківського репо. За необхідності скоригуйте інструкції.
Почніть з клонування батьківського репо та видалення старого підмодуля.
git clone git@site.com:main/main.git
git submodule deinit child
git rm child
git add --all
git commit -m "remove child submodule"
Тепер ми додамо дочірнє репортаж вгору до основного репо.
git remote add upstream git@site.com:main/child.git
git fetch upstream
git checkout -b merge-prep upstream/master
Наступний крок передбачає, що ви хочете перемістити файли на гілці злиття-підготовки до того самого місця, де був підмодуль вище, хоча ви можете легко змінити розташування, змінивши шлях до файлу.
mkdir child
перемістити всі папки та файли, крім папки .git, у дочірню папку.
git add --all
git commit -m "merge prep"
Тепер ви можете просто об'єднати свої файли назад у головну гілку.
git checkout master
git merge merge-prep # --allow-unrelated-histories merge-prep flag may be required
Огляньтесь навколо та переконайтесь, що все добре виглядає перед запуском git push
Єдине, що вам зараз потрібно пам’ятати, це те, що журнал git за замовчуванням не слідує за переміщеними файлами, проте, запустивши, git log --follow filename
ви можете побачити всю історію своїх файлів.
git merge merge-prep
і отримав помилку fatal: refusing to merge unrelated histories
. Обхідний шлях полягає в наступному: git merge --allow-unrelated-histories merge-prep
.
child
каталогу, тож вам не доведеться переміщувати їх пізніше? У мене однакові імена файлів у підмодулі та в основному репо ... тому я просто отримую конфлікт злиття, оскільки він намагається об'єднати два файли разом.
У нас трапилось, що ми створили 2 сховища для 2-х проектів, які були настільки поєднані, що не було сенсу їх розділяти, тому ми їх об'єднали.
Я покажу, як об’єднати головні гілки в кожну першу, а потім я поясню, як ви можете поширити це на всі отримані вами гілки, сподіваюся, що це допоможе вам.
Якщо підмодуль працює, і ви хочете перетворити його в каталог на місці, ви можете зробити:
git clone project_uri project_name
Тут ми робимо чистий клон для роботи. Для цього процесу вам не потрібно ініціалізувати або оновлювати підмодулі, тому просто пропустіть його.
cd project_name
vim .gitmodules
Відредагуйте .gitmodules
улюблений редактор (або Vim), щоб видалити підмодуль, який ви плануєте замінити. Рядки, які потрібно видалити, повинні виглядати приблизно так:
[submodule "lib/asi-http-request"]
path = lib/asi-http-request
url = https://github.com/pokeb/asi-http-request.git
Після збереження файлу,
git rm --cached directory_of_submodule
git commit -am "Removed submodule_name as submodule"
rm -rf directory_of_submodule
Тут ми повністю видаляємо співвідношення підмодуля, щоб ми могли створити інше репо до проекту.
git remote add -f submodule_origin submodule_uri
git fetch submodel_origin/master
Тут ми отримуємо сховище підмодуля для об'єднання.
git merge -s ours --no-commit submodule_origin/master
Тут ми починаємо операцію злиття двох сховищ, але зупиняємось перед фіксацією.
git read-tree --prefix=directory_of_submodule/ -u submodule_origin/master
Тут ми надсилаємо вміст master у підмодулі до каталогу, де він був до префіксації імені каталогу
git commit -am "submodule_name is now part of main project"
Тут ми завершуємо процедуру, виконуючи фіксацію змін у об'єднанні.
Закінчивши це, ви можете натиснути і почати знову з будь-якої іншої гілки для злиття, просто замовте гілку у вашому сховищі, яка отримає зміни та змінить гілку, яку ви вводите в операціях злиття та читання дерева.
directory_of_submodule
git log original_path_of_file_in_submodule
тобто шлях, зареєстрований у git repo для файлу (який більше не існує у файловій системі), хоча файл підмодуля зараз живе за адресоюsubmodule_path/new_path_of_file
Найкраща відповідь на це я знайшов тут:
http://x3ro.de/2013/09/01/Iintegrating-a-submodule-into-the-parent-repository.html
Ця стаття дуже добре пояснює процедуру.
Ось дещо вдосконалена версія (IMHO) відповіді @ gyim. Він робить купу небезпечних змін в основному робочому екземплярі, де я думаю, що набагато простіше оперувати окремими клонами, а потім об'єднати їх у кінці.
У окремому каталозі (щоб спростити очищення та спробувати ще раз), перегляньте як верхню репо, так і субрепо.
git clone ../main_repo main.tmp
git clone ../main_repo/sub_repo sub.tmp
Спочатку відредагуйте субрепортаж, щоб перемістити всі файли у потрібний підкаталог
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"
Запишіть голову
SUBREPO_HEAD=`git reflog | awk '{ print $1; exit; }'`
Тепер видаліть субрепо з основного репо
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"
І нарешті, просто з’єднайте їх
git fetch ../sub.tmp
# remove --allow-unrelated-histories if using git older than 2.9.0
git merge --allow-unrelated-histories $SUBREPO_HEAD
І зробили! Безпечно і без жодної магії.
subrepo
із вмістом у ньому матеріалів?
git merge $SUBREPO_HEAD fatal: refusing to merge unrelated histories
чи повинен я використовуватись git merge $SUBREPO_HEAD --allow-unrelated-histories
у цьому випадку? Або це має працювати без того, і я помилився?
Бо коли
git rm [-r] --cached submodule_path
повертає
fatal: pathspec 'emr/normalizers/' did not match any files
Контекст: Я робив rm -r .git*
у своїх папках підмодулів, перш ніж зрозумів, що їх потрібно демодулювати в основному проекті, до якого я щойно додав їх. Я отримав вищевказану помилку, коли демодулював деякі, але не всі. У будь-якому разі, я виправив їх, запустивши (після, звичайно, 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"
Зауважте, що це не зберігає історію.
На основі відповіді VonC я створив простий сценарій bash, який робить це. В add
кінці потрібно використовувати подвійні символи, інакше це скасує попереднє rm
для самого підмодуля. Важливо додати вміст каталогу підмодуля, а не називати сам каталог у add
команді.
У файлі під назвою git-integrate-submodule
:
#!/usr/bin/env bash
mv "$1" "${1}_"
git submodule deinit "$1"
git rm "$1"
mv "${1}_" "$1"
git add "$1/**"
Мені було зручніше (також?) Отримувати місцеві фіксації даних з підмодуля, тому що в іншому випадку я б втратив їх. (Не вдалося натиснути їх, оскільки я не маю доступу до цього пульта). Тож я додав підмодуль / .git як remote_origin2, виніс його і здійснив об'єднання з цією гілкою. Не впевнений, чи мені ще потрібен дистанційний підмодуль як джерело, оскільки я ще недостатньо знайомий з git.
Ось що я вважаю найкращим та найпростішим.
У підмодулі repo від HEAD ви хочете об'єднатися в основне репо:
git checkout -b "mergeMe"
mkdir "foo/bar/myLib/"
(ідентичний шлях, як ви хочете, щоб файли в основному репо)git mv * "foo/bar/myLib/"
(перемістити всіх на шлях)git commit -m "ready to merge into main"
Повернення в основний репо після видалення підмодуля та очищення шляху "foo / bar / myLib":
git merge --allow-unrelated-histories SubmoduleOriginRemote/mergeMe
бум зроблено
історії збереглися
ніяких турбот
Зауважте, це майже ідентично з деякими іншими відповідями. Але це передбачає, що у вас є підмодуль repo. Крім того, це полегшує отримання майбутніх змін для підмодуля.
git submodule deinit
, дивіться мою відповідь нижче