Как разделить и переименовать файлы кода в git с сохранением истории?

Я новичок в git и теперь столкнулся с проблемой «домашнего хозяйства» репозитория git после решения очистить структуру кода. У моей задачи есть два аспекта:

  1. Необходимо переименовать репозиторий с неоптимальным названием, а также некоторые ПОДПАПКИ и ФАЙЛЫ в чистую стандартную нотацию Python (от использования тире до более коротких имен с символами подчеркивания и т. д.).

  2. Разделите тестовый код на файлы .py, сохраненные в специальной папке \tests.

Я обнаружил, что выполнение приведенного выше кода и очистки файловой структуры в git затруднено с сохранением истории изменений. Другие ответы на эту тему, по-видимому, охватывают часть этих усилий. Я попытался переименовать файлы через git онлайн, но, хотя история формально сохраняется, в ней хранится только акт массового удаления большого фрагмента тестового кода, который был перемещен в папку \test. Вновь созданные файлы \tests\basic_test.py и \tests\advanced_test.py, по-видимому, рассматриваются git как новые, т.е. имеют нулевую предыдущую историю изменений.

Короче говоря, мне нужно разделить тестовый код на новые файлы, хранящиеся в новой подпапке \tests, а затем переименовать корневую папку кода, переименовав репозиторий. Можно ли это сделать без использования? командной строки git? Если нет, я думаю, пришло время изучить его, и я ценю руководство по реализации именно того, что мне нужно, выше, чтобы прыгнуть в воду, но не увязнуть в учебниках по командной строке git, т. Е. Внести изменения, которые мне нужны, с минимальным приобретением теории.

Большое спасибо за то, что поделились мудростью!

- mt code structure 1.0

\money-tracker # local dir and git repo name    
  money_tracker_v01_9.4


- mt code structure 2.0

\money_tracker # app root_dir (local dir and git repo name)
  \mt # code_dir (shared code base named after main mod)
   mt.py

  \tests
   test_basic.py
   test_advanced.py

   \data_in (private, local)
    coa.csv
    trxn_data_x.csv

   \data_out (private, local)
    cf_report_x.txt

* each mt_dir may contain aux files (f.e. __init__.py, context.py)


person PeterO    schedule 27.09.2020    source источник


Ответы (1)


Минимальная теоретическая часть, которую вы должны усвоить, заключается в следующем: Git не имеет историю файлов. В Git есть коммиты, а коммиты являются историей. Каждая фиксация содержит полный снимок каждого файла1.

Git может в любой момент сравнить любые два существующих коммита. Если есть файл с именем F в старой фиксации и файл с именем F в новой фиксации, мы обычно предполагаем, что это один и тот же файл. Но предположим, что в старой фиксации есть файл с именем old/path/to/name1.py, а в новой фиксации есть файл с именем new/name/of/name2.py.2 Тогда, возможно, их следует считать одним и тем же файлом, даже если у них разные имена.

Если какая-то фиксация переименовывает какой-либо файл, Git может попытаться обнаружить это переименование. Это обнаружение переименования зависит от того, насколько файлы похожи по содержанию. 100% совпадение содержимого гарантирует, что Git сможет довольно легко найти переименование. Поэтому, когда у вас есть коммит, который просто переименовывает файлы, скажите Git скажите мне, что изменилось в этом коммите, и, кстати, обнаруживайте переименования, пока вы это делаете 3 заставит Git сравнить снимок «до» и «после» и найдет все переименования.

Чтобы показать вам воображаемую историю файлов с помощью git log --follow -- path, Git просто просматривает каждую фиксацию. Git начинается в конце и работает в обратном направлении (он всегда так делает), сравнивая снимки до и после с включенным обнаружением переименования. Если path находится после фиксации, и Git обнаруживает, что он переименован из какого-то предыдущего пути в до фиксации, Git сообщит вам об этом, а затем начнет искать < em>старый путь.

