Я задав таке саме запитання деякий час назад (не тут, просто загалом) і, нарешті, вийшов із дуже схожим рішенням із пропозицією ОП. Спочатку я надам прямі відповіді на запитання 1 2 і 3, а потім опублікую рішення, яке я закінчив.
- Насправді є кілька недоліків запропонованого рішення, головним чином щодо збільшення потенціалу забруднення сховищ або випадкового додавання дублікатів файлів, коли вони знаходяться в стані «симпосилання Windows». (Детальніше про це під "обмеженнями" нижче.)
- Так, сценарій після оформлення замовлення реалізований! Можливо, не як буквальний післяшаговий
git checkout
крок, але рішення, наведене нижче, досить добре задовольнило мої потреби, що буквальний сценарій після оформлення замовлення не був необхідним.
- Так!
Рішення:
Наші розробники знаходяться в майже такій самій ситуації, як і у ОП: суміш хостів, схожих на 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
дзвінків, звичайно) - не більше башизмів !
- Підтримуються каталоги та файли з пробілами в них.
- Нульові та ненульові коди статусу виходу (для повідомлення про успіх / невдачу запитуваної команди відповідно) тепер належним чином зберігаються / повертаються.
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
Удачі!