Windows中的Git符号链接

时间:2011-05-06 21:33:50

标签: windows git symlink

我们的开发人员使用基于Windows和Unix的操作系统。因此,在Unix机器上创建的符号链接成为Windows开发人员的问题。在windows(msysgit)中,符号链接将转换为文本文件,其中包含指向其所指向文件的路径。相反,我想将符号链接转换为实际的Windows符号链接。

我需要的(更新的)解决方案是:

  • 编写一个post-checkout脚本,以递归方式查找“symlink”文本文件。
  • 将它们替换为windows符号链接(使用mklink),其名称和扩展名与虚拟“symlink”相同
  • 通过在.git / info / exclude
  • 中添加条目来忽略这些Windows符号链接

我没有实现这一点,但我相信这是解决这个问题的可靠方法。

问题:

  1. 你对这种做法有什么不利之处?
  2. 这个结账后脚本是否可以实现?即,我可以递归地找出git创建的虚拟“符号链接”文件吗?
  3. 有没有人已经处理过这样的剧本?

14 个答案:

答案 0 :(得分:171)

我曾经问过这个完全相同的问题(不是在这里,一般情况下),最终提出了一个非常类似于OP的命题的解决方案。首先,我将提供问题的直接答案1 2& 3,然后我将发布我最终使用的解决方案。

  1. 建议的解决方案确实存在一些缺点,主要是因为存储库污染的可能性增加,或者在他们的Windows符号链接中意外添加了重复文件。状态。 (更多关于这个"限制"下面。)
  2. 是的,结账后的脚本是可以实现的!也许不是作为一个文字帖子 - git checkout步骤,但下面的解决方案已经很好地满足了我的需求,因此不需要文字的结账后脚本。
  3. 是!
  4. 解决方案:

    我们的开发人员与OP的情况大致相同:Windows和类Unix主机的混合,存储库和带有许多git符号链接的子模块,并且在MsysGit的发布版本中还没有本机支持在Windows主机上智能地处理这些符号链接。

    感谢Josh Lee指出git使用特殊文件模式120000提交符号链接这一事实。有了这些信息,就可以添加一些git别名,允许在Windows主机上创建和操作git符号链接。

    1. 在Windows上创建git符号链接

      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. 用NTFS硬链接+联结替换git符号链接

      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联结(在目录的情况下)。使用硬链接+结点的好处&#34; true&#34; NTFS符号链接是提升的UAC权限,以便创建它们。

      要从子模块中删除符号链接,只需使用git的内置支持来迭代它们:

      git submodule foreach --recursive git rm-symlinks
      

      但是,对于像这样的每一次激烈的行动,反转都很好......

    3. 在Windows上恢复git符号链接

      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),则本地存储库最终可能会处于污染状态。

        使用我们的&#34;示例repo&#34;从以前开始:

        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用户执行的步骤而不是在结帐之后或推送之前包含这些别名是很好的。但每种情况都不同。这些别名对我来说非常有用,因此没有必要使用真正的结账后解决方案。

    5. 希望有所帮助!

      <强>参考文献:

      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 hardlinks + junctions。
      • checkout-symlinks别名也已更新为接受多个参数(或根本没有参数,==所有内容),以选择性地反转上述转换。

      最后注意事项虽然我确实使用Bash 3.2(甚至3.1)测试加载和运行这些别名,但是对于那些可能因为各种原因仍然困在这些古老版本上的人来说,请注意这些旧的版本因其解析器错误而臭名昭着。如果您在尝试安装任何这些别名时遇到问题,首先应该考虑升级shell(对于Bash,请使用CTRL + X,CTRL + V检查版本)。或者,如果您尝试通过将它们粘贴到终端模拟器中来安装它们,那么您可能会更幸运地将它们粘贴到文件中并改为采购它,例如,如

      . ./git-win-symlinks.sh
      
      祝你好运!

答案 1 :(得分:94)

您可以通过查找模式为120000的文件找到符号链接,可能使用以下命令:

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

更换链接后,我建议您使用git update-index --assume-unchanged将其标记为未更改,而不是将其列在.git/info/exclude中。

答案 2 :(得分:55)

最新版本的git scm(testet 2.11.1)允许启用符号链接。但是您必须再次使用符号链接克隆存储库git clone -c core.symlinks=true <URL>。您需要使用管理员权限运行此命令。也可以在Windows上使用mklink创建符号链接。 查看wiki

enter image description here

答案 3 :(得分:26)

2020年答案

  1. 在Windows 10中启用“开发人员模式”-赋予mklink权限
  2. 确保在git中启用符号链接
    • git config --global core.symlinks true
    • 在安装msysgit时选中复选框

切换分支将迫使重新创建缺少的符号链接。

注意,Windows上对git中的Symlinks的支持相对较新。 有些错误仍然会影响某些git客户端。 值得注意的是,由于fixed regression in ligbit2,在某些程序中带有相对(..)路径的符号链接被破坏了。 例如,GitKraken受此影响。

答案 4 :(得分:14)

它应该在msysgit中实现,但有两个缺点:

  • 符号链接仅适用于Windows Vista及更高版本(2011年应该不是问题,但它是......),因为旧版本仅支持目录连接。
  • (最重要的一点)Microsoft认为符号链接存在安全风险,因此只有管理员可以默认创建它们。您需要提升git进程的权限或使用fstool在您处理的每台计算机上更改此行为。

我进行了快速搜索,并且正在积极完成这项工作,请参阅问题224

答案 5 :(得分:9)

我建议你不要在repo中使用符号链接'。将实际内容存储在repo'中,然后将符号链接放在指向内容的repo'旁边。

因此,假设您正在使用回购'比较在* nix上托管您的网站与在win上托管。将内容存储在您的repo中,让我们说/httpRepoContentc:\httpRepoContent,这是通过GIT,SVN等同步的文件夹。

