Git Symlinks в Windows - PullRequest
       30

Git Symlinks в Windows

221 голосов
/ 07 мая 2011

Наши разработчики используют сочетание ОС Windows и Unix.Поэтому символические ссылки, созданные на машинах Unix, становятся проблемой для разработчиков Windows.В windows (msysgit) символическая ссылка преобразуется в текстовый файл с путем к файлу, на который она указывает.Вместо этого я хотел бы преобразовать символическую ссылку в фактическую символическую ссылку Windows.

( updated ) решение, которое у меня есть, это:

  • Написатьскрипт post-checkout, который будет рекурсивно искать текстовые файлы "symlink".
  • Заменить их символьной ссылкой Windows (используя mklink) с тем же именем и расширением, что и у фиктивной "символической ссылки"
  • Игнорировать символическую ссылку Windowsдобавив запись в .git / info / exclude

Я не реализовал это, но я считаю, что это надежный подход к этой проблеме.

Вопросы:

  1. Какие, если таковые имеются, недостатки у этого подхода?
  2. Является ли этот сценарий после оформления заказа даже реализуемым?то есть я могу рекурсивно узнать, какие фиктивные файлы "symlink" создает git?
  3. Кто-нибудь уже работал с таким скриптом?

Ответы [ 12 ]

176 голосов
/ 26 мая 2013

Я задал этот же вопрос некоторое время назад (не здесь, просто в целом) и в итоге придумал очень похожее решение для предложения ОП.Сначала я предоставлю прямые ответы на вопросы 1, 2 и 3, а затем опубликую решение, которое я использовал в итоге.

  1. У предлагаемого решения действительно есть несколько недостатков, в основном касающихсяповышенный потенциал загрязнения хранилища или случайного добавления дубликатов файлов, когда они находятся в состоянии «символьная ссылка Windows».(Подробнее об этом в разделе «Ограничения» ниже.)
  2. Да, сценарий после оформления заказа реализуем!Возможно, не как буквальный шаг после - git checkout, но приведенное ниже решение достаточно хорошо удовлетворило мои потребности, так что буквальный сценарий после проверки не был необходим.
  3. Да!

Решение:

Наши разработчики находятся практически в такой же ситуации, что и OP: смесь Windows и Unix-подобных хостов, репозиториев и подмодулей со многими символическими ссылками git и никакой собственной (пока) поддержки в версии выпускаMsysGit за интеллектуальную обработку этих символических ссылок на хостах Windows.

Спасибо Джошу Ли за то, что он указал на тот факт, что git передает символические ссылки с помощью специального файлового режима 120000.С помощью этой информации можно добавить несколько псевдонимов git, которые позволяют создавать символические ссылки git на хостах Windows.

  1. Создание символических ссылок 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"
    
  2. Замена символьных ссылок 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'sвстроенная поддержка итерации по ним:

    git submodule foreach --recursive git rm-symlinks
    

    Но, для каждого такого радикального действия подобное изменение приятно иметь ...

  3. Восстановление символьных ссылок 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
    
  4. Ограничения:

    • Каталоги / файлы / символические ссылки с пробелами в путях должны работать.Но вкладки или новые строки?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 / ru / 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

Удачи!

94 голосов
/ 09 мая 2011

Вы можете найти символические ссылки, выполнив поиск файлов в режиме 120000, возможно, с помощью этой команды:

git ls-files -s | awk '/120000/{print $4}'

После замены ссылок я бы рекомендовал пометить их как неизмененные с помощью git update-index --assume-unchanged, а не перечислять их в .git/info/exclude.

59 голосов
/ 09 февраля 2017

Последняя версия git scm (testet 2.11.1) позволяет включать символические ссылки.Но вы должны снова клонировать хранилище с символическими ссылками git clone -c core.symlinks=true <URL>.Вам нужно запустить эту команду с правами администратора.Также можно создавать символические ссылки в Windows с помощью mklink.Проверьте вики .

enter image description here

14 голосов
/ 09 мая 2011

