Некоторое время назад я задавал этот же вопрос (не здесь, просто в целом), и в итоге я нашел решение, очень похожее на предложение ОП. Сначала я предоставлю прямые ответы на вопросы 1, 2 и 3, а затем опубликую решение, которое я в итоге использовал.
- У предлагаемого решения действительно есть несколько недостатков, в основном в отношении увеличения вероятности загрязнения хранилища или случайного добавления дубликатов файлов, когда они находятся в состоянии «символьная ссылка Windows». (Подробнее об этом в разделе «Ограничения» ниже.)
- Да, скрипт после оформления заказа реализуем! Может быть, не как буквальный
git checkout
шаг, но решение, приведенное ниже, удовлетворит мои потребности достаточно хорошо, чтобы буквальный сценарий после оформления заказа не был необходим.
- Да!
Решение:
Наши разработчики находятся практически в той же ситуации, что и OP: смесь Windows и Unix-подобных хостов, репозиториев и субмодулей со многими символическими ссылками git и никакой собственной (пока) поддержки в версии MsysGit для интеллектуальной обработки этих символических ссылок на хостах Windows ,
Спасибо Джошу Ли за то, что он указал на тот факт, что git передает символические ссылки в специальном файловом режиме 120000
. С помощью этой информации можно добавить несколько псевдонимов git, которые позволяют создавать и обрабатывать символические ссылки git на хостах Windows.
Создание символических ссылок git в Windows
git config --global alias.add-symlink '!'"$(cat <<'ETX'
__git_add_symlink() {
if [ $# -ne 2 ] || [ "$1" = "-h" ]; then
printf '%b\n' \
'usage: git add-symlink <source_file_or_dir> <target_symlink>\n' \
'Create a symlink in a git repository on a Windows host.\n' \
'Note: source MUST be a path relative to the location of target'
[ "$1" = "-h" ] && return 0 || return 2
fi
source_file_or_dir=${1#./}
source_file_or_dir=${source_file_or_dir%/}
target_symlink=${2#./}
target_symlink=${target_symlink%/}
target_symlink="${GIT_PREFIX}${target_symlink}"
target_symlink=${target_symlink%/.}
: "${target_symlink:=.}"
if [ -d "$target_symlink" ]; then
target_symlink="${target_symlink%/}/${source_file_or_dir##*/}"
fi
case "$target_symlink" in
(*/*) target_dir=${target_symlink%/*} ;;
(*) target_dir=$GIT_PREFIX ;;
esac
target_dir=$(cd "$target_dir" && pwd)
if [ ! -e "${target_dir}/${source_file_or_dir}" ]; then
printf 'error: git-add-symlink: %s: No such file or directory\n' \
"${target_dir}/${source_file_or_dir}" >&2
printf '(Source MUST be a path relative to the location of target!)\n' >&2
return 2
fi
git update-index --add --cacheinfo 120000 \
"$(printf '%s' "$source_file_or_dir" | git hash-object -w --stdin)" \
"${target_symlink}" \
&& git checkout -- "$target_symlink" \
&& printf '%s -> %s\n' "${target_symlink#$GIT_PREFIX}" "$source_file_or_dir" \
|| return $?
}
__git_add_symlink
ETX
)"
Использование:, git add-symlink <source_file_or_dir> <target_symlink>
где аргумент, соответствующий исходному файлу или каталогу, должен принимать форму пути относительно целевой символической ссылки. Вы можете использовать этот псевдоним так же, как обычноln
.
Например, дерево хранилища:
dir/
dir/foo/
dir/foo/bar/
dir/foo/bar/baz (file containing "I am baz")
dir/foo/bar/lnk_file (symlink to ../../../file)
file (file containing "I am file")
lnk_bar (symlink to dir/foo/bar/)
Может быть создан в Windows следующим образом:
git init
mkdir -p dir/foo/bar/
echo "I am baz" > dir/foo/bar/baz
echo "I am file" > file
git add -A
git commit -m "Add files"
git add-symlink ../../../file dir/foo/bar/lnk_file
git add-symlink dir/foo/bar/ lnk_bar
git commit -m "Add symlinks"
Замена символьных ссылок git на жесткие ссылки NTFS + соединения
git config --global alias.rm-symlinks '!'"$(cat <<'ETX'
__git_rm_symlinks() {
case "$1" in (-h)
printf 'usage: git rm-symlinks [symlink] [symlink] [...]\n'
return 0
esac
ppid=$$
case $# in
(0) git ls-files -s | grep -E '^120000' | cut -f2 ;;
(*) printf '%s\n' "$@" ;;
esac | while IFS= read -r symlink; do
case "$symlink" in
(*/*) symdir=${symlink%/*} ;;
(*) symdir=. ;;
esac
git checkout -- "$symlink"
src="${symdir}/$(cat "$symlink")"
posix_to_dos_sed='s_^/\([A-Za-z]\)_\1:_;s_/_\\\\_g'
doslnk=$(printf '%s\n' "$symlink" | sed "$posix_to_dos_sed")
dossrc=$(printf '%s\n' "$src" | sed "$posix_to_dos_sed")
if [ -f "$src" ]; then
rm -f "$symlink"
cmd //C mklink //H "$doslnk" "$dossrc"
elif [ -d "$src" ]; then
rm -f "$symlink"
cmd //C mklink //J "$doslnk" "$dossrc"
else
printf 'error: git-rm-symlink: Not a valid source\n' >&2
printf '%s =/=> %s (%s =/=> %s)...\n' \
"$symlink" "$src" "$doslnk" "$dossrc" >&2
false
fi || printf 'ESC[%d]: %d\n' "$ppid" "$?"
git update-index --assume-unchanged "$symlink"
done | awk '
BEGIN { status_code = 0 }
/^ESC\['"$ppid"'\]: / { status_code = $2 ; next }
{ print }
END { exit status_code }
'
}
__git_rm_symlinks
ETX
)"
git config --global alias.rm-symlink '!git rm-symlinks' # for back-compat.
Использование:
git rm-symlinks [symlink] [symlink] [...]
Этот псевдоним может удалить символические ссылки git по одному или сразу за один раз. Симлинки будут заменены на жесткие ссылки NTFS (в случае файлов) или соединения NTFS (в случае каталогов). Преимущество использования жестких ссылок + переходов над «настоящими» символьными ссылками NTFS состоит в том, что для их создания не требуются повышенные разрешения UAC.
Чтобы удалить символические ссылки из подмодулей, просто используйте встроенную поддержку git для их перебора:
git submodule foreach --recursive git rm-symlinks
Но для каждого такого радикального действия приятно иметь разворот ...
Восстановление символьных ссылок git в Windows
git config --global alias.checkout-symlinks '!'"$(cat <<'ETX'
__git_checkout_symlinks() {
case "$1" in (-h)
printf 'usage: git checkout-symlinks [symlink] [symlink] [...]\n'
return 0
esac
case $# in
(0) git ls-files -s | grep -E '^120000' | cut -f2 ;;
(*) printf '%s\n' "$@" ;;
esac | while IFS= read -r symlink; do
git update-index --no-assume-unchanged "$symlink"
rmdir "$symlink" >/dev/null 2>&1
git checkout -- "$symlink"
printf 'Restored git symlink: %s -> %s\n' "$symlink" "$(cat "$symlink")"
done
}
__git_checkout_symlinks
ETX
)"
git config --global alias.co-symlinks '!git checkout-symlinks'
Использование:, git checkout-symlinks [symlink] [symlink] [...]
которое отменяет git rm-symlinks
, эффективно восстанавливая хранилище в его естественное состояние (за исключением ваших изменений, которые должны остаться нетронутыми).
И для подмодулей:
git submodule foreach --recursive git checkout-symlinks
Ограничения:
Каталоги / файлы / символические ссылки с пробелами в пути должны работать. Но вкладки или новые строки? YMMV… (Под этим я подразумеваю: не делайте этого, потому что это не будет работать.)
Если вы или другие забыли git checkout-symlinks
сделать что-то с потенциально широкими последствиями, например git add -A
, локальное хранилище может оказаться в загрязненном состоянии.
Используя наш «пример репо» из ранее:
echo "I am nuthafile" > dir/foo/bar/nuthafile
echo "Updating file" >> file
git add -A
git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: dir/foo/bar/nuthafile
# modified: file
# deleted: lnk_bar # POLLUTION
# new file: lnk_bar/baz # POLLUTION
# new file: lnk_bar/lnk_file # POLLUTION
# new file: lnk_bar/nuthafile # POLLUTION
#
Упс ...
По этой причине было бы неплохо включить эти псевдонимы в качестве шагов для пользователей Windows до и после создания проекта, а не после проверки или перед отправкой. Но каждая ситуация отличается. Эти псевдонимы были достаточно полезны для меня, так что настоящее решение после проверки не было необходимости.
Надеюсь, это поможет!
Ссылки:
http://git-scm.com/book/en/Git-Internals-Git-Objects
http://technet.microsoft.com/en-us/library/cc753194
Последнее обновление: 2019-03-13
- Соответствие POSIX (ну, кроме тех
mklink
звонков, конечно) - больше никаких Bashisms !
- Каталоги и файлы с пробелами в них поддерживаются.
- Нулевой и ненулевой коды состояния выхода (для сообщения об успешном / неудачном выполнении запрошенной команды соответственно) теперь должным образом сохраняются / возвращаются.
add-symlink
Псевдоним теперь работает больше как LN (1) и может быть использован из любого каталога в хранилище, а не только репозиторий корневого каталога.
rm-symlink
Псевдоним ( в единственном числе) был заменен на rm-symlinks
псевдониме (множественное число), которая в настоящее время принимает несколько аргументов (или без аргументов вообще, который находит все симлинка на протяжении всего хранилища, как и прежде) для избирательного превращения GIT символических ссылок на NTFS жестких ссылок + переходы ,
checkout-symlinks
Псевдоним также был обновлен , чтобы принять несколько аргументов (или вообще никакого, == все) для селективного обращения вышеуказанных преобразований.
Последнее замечание: хотя я тестировал загрузку и запуск этих псевдонимов с использованием Bash 3.2 (и даже 3.1) для тех, кто по-прежнему может зависнуть в таких древних версиях по ряду причин, имейте в виду, что такие старые версии, как эти, печально известны своим парсером ошибок. Если у вас возникли проблемы при попытке установить любой из этих псевдонимов, первое, на что вы должны обратить внимание - это обновить вашу оболочку (для Bash проверьте версию с помощью CTRL + X, CTRL + V). В качестве альтернативы, если вы пытаетесь установить их, вставив их в эмулятор терминала, вам может повезти, вставив их в файл и используя его вместо этого, например, как
. ./git-win-symlinks.sh
Удачи!