然后,替换您的网络服务器的内容文件夹(/var/wwwc:\program files\web server\www {名称并不重要,如果必须,请进行编辑}),并使用指向回购邮件内容的符号链接。 Web服务器会将内容视为实际位于“正确”的位置,但您可以使用源代码控制。

但是,如果你需要在repo'中使用符号链接,你将需要查看某种类型的前/后提交脚本。我知道你可以使用它们来做事情,比如通过格式化程序解析代码文件,所以应该可以在平台之间转换符号链接。

如果有人知道如何为常用源代码控制SVN GIT MG学习这些脚本,那么请添加评论。

答案 6 :(得分:9)

简短回答:如果您可以启用开发者模式,现在可以很好地支持它们。

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

  

现在,在Windows 10 Creators Update中,用户(具有管理员权限)可以   首先启用开发者模式,然后机器上的任何用户都可以运行   没有提升命令行控制台的mklink命令。

     

是什么推动了这一变化?符号链接的可用性和使用是一个很大的问题   与现代开发商打交道:

     

许多流行的开发工具,如git和包管理器,如npm   在创建repos或包时识别并保留符号链接,   分别。然后恢复那些repos或包   在其他地方,符号链接也会被恢复,确保磁盘空间(和   用户的时间)没有浪费。

很容易忽略&#34;创建者更新&#34;的所有其他公告,但如果您启用开发者模式,则可以创建没有提升权限的符号链接。您可能需要重新安装并确保启用支持,因为默认情况下不支持。

Symbolic Links aren't enabled by default

答案 7 :(得分:8)

自从发布了这些答案以来,GIT发生了变化,这是使符号链接自

开始在Windows中正常工作的正确说明。

2018年8月


1。确保已安装具有gitlink支持的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

注意:这将重置自上次提交以来的所有更改,因此请确保您已先提交

答案 8 :(得分:7)

对于在Vista,Win7或更高版本上使用CygWin的用户,原生git命令可以创建&#34;正确的&#34; Windows应用程序(例如Android Studio)可识别的符号链接。您只需将CYGWIN环境变量设置为包含winsymlinks:nativewinsymlinks:nativestrict

export CYGWIN="$CYGWIN winsymlinks:native"

这方面的缺点(以及一个重要的一点)是CygWin shell必须是&#34;以管理员身份运行&#34;为了使它具有创建这种符号链接所需的操作系统权限。但是,一旦创建了它们,使用它们就不需要特殊权限。只要其他开发人员在存储库中没有更改它们,git之后就可以正常用户权限运行。

就我个人而言,我只使用 作为由Windows应用程序(即非CygWin)导航的符号链接,因为这会增加难度。

有关此选项的详细信息,请参阅此问题:How to make symbolic link with cygwin in Windows 7

答案 9 :(得分:7)

我刚刚尝试使用 Git 2.30.0(2020 年 12 月 28 日发布)。

这不是一个完整的答案,但仍然是一些有用的花絮。 (随意蚕食你自己的答案。)

Git 维基条目

安装 Git for Windows 时有一个文档链接

enter image description here

此链接将您带到此处:https://github.com/git-for-windows/git/wiki/Symbolic-Links -- 这是一个相当长的讨论。

仅供参考:至少有三种“链接类型”

只是为了强调这个维基条目的一个重要方面:我不知道这一点,但是有几种方法表面上都是“某种”符号链接,但在技术层面上却非常不同:

  • git bash 的“ln -s”
    这只是复制的东西。好家伙。这出乎我的意料。
    (仅供参考:Plain Cygwin 不会这样做。Mobaxterm 不会这样做。相反,它们都创建了一些其 stat 命令实际上识别为“符号链接”的东西。)
  • cmd.exe 的内置“mklink”命令,带有“/D”参数
    这将创建一个目录符号链接。 (见MS Docs
  • cmd.exe 的内置“mklink”命令,带有“/J”参数
    这会创建一个目录结点 AKA 软链接 AKA 重新分析点。 (见MS Docs

发行说明条目

符号链接也在 the release notes 中不断弹出。截至 2.30.0,此处仍列为“已知问题”:

<块引用>

在 1703 之前的 Windows 10 上,或者当开发者模式关闭时,克隆带有符号链接的存储库时需要特殊权限,因此默认情况下禁用对符号链接的支持。使用 git clone -c core.symlinks=true <URL> 启用它,查看详细信息 here

答案 10 :(得分:5)

这是一个批处理脚本,用于根据Josh Lee的回答转换存储库中的符号链接,仅用于文件。带有管理员权限的额外检查的脚本位于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

答案 11 :(得分:2)

我在文档根目录和git repo目录之间一直使用sym链接。我喜欢将它们分开。在Windows上我使用mklink / j选项。结点似乎让git正常运行:

label.GetComponent<Text> ().font = Resources.GetBuiltinResource(typeof(Font), "Arial.ttf") as Font;

例如:

>mklink /j <location(path) of link> <source of link>

答案 12 :(得分:2)

我一直在寻找一个简单的解决方案来处理Windows上的unix符号链接。非常感谢上面的Git别名。 可以对rm-symlink进行一个小的优化,这样它就不会删除目标文件夹中的文件,以防别名第二次意外运行。请在循环中观察新的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 

答案 13 :(得分:1)

我们使用的一个简单技巧是连续两次调用git add --all

例如,我们的Windows 7提交脚本调用:

$ git add --all
$ git add --all

第一个add将链接视为文本,并添加要删除的文件夹。

第二个add正确遍历链接,并通过恢复文件撤消删除。

它不像其他一些提议的解决方案那么优雅,但它是对我们添加了符号链接的一些遗留环境的简单修复。