493 Stimmen

Wie kann man ein Git-Submodul aufheben?

Was sind die besten Praktiken für die Aufhebung der Submodulierung eines Git-Submoduls, um den gesamten Code zurück in das Kern-Repository zu bringen?

669voto

gyim Punkte 8445

Wenn Sie nur den Code Ihres Submoduls in das Hauptrepository stellen wollen, müssen Sie nur das Submodul entfernen und die Dateien wieder in das Hauptrepository einfügen:

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"

Wenn Sie auch die Geschichte des Submoduls bewahren wollen, können Sie einen kleinen Trick anwenden: "Mergen" Sie das Submodul in das Haupt-Repository, so dass das Ergebnis dasselbe ist wie vorher, nur dass die Submoduldateien jetzt im Haupt-Repository sind.

Im Hauptmodul müssen Sie Folgendes tun:

# 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

Das resultierende Repository wird etwas seltsam aussehen: Es wird mehr als eine erste Übergabe geben. Aber es wird keine Probleme für Git verursachen.

Ein großer Vorteil dieser zweiten Lösung ist, dass Sie weiterhin git blame o git log auf die Dateien, die sich ursprünglich in Submodulen befanden. Was hier passiert, ist eigentlich nur eine Umbenennung vieler Dateien innerhalb eines Repositorys, und Git sollte dies automatisch erkennen. Wenn Sie immer noch Probleme haben mit git log versuchen Sie einige Optionen (z. B., --follow , -M , -C ), die eine bessere Erkennung von Umbenennungen und Kopien ermöglichen.

105voto

jsears Punkte 4283

Ich habe ein Skript erstellt, das ein Submodul in ein einfaches Verzeichnis übersetzt, wobei der gesamte Dateiverlauf erhalten bleibt. Es leidet nicht unter dem git log --follow <file> Probleme, unter denen die anderen Lösungen leiden. Es ist auch ein sehr einfacher Ein-Zeilen-Aufruf, der die ganze Arbeit für Sie erledigt. G'luck.

Es baut auf der hervorragenden Arbeit von Lucas Jenß auf, die er in seinem Blogbeitrag " Integration eines Submoduls in das übergeordnete Repository ", sondern automatisiert den gesamten Prozess und bereinigt einige andere Eckfälle.

Der aktuelle Code wird mit Fehlerkorrekturen auf github gepflegt unter https://github.com/jeremysears/scripts/blob/master/bin/git-submodule-rewrite aber um dem korrekten Stackoverflow-Antwortprotokoll gerecht zu werden, habe ich die Lösung in ihrer Gesamtheit unten eingefügt.

Verwendung:

$ 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

87voto

VonC Punkte 1117238

Seit git 1.8.5 (Nov 2013 ) ( ohne die Historie des Submoduls zu behalten ):

mv yoursubmodule yoursubmodule_tmp
git submodule deinit yourSubmodule
git rm yourSubmodule
mv yoursubmodule_tmp yoursubmodule
git add yoursubmodule

Das wird:

  • abmelden und entladen (d.h. löschen Sie den Inhalt von ) das Submodul ( deinit daher die mv erste ),
  • Reinigen Sie die .gitmodules für Sie ( rm ),
  • und entfernen Sie die Sondereintrag das dieses Submodul SHA1 im Index des übergeordneten Repos repräsentiert ( rm ).

Sobald die Entfernung des Submoduls abgeschlossen ist ( deinit y git rm ), können Sie den Ordner wieder in seinen ursprünglichen Namen umbenennen und ihn als normalen Ordner zum Git-Repository hinzufügen.

Hinweis: Wenn das Submodul mit einem alten Git (< 1.8) erstellt wurde, müssen Sie möglicherweise die verschachtelte .git Ordner innerhalb des Submoduls selbst, als kommentiert von Simon Ost


Wenn Sie die Historie des Submoduls aufbewahren müssen, siehe jsears 's réponse , die die git filter-branch .

45voto

Marcel Jackwerth Punkte 51964
  1. git rm --cached the_submodule_path
  2. den Submodulabschnitt aus der Datei .gitmodules Datei, oder wenn es das einzige Submodul ist, entfernen Sie die Datei.
  3. ein Commit "Submodul xyz entfernt" durchführen
  4. git add the_submodule_path
  5. eine weitere Übergabe "Codebase von xyz hinzugefügt"

Einen einfacheren Weg habe ich noch nicht gefunden. Sie können 3-5 in einem Schritt komprimieren über git commit -a - eine Frage des Geschmacks.

19voto

eatingthenight Punkte 3390

Hier gibt es viele Antworten, aber alle scheinen zu komplex zu sein und bringen wahrscheinlich nicht das, was Sie wollen. Ich bin sicher, dass die meisten Leute ihre Geschichte behalten wollen.

Für dieses Beispiel wird das Haupt-Repositorium sein git@site.com:main/main.git und das Submodul-Repositorium wird git@site.com:main/child.git . Dabei wird davon ausgegangen, dass sich das Submodul im Root-Verzeichnis des übergeordneten Repos befindet. Passen Sie die Anweisungen nach Bedarf an.

Beginnen Sie damit, das übergeordnete Projektarchiv zu klonen und das alte Submodul zu entfernen.

git clone git@site.com:main/main.git
git submodule deinit child
git rm child
git add --all
git commit -m "remove child submodule"

Nun fügen wir die Child-Repos dem Haupt-Repo vorgelagert hinzu.

git remote add upstream git@site.com:main/child.git
git fetch upstream
git checkout -b merge-prep upstream/master

Der nächste Schritt setzt voraus, dass Sie die Dateien auf dem merge-prep-Zweig an denselben Ort verschieben wollen, an dem sich das Submodul oben befand, obwohl Sie den Ort leicht ändern können, indem Sie den Dateipfad ändern.

mkdir child

verschieben Sie alle Ordner und Dateien mit Ausnahme des Ordners .git in den untergeordneten Ordner.

git add --all
git commit -m "merge prep"

Jetzt können Sie Ihre Dateien einfach wieder in den Master-Zweig einfügen.

git checkout master
git merge merge-prep # --allow-unrelated-histories merge-prep flag may be required 

Schauen Sie sich um und vergewissern Sie sich, dass alles gut aussieht, bevor Sie loslaufen. git push

Eine Sache, an die Sie sich jetzt erinnern müssen, ist, dass das Git-Protokoll nicht standardmäßig verschobenen Dateien folgt, aber indem Sie git log --follow filename können Sie den gesamten Verlauf Ihrer Dateien einsehen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X