В этом ответе представлены интересные команды, основанные git am
и представленные с помощью примеров, шаг за шагом.
Задача
- Вы хотите переместить некоторые или все файлы из одного хранилища в другое.
- Вы хотите сохранить свою историю.
- Но вы не заботитесь о сохранении тегов и веток.
- Вы принимаете ограниченную историю переименованных файлов (и файлов в переименованных каталогах).
Процедура
- Извлечь историю в формате электронной почты, используя
git log --pretty=email -p --reverse --full-index --binary
- Реорганизовать дерево файлов и обновить изменение имени файла в истории [необязательно]
- Применить новую историю, используя
git am
1. Извлечь историю в формате электронной почты
Пример: история Экстракт file3
, file4
иfile5
my_repo
├── dirA
│ ├── file1
│ └── file2
├── dirB ^
│ ├── subdir | To be moved
│ │ ├── file3 | with history
│ │ └── file4 |
│ └── file5 v
└── dirC
├── file6
└── file7
Очистить временный каталог назначения
export historydir=/tmp/mail/dir # Absolute path
rm -rf "$historydir" # Caution when cleaning
Очистите свой источник репо
git commit ... # Commit your working files
rm .gitignore # Disable gitignore
git clean -n # Simulate removal
git clean -f # Remove untracked file
git checkout .gitignore # Restore gitignore
Извлечь историю каждого файла в формате электронной почты
cd my_repo/dirB
find -name .git -prune -o -type d -o -exec bash -c 'mkdir -p "$historydir/${0%/*}" && git log --pretty=email -p --stat --reverse --full-index --binary -- "$0" > "$historydir/$0"' {} ';'
К сожалению вариант --follow
или --find-copies-harder
нельзя сочетать с --reverse
. Вот почему история обрезается при переименовании файла (или при переименовании родительского каталога).
После: Временная история в формате электронной почты
/tmp/mail/dir
├── subdir
│ ├── file3
│ └── file4
└── file5
2. Реорганизовать дерево файлов и обновить изменение имени файла в истории [необязательно]
Предположим, вы хотите переместить эти три файла в этом другом репо (это может быть тот же репо).
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB # New tree
│ ├── dirB1 # was subdir
│ │ ├── file33 # was file3
│ │ └── file44 # was file4
│ └── dirB2 # new dir
│ └── file5 # = file5
└── dirH
└── file77
Поэтому реорганизуйте ваши файлы:
cd /tmp/mail/dir
mkdir dirB
mv subdir dirB/dirB1
mv dirB/dirB1/file3 dirB/dirB1/file33
mv dirB/dirB1/file4 dirB/dirB1/file44
mkdir dirB/dirB2
mv file5 dirB/dirB2
Ваша временная история сейчас:
/tmp/mail/dir
└── dirB
├── dirB1
│ ├── file33
│ └── file44
└── dirB2
└── file5
Измените также имена файлов в истории:
cd "$historydir"
find * -type f -exec bash -c 'sed "/^diff --git a\|^--- a\|^+++ b/s:\( [ab]\)/[^ ]*:\1/$0:g" -i "$0"' {} ';'
Примечание: это переписывает историю, чтобы отразить изменение пути и имени файла.
(т.е. изменение нового местоположения / имени в новом репо)
3. Применить новую историю
Ваш другой репо:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
└── dirH
└── file77
Применить коммиты из временных файлов истории:
cd my_other_repo
find "$historydir" -type f -exec cat {} + | git am
Ваш другой репо сейчас:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB ^
│ ├── dirB1 | New files
│ │ ├── file33 | with
│ │ └── file44 | history
│ └── dirB2 | kept
│ └── file5 v
└── dirH
└── file77
использование git status
чтобы увидеть количество коммитов, готовых к отправке :-)
Примечание. Поскольку история была переписана, чтобы отразить изменение пути и имени файла:
(т.е. по сравнению с местоположением / именем в предыдущем репо)
- Не нужно
git mv
менять местоположение / имя файла.
- Нет необходимости для
git log --follow
доступа к полной истории.
Дополнительный трюк: обнаружение переименованных / перемещенных файлов в вашем репо
Для просмотра списка файлов, которые были переименованы:
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'
Дополнительные настройки: вы можете выполнить команду, git log
используя параметры --find-copies-harder
или --reverse
. Вы также можете удалить первые два столбца, используя cut -f3-
полный шаблон '{. * =>. *}'.
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'