Wie kann ich ein Verzeichnis in einem Git-Repo für alle Commits verschieben?

70

Angenommen, ich habe ein Repo, das diese Verzeichnisstruktur enthält:

repo/
  blog/
    _posts/
      some-post.html
  another-file.txt

Ich möchte zur _postsobersten Ebene des Repos wechseln , damit die Struktur folgendermaßen aussieht:

repo/
  _posts/
    some-post.html
  another-file.txt

Das ist einfach genug git mv, aber ich möchte die Geschichte so aussehen lassen, als ob sie _posts immer an der Wurzel des Repos existiert, und ich möchte in der Lage sein, die gesamte Geschichte von some-post.htmlvia abzurufen git log -- _posts/some-post.html. Ich kann mir vorstellen, dass ich etwas Magie einsetzen kann git filter-branch, um dies zu erreichen, aber ich habe nicht genau herausgefunden, wie das geht. Irgendwelche Ideen?

Mipadi
quelle

Antworten:

100

Sie können den Unterverzeichnisfilter verwenden, um dies zu erreichen

 $ git filter-branch --subdirectory-filter blog/ -- --all

EDIT 1: Wenn Sie _postsdie Wurzel nicht effektiv erstellen möchten , verwenden Sie stattdessen einen Baumfilter:

 $ git filter-branch --tree-filter 'mv blog/_posts .' HEAD

EDIT 2: Wenn blog/_postsin einigen Commits nicht vorhanden, schlägt das oben genannte fehl. Verwenden Sie stattdessen Folgendes:

 $ git filter-branch --tree-filter 'test -d blog/_posts && mv blog/_posts . || echo "Nothing to do"' HEAD
Artagnon
quelle
3
Es ist auch viel schneller zu bedienen --index-filter, da es den Baum nicht auschecken muss.
Cascabel
7
Ja, der Indexfilter ist schneller, funktioniert aber nicht, da die angezeigten Befehle den Index nicht beeinflussen. Sie müssen nur git rm --cachedrm
Indexmanipulationen durchführen,
Kann ich auch Tags behalten? Es sieht so aus, als wären sie in meinem Fall weg.
Michael
1
git filter-branch --index-filter 'git read-tree --prefix=/ $GIT_COMMIT:_posts; git rm -r --cached _posts' -- --all. Fügen Sie hinzu, --tag-name-filter catwenn Ihre Tags nicht signiert sind und Sie sie verschieben / die alten ungültig machen möchten.
Bis zum
Für diejenigen, die nur zum Kopieren und Einfügen hier sind: Denken Sie an git push --force --allalle Zweige. Andernfalls kann es zu lustigen Situationen kommen.
SiliconMind
37

Obwohl Ramkumars Antwort sehr hilfreich und wertvoll ist, wird sie in vielen Situationen nicht funktionieren. Zum Beispiel, wenn Sie ein Verzeichnis mit anderen Unterverzeichnissen an einen neuen Speicherort verschieben möchten.

Dazu enthält die Manpage den perfekten Befehl:

git filter-branch --index-filter \
  'git ls-files -s | sed "s-\t\"*-&NEWSUBDIR/-" |
   GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
   git update-index --index-info &&
   mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD

Ersetzen Sie einfach NEWSUBDIR durch Ihr gewünschtes neues Verzeichnis. Sie können auch verschachtelte Verzeichnisse wie dir1 / dir2 / dir3 / - "verwenden.

der Herzog
quelle
12
Und da es nicht sofort offensichtlich ist, wenn man sich diesen Befehl oder die daraus resultierenden Fehler ansieht, funktioniert das \ t nicht mit der Version von sed von os x. Es gibt viele Möglichkeiten, dies zu umgehen. Am schnellsten ist es jedoch, das \ t zu löschen und durch eine wörtliche Registerkarte zu ersetzen, indem Sie Strg-V, <Tab> eingeben.
Jeremy Huiskamp
4
Wie geben Sie den ursprünglichen Ordner an oder verschiebt dies nur den gesamten Zweig? Wenn ich versuche, dies aus einem Ordner auszuführen, den ich verschieben möchte, bekomme ichYou need to run this command from the toplevel of the working tree
Joshcomley
1
Was macht der sedBefehl? Ich versuche dies unter Windows und brauche eine Alternative.
Lucius
1
Brillant. Aber ja, das Sed ist verwirrend. Versuchen Sie es mit der zweiten Zeile, um es zu testen. Dh um unnötige Verzeichnisse der obersten Ebene zu entfernen, habe ich eine einfachegit ls-files -s | sed "s-\tdir1/dir2/dir3/-\t-"
KCD
2
Wenn Sie ein Commit filtern, das alle Dateien effektiv löscht git update-index, wird die Datei "$ GIT_INDEX_FILE.new" nicht erstellt, und der Befehl mv schlägt fehl. Am Ende befand ich mich test -f \$GIT_INDEX_FILE.new && mv \$GIT_INDEX_FILE.new \$GIT_INDEX_FILE || touch \$GIT_INDEX_FILE im Filter-Branch-Skript.
Knut Forkalsrud
1

Erstellt ein generisches Skript für beliebige Verschiebungen / Umbenennungen: https://gist.github.com/xkr47/f766f4082112c086af63ef8d378c4304

Beispiele:

git filter-mv 's!^!subdir/!'

➜ verschiebt alle Dateien in allen Commits des aktuellen Zweigs in ein Unterverzeichnis "subdir /"

git filter-mv 's!^foo/bar.txt!foo/barbar.txt!'

➜ benennt foo / bar.txt in allen Commits des aktuellen Zweigs in foo / barbar.txt um

Jonas Berlin
quelle