Intereting Posts
Скопируйте файл из старой версии и сохраните оба из них (старая / новая версия) Как я могу добавить какие-либо папки с файлами под заданным каталогом к подрывной деятельности в файле пакетной / предварительной фиксации? Как пометить файлы для редактирования в Subclipse Правильный способ удаления Gruntjs, package.json и других файлов из ветви выпуска Слияние одной и той же ветви снова вызовет любые проблемы в Git Список файлов и ревизий с определенным сообщением о регистрации в Subversion Удалить удаленный репозиторий из оболочки git PhpStorm, как настроить git push в удаленный репозиторий? Лучшие инструменты управления проектами, управление версиями, построитель и вики Должны ли ветви git-flow быть доступны в голом репозитории и его клоне? Каков процент авторства в статистике TortoiseSVN? Добавить в локальный репозиторий SVN в Eclipse и ошибку: «обращение к логическим» git совершает новые файлы, даже если их не добавлено и не сделано Загрузить папку из Gerrit через HTTP gitignore включают определенную подпапку

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

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

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

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

Вы хотите использовать 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 и некоторые авторы / сообщения ссылок метаданных / etc. Команда git commit-tree – это бит низкого уровня, который объединяет все это вместе. Параметр --commit-filter обрабатывается как функция оболочки и запускается вместо git commit-tree во время процесса фильтрации и должен действовать как он.

То, что я делаю, – это взять первый параметр, исходное дерево для фиксации и создать новый «древовидный объект», который говорит, что он находится в git mktree через git mktree , другую команду git низкого уровня. Для этого мне нужно вложить в него что-то похожее на дерево git, то есть на набор строк (тип SP типа SP SHA TAB filename); таким образом, команда эха. Результат mktree затем заменяется первым параметром, когда я mktree к реальному commit-tree ; "$@" – это способ передать все остальные параметры неповрежденными, разделив первый со 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 mv /foo /foo2; mv /foo2/bar /foo; rmdir /foo2 и все готово :).

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

В большинстве нормальных ситуаций 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

Вы можете создать git repo в foo и ссылаться как на baz и на bar через git submodules .

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


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