Jak mogę cofnąć podmoduł git (przywrócić cały kod z powrotem do rdzenia)?
Jak w „jak” powinienem, tak jak w „Najlepszej procedurze” ...
git submodule deinit asubmodule ; git rm asubmodule
wystarczy prosty , jak pokazano w mojej odpowiedzi poniżej
Jak mogę cofnąć podmoduł git (przywrócić cały kod z powrotem do rdzenia)?
Jak w „jak” powinienem, tak jak w „Najlepszej procedurze” ...
git submodule deinit asubmodule ; git rm asubmodule
wystarczy prosty , jak pokazano w mojej odpowiedzi poniżej
Odpowiedzi:
Jeśli wszystko, czego chcesz, to umieścić kod submodułu w głównym repozytorium, wystarczy usunąć submoduł i ponownie dodać pliki do głównego repozytorium:
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"
Jeśli chcesz również zachować historię podmodułu, możesz zrobić małą sztuczkę: „scal” podmoduł z głównym repozytorium, aby wynik był taki sam jak wcześniej, z tym że pliki podmodułów znajdują się teraz w główne repozytorium.
W głównym module musisz wykonać następujące czynności:
# 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
Powstałe repozytorium będzie wyglądać nieco dziwnie: będzie więcej niż jedno wstępne zatwierdzenie. Ale to nie spowoduje żadnych problemów dla git.
W tym drugim rozwiązaniu będziesz miał dużą zaletę, że nadal możesz uruchamiać git blame lub git log na plikach, które pierwotnie były w submodułach. W rzeczywistości zmieniłeś nazwę wielu plików w jednym repozytorium i git powinien to automatycznie wykryć. Jeśli nadal masz problemy z git logiem, wypróbuj kilka opcji (--follow, -M, -C), które lepiej wykrywają zmianę nazwy / kopiowanie.
git merge
zapewnia, że dla każdego pliku będzie „poprzednie zatwierdzenie” (po jednej z dwóch „stron” scalenia).
mkdir foo && git mv !(foo) foo && git commit
.
--allow-unrelated-histories
aby wymusić scalanie podczas fałszywego scalania, gdy się dostawałem fatal: refusing to merge unrelated histories
, więcej tutaj: github.com/git/git/blob/master/Documentation/RelNotes/…
Od wersji git 1.8.5 (listopad 2013 ) ( bez przechowywania historii submodułu ):
mv yoursubmodule yoursubmodule_tmp
git submodule deinit yourSubmodule
git rm yourSubmodule
mv yoursubmodule_tmp yoursubmodule
git add yoursubmodule
To będzie:
deinit
stąd mv
pierwszy ),.gitmodules
dla ciebie ( rm
),rm
).Po zakończeniu usuwania submodułu ( deinit
i git rm
) możesz zmienić nazwę folderu z powrotem na jego pierwotną nazwę i dodać go do repozytorium git jako zwykłego folderu.
Uwaga: jeśli modułem został stworzony przez starego Git (<1,8), może być konieczne, aby usunąć zagnieżdżony .git
folder, w samym modułem, jak skomentował przez Simona Wschodzie
Jeśli trzeba zachować historię modułem, patrz jsears „s odpowiedź , który korzysta git filter-branch
.
deinit
sam wyczyściłeś działające drzewo ze swojego podmodułu?
Stworzyłem skrypt, który przetłumaczy submoduł do prostego katalogu, zachowując całą historię plików. Nie ma git log --follow <file>
problemów z innymi rozwiązaniami. Jest to również bardzo łatwe wywołanie jednowierszowe, które wykonuje całą pracę za Ciebie. Chodź.
Opiera się na doskonałej pracy Lucasa Jenßa, opisanej w jego blogu „ Integracja submodułu w repozytorium nadrzędnym ”, ale automatyzuje cały proces i oczyszcza kilka innych przypadków narożnych.
Najnowszy kod będzie utrzymywany z poprawkami błędów w github na https://github.com/jeremysears/scripts/blob/master/bin/git-submodule-rewrite , ale ze względu na odpowiedni protokół odpowiedzi stackoverflow, zawarłem rozwiązanie w całości poniżej.
Stosowanie:
$ 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
i./git-submodule-rewrite.sh <submodule-name>
git rm --cached the_submodule_path
.gitmodules
pliku lub, jeśli jest to jedyny submoduł, usuń plik.git add the_submodule_path
Nie znalazłem jeszcze łatwiejszego sposobu. Możesz skompresować 3-5 w jeden krok git commit -a
- kwestia smaku.
.gitmodules
zamiast .submodules
?
.gitmodules
nie.submodules
.git
katalog submodułu, zanim zacznę git add
pracować z folderem submodułu
Wiele odpowiedzi tutaj, ale wszystkie wydają się zbyt skomplikowane i prawdopodobnie nie robią tego, co chcesz. Jestem pewien, że większość ludzi chce zachować swoją historię.
W tym przykładzie będzie to repozytorium główne git@site.com:main/main.git
i repozytorium submodułu git@site.com:main/child.git
. Zakłada się, że podmoduł znajduje się w katalogu głównym repozytorium nadrzędnego. Dostosuj instrukcje w razie potrzeby.
Zacznij od klonowania repozytorium nadrzędnego i usunięcia starego podmodułu.
git clone git@site.com:main/main.git
git submodule deinit child
git rm child
git add --all
git commit -m "remove child submodule"
Teraz dodamy repozytorium potomne w górę do repozytorium głównego.
git remote add upstream git@site.com:main/child.git
git fetch upstream
git checkout -b merge-prep upstream/master
W następnym kroku założono, że chcesz przenieść pliki z gałęzi scalania do tej samej lokalizacji, co podmoduł powyżej, chociaż możesz łatwo zmienić lokalizację, zmieniając ścieżkę pliku.
mkdir child
przenieś wszystkie foldery i pliki oprócz folderu .git do folderu podrzędnego.
git add --all
git commit -m "merge prep"
Teraz możesz po prostu scalić swoje pliki z powrotem w gałęzi master.
git checkout master
git merge merge-prep # --allow-unrelated-histories merge-prep flag may be required
Rozejrzyj się i upewnij się, że wszystko wygląda dobrze przed uruchomieniem git push
Jedyną rzeczą, którą musisz teraz zapamiętać, jest to, że git log domyślnie nie śledzi przeniesionych plików, jednak po uruchomieniu git log --follow filename
możesz zobaczyć pełną historię swoich plików.
git merge merge-prep
i otrzymałem błąd fatal: refusing to merge unrelated histories
. Obejście to: git merge --allow-unrelated-histories merge-prep
.
child
katalogu, więc nie musisz ich przenosić później? Mam takie same nazwy plików w podmodule i głównym repozytorium ... więc dostaję konflikt scalania, ponieważ próbuje połączyć dwa pliki razem.
Zdarzyło się nam, że stworzyliśmy 2 repozytoria dla 2 projektów, które były tak połączone, że rozdzielenie ich nie miało sensu, więc połączyliśmy je.
Pokażę, jak połączyć główne gałęzie w każdej z nich, a następnie wyjaśnię, jak możesz to rozszerzyć na każdą otrzymaną gałąź, mam nadzieję, że to ci pomoże.
Jeśli podmoduł działa i chcesz przekonwertować go na katalog w miejscu, możesz:
git clone project_uri project_name
Tutaj robimy czysty klon do pracy. W tym procesie nie musisz inicjować ani aktualizować submodułów, więc po prostu go pomiń.
cd project_name
vim .gitmodules
Edytuj za .gitmodules
pomocą swojego ulubionego edytora (lub Vima), aby usunąć submoduł, który planujesz zastąpić. Linie, które musisz usunąć, powinny wyglądać mniej więcej tak:
[submodule "lib/asi-http-request"]
path = lib/asi-http-request
url = https://github.com/pokeb/asi-http-request.git
Po zapisaniu pliku
git rm --cached directory_of_submodule
git commit -am "Removed submodule_name as submodule"
rm -rf directory_of_submodule
Tutaj całkowicie usuwamy relację podmodułu, abyśmy mogli stworzyć drugą repozytorium w projekcie.
git remote add -f submodule_origin submodule_uri
git fetch submodel_origin/master
Tutaj pobieramy repozytorium submodułu do scalenia.
git merge -s ours --no-commit submodule_origin/master
Tutaj rozpoczynamy operację scalania 2 repozytoriów, ale zatrzymujemy się przed zatwierdzeniem.
git read-tree --prefix=directory_of_submodule/ -u submodule_origin/master
Tutaj wysyłamy zawartość wzorca w podmodule do katalogu, w którym była przed prefiksem nazwy katalogu
git commit -am "submodule_name is now part of main project"
Tutaj kończymy procedurę zatwierdzając zmiany w scalaniu.
Po zakończeniu możesz pchać i zaczynać od innej gałęzi w celu scalenia, po prostu sprawdź gałąź w swoim repozytorium, która otrzyma zmiany i zmieni gałąź, którą wprowadzasz w operacjach scalania i odczytu.
directory_of_submodule
git log original_path_of_file_in_submodule
np. ścieżkę zarejestrowaną w repozytorium git dla pliku (który już nie istnieje w systemie plików), mimo że plik submodułu teraz mieszka wsubmodule_path/new_path_of_file
Najlepsza odpowiedź na to, którą znalazłem, jest tutaj:
http://x3ro.de/2013/09/01/Integrating-a-submodule-into-the-parent-repository.html
W tym artykule bardzo dobrze wyjaśniono tę procedurę.
Oto nieco ulepszona wersja (IMHO) odpowiedzi @ gyim. Dokonuje wielu niebezpiecznych zmian w głównej kopii roboczej, gdzie myślę, że o wiele łatwiej jest operować na oddzielnych klonach, a następnie scalić je na końcu.
W osobnym katalogu (aby ułatwić usuwanie błędów i spróbuj ponownie), sprawdź zarówno najlepsze repozytorium, jak i subrepo.
git clone ../main_repo main.tmp
git clone ../main_repo/sub_repo sub.tmp
Najpierw edytuj subrepo, aby przenieść wszystkie pliki do żądanego podkatalogu
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"
Zanotuj HEAD
SUBREPO_HEAD=`git reflog | awk '{ print $1; exit; }'`
Teraz usuń subrepo z głównego repozytorium
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"
I w końcu po prostu je połącz
git fetch ../sub.tmp
# remove --allow-unrelated-histories if using git older than 2.9.0
git merge --allow-unrelated-histories $SUBREPO_HEAD
I zrobione! Bezpiecznie i bez magii.
subrepo
z zawartością ?
git merge $SUBREPO_HEAD fatal: refusing to merge unrelated histories
Czy powinienem użyć git merge $SUBREPO_HEAD --allow-unrelated-histories
w tym przypadku? A może powinien działać bez i popełniłem błąd?
Do kiedy
git rm [-r] --cached submodule_path
zwroty
fatal: pathspec 'emr/normalizers/' did not match any files
Kontekst: Zrobiłem to rm -r .git*
w folderach submodułów, zanim zdałem sobie sprawę, że trzeba je zdekodulodować w głównym projekcie, do którego właśnie je dodałem. Wystąpił powyższy błąd podczas usuwania niektórych modeli, ale nie wszystkich. W każdym razie naprawiłem je, uruchamiając (po, oczywiście 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"
Pamiętaj, że to nie zachowuje historii.
Na podstawie odpowiedzi VonC stworzyłem prosty skrypt bash, który to robi. Na add
końcu należy użyć symboli wieloznacznych, w przeciwnym razie cofnie poprzednie rm
dla samego podmodułu. Ważne jest, aby dodać zawartość katalogu submodułu, a nie nazywać samego katalogu w add
poleceniu.
W pliku o nazwie git-integrate-submodule
:
#!/usr/bin/env bash
mv "$1" "${1}_"
git submodule deinit "$1"
git rm "$1"
mv "${1}_" "$1"
git add "$1/**"
Uważam, że wygodniej jest (również?) Pobierać dane lokalnego zatwierdzenia z podmodułu, ponieważ w przeciwnym razie straciłbym je. (Nie można ich wypchnąć, ponieważ nie mam dostępu do tego pilota). Więc dodałem submodule / .git jako remote_origin2, ściągnąłem commits i połączyłem z tej gałęzi. Nie jestem pewien, czy nadal potrzebuję zdalnego modułu podrzędnego jako źródła, ponieważ nie znam jeszcze git.
Oto, co uważam za najlepsze i najprostsze.
W repozytorium submodułowym HEAD chcesz scalić w repozytorium główne:
git checkout -b "mergeMe"
mkdir "foo/bar/myLib/"
(identyczna ścieżka jak w przypadku plików na głównym repozytorium)git mv * "foo/bar/myLib/"
(przenieś wszystko na ścieżkę)git commit -m "ready to merge into main"
Powrót do głównego repozytorium po usunięciu podmodułu i wyczyszczeniu ścieżki „foo / bar / myLib”:
git merge --allow-unrelated-histories SubmoduleOriginRemote/mergeMe
boom zrobiony
historie zachowane
bez obaw
Zauważ, że prawie identyczne z niektórymi innymi odpowiedziami. Ale zakłada to, że posiadasz repozytorium submodułu. Ułatwia to także uzyskanie przyszłych zmian dla podmodułu.
git submodule deinit
, patrz moja odpowiedź poniżej