GIT история сохранения файла копирования
У меня несколько запутанный вопрос в GIT.
Допустим, у меня есть файл dir1/A.txt
, и git сохраняет историю коммитов
Теперь мне нужно (по некоторым причинам) скопировать файл в dir2/A.txt
(не перемещать, а копировать).
Я знаю, что есть команда git mv
, но мне нужно dir2/A.txt
иметь ту же историю фиксаций, что и dir1/A.txt
, и dir1/A.txt
, чтобы оставаться там.
Я не планирую обновлять A.txt
после создания копии, и вся будущая работа будет выполнена на dir2/A.txt
Я знаю, что это звучит запутанно, я добавлю, что эта ситуация связана с модулем java (mavenized project), и нам нужно создать новую версию кода, чтобы наши клиенты имели возможность иметь 2 разных версии во время выполнения, первая версия будет удалена в конце концов, когда выравнивание будет выполнено.
Конечно, мы можем использовать управление версиями maven, я просто новичок в git и вам интересно, что здесь может предоставить git.
Ответы
Ответ 1
В отличие от subversion, git не имеет истории каждого файла. Если вы посмотрите на структуру данных фиксации, она указывает только на предыдущие коммиты и новый объект дерева для этой фиксации. В объекте фиксации не сохраняется явная информация, файлы которой изменяются фиксацией; ни характер этих изменений.
Инструменты для проверки изменений могут обнаруживать переименования на основе эвристики. Например. "git diff" имеет опцию -M, которая включает обнаружение переименования. Поэтому в случае переименования "git diff" может показать вам, что один файл был удален, а другой создан, тогда как "git diff -M" будет фактически определять ход и отображать изменение соответственно (см. "Человек git diff" для деталей).
Итак, в git это не вопрос того, как вы совершаете свои изменения, но как вы смотрите на зафиксированные изменения позже.
Ответ 2
Все, что вам нужно сделать, это переместить его параллельно в два разных места, объединить, а затем переместить одну копию обратно в исходное место, и все готово.
Затем вы можете запустить git blame
для любой копии без специальных опций и увидеть лучшие исторические атрибуты для обеих. Вы также можете запустить git log
на оригинале и посмотреть полную историю.
Подробно, предположим, что вы хотите скопировать foo/в вашем хранилище в bar/. Сделайте это (обратите внимание, что я использую -n для фиксации, чтобы избежать переписывания и т.д. С помощью хуков):
git mv foo bar
git commit -n
SAVED='git rev-parse HEAD'
git reset --hard HEAD^
git mv foo foo-magic
git commit -n
git merge $SAVED # This will generate conflicts
git commit -a -n # Trivially resolved like this
git mv foo-magic foo
git commit -n
Примечание: я использовал git 2.1.4 в Linux
Почему это работает
В итоге вы получите историю изменений, подобную этой:
ORIG_HEAD
/ \
SAVED ALTERNATE
\ /
MERGED
|
RESTORED
Когда вы спрашиваете git об истории 'foo', он (1) обнаружит переименование из 'foo-magic' между MERGED и RESTORED, (2) обнаружит, что 'foo-magic' пришло от АЛЬТЕРНАТИВНОГО родителя MERGED, и (3) обнаружить переименование из 'foo' между ORIG_HEAD и ALTERNATE. Оттуда он будет копаться в истории 'Foo'.
Когда вы спросите git об истории 'bar', он (1) не заметит изменений между MERGED и RESTORED, (2) обнаружит, что 'bar' произошел от SAVED-родителя MERGED, и (3) обнаружит переименование из ' foo 'между ORIG_HEAD и SAVED. Оттуда он будет копаться в истории 'Foo'.
Это так просто. Вам просто нужно заставить git войти в ситуацию слияния, в которой вы можете принять две прослеживаемые копии файла (ов), и мы делаем это с параллельным перемещением оригинала (которое мы вскоре отменим).
Ответ 3
Просто скопируйте файл, добавьте и зафиксируйте его:
cp dir1/A.txt dir2/A.txt
git add dir2/A.txt
git commit -m "Duplicated file from dir1/ to dir2/"
Затем следующие команды покажут полную историю предварительной копии:
git log --follow dir2/A.txt
Чтобы увидеть унаследованные поэтапные аннотации из исходного файла, используйте это:
git blame -C -C -C dir2/A.txt
Git не отслеживает копии во время фиксации, вместо этого он обнаруживает их при проверке истории, например. git blame
и git log
.
Большая часть этой информации взята из ответов здесь: Операция копирования файла записи с Git
Ответ 4
Для полноты я бы добавил, что если вы хотите скопировать весь каталог, полный контролируемых и неконтролируемых файлов, вы можете использовать следующее:
git mv old new
git checkout HEAD old
Неконтролируемые файлы будут скопированы, поэтому вы должны их очистить:
git clean -fdx new
Ответ 5
Я немного изменил ответ питера здесь, чтобы создать многоразовый неинтерактивный скрипт оболочки под названием git-split.sh
:
#!/bin/sh
if [[ $# -ne 2 ]] ; then
echo "Usage: git-split.sh original copy"
exit 0
fi
git mv "$1" "$2"
git commit -n -m "Split history $1 to $2 - rename file to target-name"
REV='git rev-parse HEAD'
git reset --hard HEAD^
git mv "$1" temp
git commit -n -m "Split history $1 to $2 - rename source-file to temp"
git merge $REV
git commit -a -n -m "Split history $1 to $2 - resolve conflict and keep both files"
git mv temp "$1"
git commit -n -m "Split history $1 to $2 - restore name of source-file"
Ответ 6
В моем случае я сделал изменения на своем жестком диске (вырезал/вставлял около 200 папок/файлов с одного пути в моей рабочей копии на другой путь в моей рабочей копии) и использовал SourceTree (2.0.20.1) для стадии как обнаруженные изменения (один добавить, один удалить), и пока я устраивал как добавление, так и удаление вместе, он автоматически объединяется в одно изменение с пиктограммой розового R (переименование, я предполагаю).
Я заметил, что, поскольку у меня было такое большое количество изменений сразу, SourceTree немного замедлил обнаружение всех изменений, поэтому некоторые из моих поэтапных файлов выглядят просто добавляет (зеленый плюс) или просто удаляет (красный минус), но я продолжал обновлять статус файла и продолжал создавать новые изменения по мере их появления, и через несколько минут весь список был идеальным и готовым к фиксации.
Я проверил, что история присутствует, пока я ищу историю, я проверяю опцию "Follow rename files".
Ответ 7
Этот процесс сохраняет историю, но это небольшой обходной путь:
# make branchs to new files
$: git mv arquivos && git commit
# in original branch, remove original files
$: git rm arquivos && git commit
# do merge and fix conflicts
$: git merge branch-copia-arquivos
# back to original branch and revert commit removing files
$: git revert commit