Это, по сути, все, что вы получаете. Таким образом, при переименовании файла или реструктуризации проекта лучше всего зафиксировать только переименование в виде одной фиксации, а затем зафиксировать любые другие необходимые изменения. Вы не обязаны делать это, так как детектор переименования часто может определить переименованный-и-измененный файл как переименованный, но вы получаете лучшую гарантию обнаружения переименования, когда вы зафиксируйте переименование отдельно, чтобы каждый файл на 100% соответствовал предыдущему.

Обратите внимание, включает ли какой-либо конкретный графический интерфейс обнаружение переименования, и если да, то каким образом, зависит от этого графического интерфейса. Все, что предоставляет Git, — это коммиты.


1Файлы внутри коммита хранятся в специальном, доступном только для чтения, только для Git, сжатом и дедублированном формате. Это означает, что если вы сделаете тысячу коммитов подряд и измените README.md только один раз, у вас будет, скажем, 998 общих копий старого и 2 общих копии нового, или 400 общих копий старого и 600 общих копий. общие копии нового, так что в любом случае он действительно только в репозиторий дважды, а не тысячу раз.

Однако это также означает, что файлы, которые вы видите и над которыми работаете при работе с репозиторием Git, не находятся в репозитории Git. Файлы, которые вы видите и с которыми работаете, являются копиями, которые были извлечены из репозитория и в процессе превращены обратно в пригодные для использования файлы. Это многое объясняет, почему Git ведет себя именно так.

2Обратите внимание, что косая черта, которая идет вперед, хотя вы можете использовать обратную косую черту в Windows, является частью имени каждого файла: например, имя old/path/to/name1.py. Это не папка с именем old, содержащая папку с именем path и так далее, это просто файл с именем old/path/to/name1.py.

3В командной строке используйте git diff --find-renames или git show --find-renames, чтобы включить детектор переименования, или установите diff.renames на true. В Git версии 2.9 и выше для diff.renames по умолчанию установлено значение true; в более ранних версиях по умолчанию установлено значение false.

person torek    schedule 27.09.2020
comment
Спасибо, торек! Хотя я не понял ваш ответ в деталях, я укусил пулю и погрузился во внесение изменений через Git Bash после резервного копирования локального каталога репо. Я закончил вносить изменения с помощью следующего набора команд git: (1) git mv -M (ожидая, что это установит diff.rename в true); (2) git mv old new (переименовал файл кода); (3) git commit -am 'commit msg' (сделал локальную фиксацию); (4) git push (отправлено на удаленный сервер); (5) статус git (подтверждено, что все чисто). Сохранение истории коммитов в старом файле кода было подтверждено в удаленном репозитории git (онлайн). Спасибо еще раз! - person PeterO; 30.09.2020
comment
git mv не имеет -M, но это не важно. Чтобы установить diff.renames в true (если вы используете Git 2.8 или более раннюю версию), используйте git config. Если ваша версия Git 2.9 или более поздняя, ​​по умолчанию она уже установлена ​​на true. - person torek; 30.09.2020
comment
Еще раз спасибо, Торек. Это была опечатка - я имел в виду настройку diff.renames, запустив git diff -M. Это делает работу? Я также добавил [diff] renames = true в конфиг на всякий случай. Как проверить, установлено ли для diff.renames значение true (вступило в силу)? - person PeterO; 02.10.2020
comment
git config --get diff.renames сообщит вам, что он явно установлен (для этого конкретного репозитория). Если это ничего не показывает, оно не установлено, и вы получаете значение по умолчанию: git --version сообщит вам, какую версию Git вы используете, и, следовательно, что это за значение по умолчанию. В любом случае, мне нравится устанавливать глобально, хотя я не ожидаю, что в будущем буду использовать Git до версии 2.9 с git config --global diff.renames true. - person torek; 02.10.2020
comment
Другой способ узнать это — сравнить пару коммитов, в которых какой-то файл был переименован, и, конечно, посмотреть, сообщает ли Git об этом как о переименовании. :-) - person torek; 02.10.2020