Это должно быть реализовано в msysgit, но есть два недостатка:

  • Символические ссылки доступны только в Windows Vista и более поздних версиях (не должно быть проблемой в 2011 году, и все же это ...), поскольку более старые версии поддерживают только соединения каталогов.
  • (большой) Microsoft считает символические ссылки угрозой безопасности, поэтому только администраторы могут создавать их по умолчанию. Вам нужно будет повысить привилегии процесса git или использовать fstool для изменения этого поведения на каждой машине, на которой вы работаете.

Я выполнил быстрый поиск, и над этим активно ведется работа, см. Вопрос 224 .

9 голосов
/ 30 августа 2018

так как с GIT все изменилось, так как многие из этих ответов были опубликованы здесь, это правильные инструкции для правильной работы символических ссылок в windows по состоянию на

АВГУСТ 2018


1.Убедитесь, что git установлен с поддержкой символических ссылок

During the install of git on windows

2.Скажите Bash создать жесткие ссылки вместо символических ссылок

РЕДАКТИРОВАТЬ - (папка git) /etc/bash.bashrc

ДОБАВИТЬ В ДНО - MSYS=winsymlinks:nativestrict

3.Установите в git config использование символических ссылок

git config core.symlinks true

или

git clone -c core.symlinks=true <URL>

ПРИМЕЧАНИЕ. Я попытался добавить это в глобальный git config и в данный моментэто не работает для меня, поэтому я рекомендую добавлять это к каждому репо ...

4.потяните репо

ПРИМЕЧАНИЕ: Если вы не включили режим разработчика в последней версии Windows 10, вам нужно запустить bash от имени администратора для создания символических ссылок

5.Сбросить все символические ссылки (необязательно) Если у вас есть репо или вы используете подмодули, вы можете обнаружить, что символические ссылки создаются неправильно, поэтому для обновления всех символических ссылок в репо вы можете выполнить эти команды.

find -type l -delete
git reset --hard

ПРИМЕЧАНИЕ: при этом будут сброшены все изменения с момента последнего коммита, поэтому убедитесь, что вы зафиксировали первый

9 голосов
/ 19 апреля 2018

Краткий ответ: теперь они хорошо поддерживаются, если вы можете включить режим разработчика.

С https://blogs.windows.com/buildingapps/2016/12/02/symlinks-windows-10/

Теперь в Windows 10 Creators Update пользователь (с администратором)права) может сначала включить режим разработчика, а затем любой пользователь на компьютере может запустить команду mklink, не поднимая консоль командной строки.

Что послужило причиной этого изменения?Доступность и использование символических ссылок очень важны для современных разработчиков:

Многие популярные инструменты разработки, такие как git и менеджеры пакетов, такие как npm, распознают и сохраняют символические ссылки при создании репозиториев или пакетов соответственно.Когда эти репозитории или пакеты затем восстанавливаются в другом месте, символические ссылки также восстанавливаются, гарантируя, что дисковое пространство (и время пользователя) не теряется.

Легко пропустить со всеми другими объявлениями "Обновление создателя », но если вы включите режим разработчика, вы сможете создавать символические ссылки без повышенных прав.Возможно, вам придется переустановить и убедиться, что поддержка включена, поскольку это не по умолчанию.

Symbolic Links aren't enabled by default

9 голосов
/ 09 февраля 2012

Я бы посоветовал вам не использовать символические ссылки в репо '.Сохраните фактический контент внутри репо, а затем разместите символические ссылки вне репо, указывающие на контент.

Итак, допустим, вы используете репо, чтобы сравнить хостинг вашего сайта на * nix с хостингом на win,Храните контент в своем репозитории ', скажем, /httpRepoContent и c:\httpRepoContent, причем это папка, которая синхронизируется через GIT, SVN и т. Д.

Затем замените папку контента на вашем веб-сервере (/var/www и c:\program files\web server\www {имена не имеют большого значения, отредактируйте, если необходимо}) с символической ссылкой на контент в вашем репо '.Веб-серверы будут видеть содержимое как на самом деле в «правильном» месте, но вы можете использовать свой источник контроля.

