Как переместить каталог и файлы в подкаталог вместе с историей фиксации?
Например:
-
Структура исходного каталога:
[project]/x/[files & sub-dirs]
-
Структура целевой директории:
[project]/x/p/q/[files & sub-dirs]
Как переместить каталог и файлы в подкаталог вместе с историей фиксации?
Например:
Структура исходного каталога: [project]/x/[files & sub-dirs]
Структура целевой директории: [project]/x/p/q/[files & sub-dirs]
Чтобы добавить к bmargulies comment, полная последовательность:
mkdir -p x/p/q # make sure the parent directories exist first
git mv x/* x/p/q # move folder, with history preserved
git commit -m "changed the foldername x into x/p/q"
Попробуйте сначала просмотреть предварительный просмотр перемещения:
git mv -n x/* x/p/q
Если вы используете bash, вы можете избежать проблемы с попыткой переместить папку в себя, используя расширенный глобус, подобный этому (используя
shopt
встроенный):
shopt -s extglob; git mv !(folder) folder
Капитан Man сообщает в комментариях:
mkdir temp
git mv x/* temp
mkdir -p x/p/q
git mv temp x/p/q
rmdir temp;
Контекст:
Я нахожусь в Windows с Cygwin.
Я просто понял, что сделал неправильный примерshopt -s extglob
, поэтому мой путь может быть не нужен, но я обычно использую zsh вместо bash, и у него не было командыshopt -s extglob
(хотя я уверен, что там является альтернативой), поэтому этот подход должен работать через оболочки (подталкивание в вашей оболочкеmkdir
иrmdir
, если оно особенно чуждо)
В качестве альтернативы spanky упоминает в комментариях -k
опции git mv
:
Продвиньте или переименуйте действия, которые приведут к возникновению ошибки.
git mv -k * target/
Это позволит избежать ошибки "can not move directory into itself
".
Git отлично справляется с отслеживанием контента, даже если он перемещается, поэтому git mv
, безусловно, лучший способ, если вы перемещаете файлы, потому что раньше они принадлежали x
, но теперь они принадлежат x/p/q
, потому что проект развивался таким образом.
Однако иногда есть причина для перемещения файлов в подкаталог на протяжении всей истории проекта. Например, если файлы были помещены где-то по ошибке, и с тех пор было сделано еще несколько коммитов, но прерывистые коммиты даже не имеют смысла с файлами в неправильном месте. Если все это произошло в локальной ветке, мы хотим навести порядок, прежде чем нажимать.
Вопрос гласит "вместе с историей коммитов", что я бы интерпретировал как переписывание истории именно таким образом. Это можно сделать с помощью
git filter-branch --tree-filter "cd x; mkdir -p p/q; mv [files & sub-dirs] p/q" HEAD
Затем файлы появляются в подкаталоге p/q
на протяжении всей истории.
Древовидный фильтр хорошо подходит для небольших проектов, его преимущество в том, что команда проста и понятна. Для больших проектов это решение не очень хорошо масштабируется, если производительность имеет значение, подумайте об использовании фильтра индекса, как описано в этом ответе.
Обратите внимание, что результат не должен передаваться на общедоступный сервер, если перезапись затрагивает коммиты, которые уже были переданы ранее.
Я вижу, что это старый вопрос, но я все еще чувствую себя обязанным ответить С моим текущим решением проблемы, которое я извлек из одного из примеров в git book.
Вместо использования неэффективного --tree-filter
я перемещаю файлы прямо в индекс с помощью --index-filter
.
git filter-branch -f --index-filter 'PATHS='git ls-files -s | sed "s/^<old_location>/<new_location>/"';GIT_INDEX_FILE=$GIT_INDEX_FILE.new; echo -n "$PATHS" | git update-index --index-info && if [ -e "$GIT_INDEX_FILE.new" ]; then mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"; fi' -- --all
это специализация одного из примеров из книги. Я также использовал этот же пример для массового переименования файлов в истории фиксации в одном особом случае. при перемещении файлов в подкаталогах: не забудьте экранировать символ/в путях с помощью символа \, чтобы команда sed работала как положено.
Пример:
Project Directory
|-a
| |-a1.txt
| |-b
| | |-b1.txt
чтобы переместить каталог b в корневой каталог проекта:
git filter-branch -f --index-filter 'PATHS='git ls-files -s | sed "s/a\/b\//b\//"';GIT_INDEX_FILE=$GIT_INDEX_FILE.new; echo -n "$PATHS" | git update-index --index-info && if [ -e "$GIT_INDEX_FILE.new" ]; then mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"; fi' -- --all
Результат:
Project Directory
|-a
| |-a1.txt
|
|-b
| |-b1.txt
Команда git mv - самый простой способ. Однако, по крайней мере, на моем Linux-ящике мне нужно было указать флаг -k, чтобы избежать получения ошибки, что папка не может быть перемещена в себя. Я смог выполнить действие, используя...
mkdir subdirectory
git mv -k ./* ./subdirectory
# check to make sure everything moved (see below)
git commit
В качестве предупреждения это пропустит все ходы, которые приведут к возникновению ошибки, поэтому вы, вероятно, захотите проверить, что все работает правильно после перемещения и до фиксации.