Подтвердить что ты не робот

Как переустанавливать репозиторий git в родительскую папку при сохранении истории?

У меня есть git-репо в /foo/bar с большой историей коммитов и несколькими ветками.

Теперь я хочу, чтобы /foo/baz находился в том же репо, что и /foo/bar, что (я думаю) означает, что мне нужно создать новый репо в /foo. Тем не менее, я хочу сохранить историю изменений, которые я внес в /foo/bar.

Сначала я подумал о git format-patch, а затем о apply, но сообщения коммитов не сохранились.

Редактировать: мой вопрос не является обманом этого вопроса, потому что мой специально о сохранении истории, и ответ здесь включает в себя дополнительные сведения о том, как сохранить историю, и дополнительные сведения о том, как выполнить более сложную работу по воспитанию детей на произвольно более высоком уровне, а не чем один раз.

4b9b3361

Ответ 1

Вы хотите git filter-branch, который может перемещать весь репозиторий в поддерево, сохраняя историю, заставляя его выглядеть так, как будто это всегда было так. Создайте резервную копию своего хранилища, прежде чем использовать это!

Здесь волшебство. В /foo/bar запустите:

git filter-branch --commit-filter '
    TREE="$1";
    shift;
    SUBTREE=`echo -e 040000 tree $TREE"\tbar" | git mktree`
    git commit-tree $SUBTREE "[email protected]"' -- --all

Это сделает репозиторий /foo/bar еще одним поддиректорией "bar" со всем содержимым на протяжении всей своей истории. Затем вы можете переместить все репо до уровня foo и добавить к нему код baz.

Update:

Хорошо, вот что происходит. Конец - это ссылка на "дерево" (подумайте об этом как о SHA, представляющем содержимое подкаталога всей файловой системы) плюс некоторые "родительские" SHA и некоторые авторы/сообщения ссылки метаданных/и т.д. Команда git commit-tree - это бит низкого уровня, который объединяет все это вместе. Параметр --commit-filter обрабатывается как функция оболочки и запускается вместо git commit-tree во время процесса фильтрации и должен действовать как он.

Что я делаю, это взять первый параметр, исходное дерево для фиксации и создать новый "древовидный объект", который говорит об этом во вложенной папке с помощью git mktree, другой низкоуровневой команды git. Для этого мне нужно вложить в него что-то похожее на дерево git, то есть набор (тип SP типа SP SHA TAB filename) строк; таким образом, команда эха. Выходной сигнал mktree затем заменяется первым параметром, когда I-цепь до вещественной commit-tree; "[email protected]" - способ передать все остальные параметры без изменений, разделив первый на shift. Для информации см. git help mktree и git help commit-tree.

Итак, если вам нужно несколько уровней, вам нужно вложить несколько дополнительных уровней древовидных объектов (это не проверено, а общая идея):

git filter-branch --commit-filter '
    TREE="$1"
    shift
    SUBTREE1=`echo -e 040000 tree $TREE"\tbar" | git mktree`
    SUBTREE2=`echo -e 040000 tree $SUBTREE1"\tb" | git mktree`
    SUBTREE3=`echo -e 040000 tree $SUBTREE2"\ta" | git mktree`
    git commit-tree $SUBTREE3 "[email protected]"' -- --all

Это должно переместить реальное содержимое вниз на a/b/bar (обратите внимание на обратный порядок).

Обновление. Интегрированные улучшения. Матвей Алперт ниже. Без -- --all это работает только в текущей проверенной ветке, но поскольку вопрос задает вопрос об общем репо, имеет смысл делать это так, как разветвление.

Ответ 2

Вместо создания нового репозитория переместите то, что в вашем текущем репозитории, в нужное место: создайте новый каталог bar в вашем текущем каталоге и переместите текущий контент (так что ваш код находится в /foo/bar/bar). Затем создайте каталог baz рядом с вашим новым каталогом bar (/foo/bar/baz). mv /foo /foo2; mv /foo2/bar /foo; rmdir /foo2 и вы закончили:).

Git Отслеживание переименования означает, что ваша история все равно будет работать, а Git хеширование содержимого означает, что даже если вы перемещаете вещи, вы все равно ссылаетесь на одни и те же объекты в репозитории.

Ответ 3

