Цель
- Использование
git am
(вдохновлено Смар , заимствовано у Exherbo )
- Добавить историю коммитов скопированных / перемещенных файлов
- Из одного каталога в другой
- Или из одного хранилища в другое
Ограничение
- Теги и ветки не сохраняются
- История вырезана при переименовании файла пути (переименование каталога)
Краткое описание
- Извлечение истории в формате электронной почты с использованием
git log --pretty=email -p --reverse --full-index --binary
- Реорганизовать дерево файлов и обновить имена файлов
- Добавить новую историю, используя
cat extracted-history | git am --committer-date-is-author-date
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 the folder
Извлечение истории каждого файла в формате электронной почты
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
Dan Bonachea предлагает инвертировать циклы команды генерации git log на этом первом шаге: вместо того, чтобы запускать git log один раз для файла, запускайте его ровно один раз со списком файлов в командной строке и создать единый лог. Таким образом, коммиты, которые изменяют несколько файлов, остаются в результате одним коммитом, и все новые коммиты сохраняют свой первоначальный относительный порядок. Обратите внимание, что это также требует изменений на втором этапе ниже при перезаписи имен файлов в (теперь объединенном) журнале.
2. Реорганизовать дерево файлов и обновить имена файлов
Предположим, вы хотите переместить эти три файла в этом другом репо (это может быть тот же репо).
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB # New tree
│ ├── dirB1 # from subdir
│ │ ├── file33 # from file3
│ │ └── file44 # from file4
│ └── dirB2 # new dir
│ └── file5 # from file5
└── dirH
└── file77
Поэтому реорганизуйте ваши файлы:
cd /tmp/mail/dir
mkdir -p dirB/dirB1
mv subdir/file3 dirB/dirB1/file33
mv subdir/file4 dirB/dirB1/file44
mkdir -p 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 --committer-date-is-author-date
--committer-date-is-author-date
сохраняет исходные отметки времени коммита ( Dan Bonachea * комментарий 1097 *).
Ваш другой репо сейчас:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB
│ ├── dirB1
│ │ ├── file33
│ │ └── file44
│ └── dirB2
│ └── file5
└── dirH
└── file77
Используйте git status
, чтобы увидеть количество коммитов, готовых к отправке: -)
Дополнительный трюк: проверка переименованных / перемещенных файлов в вашем репо
Для просмотра списка файлов, переименованных:
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'
Дополнительные настройки: Вы можете выполнить команду git log
, используя опции --find-copies-harder
или --reverse
. Вы также можете удалить первые два столбца, используя cut -f3-
и полный шаблон grepping '{. * =>. *}'.
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'