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