Что вам нужно, так это 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 "$@"' -- --all
Это сделает в репозитории / foo / bar
еще один подкаталог bar со всем его содержимым на протяжении всей истории. Затем вы можете переместить все репо на уровень foo
и добавить к нему код baz
.
Обновление :
Хорошо, вот что происходит. Коммит - это ссылка на «дерево» (представьте себе SHA, представляющее содержимое всего подкаталога файловой системы) плюс некоторые «родительские» SHA и некоторые ссылки на метаданные автора / сообщение / и т. Д. Команда git commit-tree
- это бит низкого уровня, который объединяет все это вместе. Параметр - commit-filter
обрабатывается как функция оболочки и запускается вместо git commit-tree
во время процесса фильтрации и должен действовать как он.
Я беру первый параметр, исходное дерево для фиксации, и создаю новый «объект дерева», который сообщает, что он находится во вложенной папке, с помощью git mktree
, другой низкоуровневой команды git. . Для этого мне нужно передать в него что-то похожее на дерево git, то есть набор строк (режим SP type SP SHA TAB filename); таким образом, команда эха.Затем вывод mktree
заменяется первым параметром, когда я подключаюсь к реальному дереву фиксации
; «$ @»
- это способ передать все остальные параметры без изменений, убрав первый с помощью 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 "$@"' -- --all
Это должно сдвинуть реальное содержимое вниз в a / b / bar
(обратите внимание на обратный порядок).
Обновление : интегрированные улучшения Из ответа Мэтью Альперта ниже. Без - --all
это работает только для текущей проверенной ветки, но, поскольку вопрос касается всего репо, имеет больше смысла делать это таким образом, чем ветвь за веткой.
Вместо того чтобы создавать новый репозиторий, переместите то, что находится в вашем текущем репозитории, в нужное место: создайте новый каталог bar
в вашем текущем каталоге и переместите туда текущее содержимое (так ваш код окажется в /foo/bar/bar
). Затем создайте каталог baz
рядом с новым каталогом bar
(/foo/bar/baz
). mv /foo /foo2; mv /foo2/bar /foo; rmdir /foo2
и готово :).
Отслеживание переименований в Git'е означает, что ваша история будет работать, а хэширование содержимого в Git'е означает, что даже если вы перемещали вещи, вы все еще ссылаетесь на те же объекты в репозитории.
Вы можете создать git-репо в foo
и ссылаться на baz
и bar
через git submodules
.
Тогда и bar
, и baz
сосуществуют с сохранением полной истории.
Если вам действительно нужно только одно репозиторий (foo), с историей bar и baz в нем, тогда некоторые графты или стратегия слияния поддеревьев в порядке.