У меня было решение, о котором никто пока не сказал:

Мне особенно нужно было включить файлы из родительского каталога в мой репозиторий (эффективно перемещая репо на один каталог).

Я достиг этого:

  • переместить все файлы (кроме .git) в новый подкаталог с тем же именем. И сообщите git об этомgit mv)
  • переместите все файлы из родительского каталога в пустую (за исключением .git/) текущую директорию и сообщите git об этом (с помощью git add)
  • передать все это в репо, которое не переместило (git commit).
  • переместить текущий каталог на один уровень в иерархии каталогов. (с командной строки jiggery-pokery)

Я надеюсь, что это поможет следующему парню прийти - у меня, наверное, просто безмозглый день, но я нашел ответы выше чрезмерно сложными и страшными (для чего нужен I). Я знаю, что это похоже на ответ Эндрю Айлетта выше, но моя ситуация казалась немного иной, и мне хотелось получить более общий вид.

Ответ 4

Это добавляет принятый ответ Уолтера Мундта. Я бы скорее прокомментировал его ответ, но у меня нет репутации.

Итак, метод Уолтера Мундта отлично работает, но он работает только для одной ветки за раз. И после первой ветки могут быть предупреждения, требующие -f для принудительного действия. Чтобы сделать это для всех ветвей сразу, просто добавьте "- --all" в конец:

git filter-branch --commit-filter '
    tree="$1";
    shift;
    subtree=`echo -e 040000 tree $tree"\tsrc" | git mktree`
    git commit-tree $subtree "[email protected]"' -- --all

И чтобы сделать это для определенных ветвей, вместо этого добавьте их имена в конец, хотя я не могу себе представить, почему вы изменили структуру каталогов только некоторых ветвей.

Подробнее об этом читайте на странице руководства для ветки фильтра git. Однако, пожалуйста, обратите внимание на предупреждение о возможных трудностях при нажатии такой команды. Просто убедитесь, что вы знаете, что делаете.

Я хотел бы получить больше информации о любых потенциальных проблемах с этим методом.

Ответ 5

Наиболее распространенное решение

В большинстве нормальных ситуаций git просматривает все файлы относительно своего местоположения (что означает каталог .git), в отличие от использования абсолютных путей к файлам.

Таким образом, если вы не против иметь фиксацию в своей истории, которая показывает, что вы все переместили, существует очень простое решение, которое заключается в перемещении каталога git. Единственное немного сложная задача - убедиться, что git понимает, что файлы одинаковы и что они только перемещаются относительно него:

# Create sub-directory with the same name in /foo/bar
mkdir bar

# Move everything down, notifying git :
git mv file1 file2 file3 bar/

# Then move everything up one level :
mv .git ../.git
mv bar/* .
mv .gitignore ../

# Here, take care to move untracked files

# Then delete unused directory
rmdir bar

# and commit
cd ../
git commit

Единственное, что нужно быть осторожным, - это правильно обновить .gitignore при переходе в новый каталог, чтобы избежать размещения нежелательных файлов или забыть некоторые.

Бонусное решение

В некоторых настройках git удается самостоятельно выяснить, что файлы были перемещены, когда он видит новые файлы, которые точно такие же, как и удаленные. В этом случае решение еще проще:

mv .git ../.git
mv .gitignore ../.gitignore

cd ../
git commit

Снова, будьте осторожны с вашим .gitignore

Ответ 6

1 дополнение к принятому ответу, который помог мне заставить его работать: когда я помещаю указанный текст в оболочку script, по какой-то причине -e сохраняется. (вполне вероятно, потому что я слишком толстый, чтобы работать со сценариями оболочки)

когда я удалил -e и переместил кавычки, чтобы охватить все, он сработал. SUBTREE2 = echo "040000 tree $SUBTREE1 modules" | git mktree

Обратите внимание, что есть вкладка между $SUBTREE1 и модулями, которая является той же \t, что -e должна интерпретировать.

Ответ 7

Вы можете создать репозиторий git в foo и указать как baz, так и bar через git submodules.

Тогда оба bar и baz сосуществуют с сохранением их полной истории.


Если вам действительно нужно только одно репо (foo), в нем есть как история бара, так и история baz, тогда метод трансплантатов или стратегия слияния подделок находятся в порядке.