Однако, если вам нужно использовать символические ссылки с в репо », вам нужно будет посмотретьво что-то вроде скриптов пред / пост фиксации.Я знаю, что вы можете использовать их для выполнения таких действий, как, например, анализ файлов кода через форматтер, поэтому должна быть возможность конвертировать символические ссылки между платформами.

если кто-нибудь знает хорошее место, чтобы научиться делать эти сценарии для общих элементов управления исходным кодом, SVN GIT MG, тогда, пожалуйста, добавьте комментарий.

7 голосов
/ 26 мая 2015

Для тех, кто использует CygWin в Vista, Win7 или выше, встроенная команда git может создавать «правильные» символические ссылки, которые распознаются приложениями Windows, такими как Android Studio .Вам просто нужно установить переменную окружения CYGWIN так, чтобы она включала winsymlinks:native или winsymlinks:nativestrict следующим образом:

export CYGWIN="$CYGWIN winsymlinks:native"

Недостатком этого (и существенного при этом) является то, что оболочка CygWin имеетбыть «Запуск от имени администратора», чтобы иметь разрешения ОС, необходимые для создания таких символических ссылок.Однако после их создания никаких специальных разрешений для использования им не требуется.Пока они не изменены в хранилище другим разработчиком, git после этого работает нормально с обычными разрешениями пользователя.

Лично я использую этот только для символических ссылок, по которым перемещается Windowsприложения (т.е. не CygWin) из-за этой дополнительной сложности.

Для получения дополнительной информации об этой опции см. этот вопрос SO: Как сделать символическую ссылку с Cygwin в Windows 7

5 голосов
/ 27 февраля 2015

Вот пакетный скрипт для преобразования символических ссылок в хранилище, только для файлов, основанный на ответе Джоша Ли.Сценарий с дополнительной проверкой прав администратора: https://gist.github.com/Quazistax/8daf09080bf54b4c7641.

@echo off
pushd "%~dp0"
setlocal EnableDelayedExpansion

for /f "tokens=3,*" %%e in ('git ls-files -s ^| findstr /R /C:"^120000"') do (
     call :processFirstLine %%f
)
REM pause
goto :eof

:processFirstLine
@echo.
@echo FILE:    %1

dir "%~f1" | find "<SYMLINK>" >NUL && (
  @echo FILE already is a symlink
  goto :eof
)

for /f "usebackq tokens=*" %%l in ("%~f1") do (
  @echo LINK TO: %%l

  del "%~f1"
  if not !ERRORLEVEL! == 0 (
    @echo FAILED: del
    goto :eof
  )

  setlocal
  call :expandRelative linkto "%1" "%%l"
  mklink "%~f1" "!linkto!"
  endlocal
  if not !ERRORLEVEL! == 0 (
    @echo FAILED: mklink
    @echo reverting deletion...
    git checkout -- "%~f1"
    goto :eof
  )

  git update-index --assume-unchanged "%1"
  if not !ERRORLEVEL! == 0 (
    @echo FAILED: git update-index --assume-unchanged
    goto :eof
  )
  @echo SUCCESS
  goto :eof
)
goto :eof

:: param1 = result variable
:: param2 = reference path from which relative will be resolved
:: param3 = relative path
:expandRelative
  pushd .
  cd "%~dp2"
  set %1=%~f3
  popd
goto :eof
2 голосов
/ 26 февраля 2016

Я искал простое решение для работы с символьными ссылками Unix на окнах.Большое спасибо за вышеупомянутые псевдонимы Git.Есть небольшая оптимизация, которую можно выполнить с rm-символическими ссылками, чтобы она не удаляла файлы в папке назначения в случае, если псевдоним запускается во второй раз случайно.Обратите внимание на новое условие if в цикле, чтобы убедиться, что файл не является ссылкой на каталог до запуска логики.

git config --global alias.rm-symlinks '!__git_rm_symlinks(){
for symlink in $(git ls-files -s | egrep "^120000" | cut -f2); do
    *if [ -d "$symlink" ]; then
      continue
    fi*
    git rm-symlink "$symlink"
    git update-index --assume-unchanged "$symlink"
done
}; __git_rm_symlinksenter 
...