Ответ 1
Есть ли простой способ увидеть:
- что получилось от b?
- что произошло с c?
- все дополнительные изменения, кроме всего лишь движущихся вещей?
Я не думаю, что есть какой-либо способ извлечь эту информацию, кроме визуального осмотра diff. Однако похоже, что мы сможем обнаружить разделенные файлы с помощью git diff
вместе с аргументом -C
. Например, я начинаю с файла, который содержит 38 строк, и перемещает 24 в один файл и 14 в другой (и удаляет оригинал). git diff --name-status
просто говорит мне, что я переименовал один файл и добавил другое:
R060 lorem.txt fileA
A fileB
Но если мы модифицируем нашу командную строку для обнаружения копий:
git diff --name-status -C30 HEAD^
Получаем:
C060 lorem.txt fileA
R039 lorem.txt fileB
Аргумент -C30
говорит: "Рассмотрите файл как копию, если она по крайней мере на 30% похожа на другой файл, включенный в commit". Обратите внимание, что существует соответствующая опция -M
, которая контролирует обнаружение переименования; по умолчанию используется 50%
.
Некоторая политика/рабочий процесс, который предотвращает подобные проблемы, также поможет.
Что именно вы пытаетесь предотвратить? На самом деле не все равно, чтобы отличить "я разбил файл на два новых файла" из "Я удалил файл и создал два новых файла".
Вы могли бы теоретически предотвратить коммиты, которые вводят новые файлы и изменяют существующие файлы. Например, это было бы относительно легко с помощью крюка pre-receive
. Но в такой общей ситуации я не уверен, что вы захотите сделать это на практике.
Для вышесказанного может работать a pre-receive
, как показано ниже:
#!/bin/bash
while read old new ref; do
while read type name; do
if [ "$type" = "A" ]; then
has_new=1
else
has_mod=1
fi
done < <(git show --name-status --format='' $new)
done
if [ "$has_new" = 1 -a "$has_mod" = 1 ]; then
echo "ERROR: commits may not both create and modify files" >&2
exit 1
fi
exit 0
В качестве альтернативы мы могли бы использовать наше "разделенное обнаружение", обсужденное ранее, и реализовать что-то вроде:
#!/bin/bash
while read old new ref; do
git diff --name-status -C30 $old $new |
awk '
{total[$2]++}
END {for (i in total) if (total[i] > 1) exit 1}
'
if [ $? -ne 0 ]; then
echo "ERROR: detected a split file"
exit 1
fi
done
exit 0
Это приведет к выходу с ошибкой, если какой-либо файл будет отображаться как "старое имя" для файла более одного раза. Попытка нажать в хранилище, используя этот крюк pre-receive
, используя пример, приведенный в первой части этих ответов, получить меня:
$ git push
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 1.46 KiB | 1.46 MiB/s, done.
Total 5 (delta 0), reused 0 (delta 0)
remote: ERROR: detected a split file
To upstream
! [remote rejected] master -> master (pre-receive hook declined)
Может быть, это помогает? Без обширного тестирования я бы беспокоился о ложных срабатываниях с этим решением.