Windows中的Git符号链接

Ken*_*awa 227 windows git symlink

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

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

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

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

问题:

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

Mar*_* G. 182

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

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

解决方案:

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

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

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

    更新2014-11-12(见下文)

    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
    )"
    
    Run Code Online (Sandbox Code Playgroud)

    使用方法:git add-symlink <source_file_or_dir> <target_symlink>,其中ln相对于参考(相对于git checkout-symlinks [symlink] [symlink] [...])的文件或目录的当前位置链接到,并且git rm-symlinks是一个相对参考(相对于库的根),以链接的期望的目的地.

    例如,存储库树:

    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/)
    
    Run Code Online (Sandbox Code Playgroud)

    可以在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"
    
    Run Code Online (Sandbox Code Playgroud)
  2. 用NTFS hardlinks + junctions替换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.
    
    Run Code Online (Sandbox Code Playgroud)

    用法:

    git rm-symlinks [symlink] [symlink] [...]
    
    Run Code Online (Sandbox Code Playgroud)

    这将逐个删除git符号链接,将其替换为NTFS硬链接(在文件的情况下)或NTFS联结(在目录的情况下).在"真正的"NTFS符号链接上使用硬链接+联结的好处是,为了创建它们,不需要提升的UAC权限.最后,在您自己的闲暇时,您可以选择取消标记为"已删除"的符号链接(或不包含)git checkout-symlinks.

    为方便起见,您还可以运行:

    git submodule foreach --recursive git rm-symlinks
    
    Run Code Online (Sandbox Code Playgroud)

    这将删除当前存储库中的所有git符号链接,根据需要将它们替换为硬链接+联结,并自动标记要忽略的更改git add -A.

    要从子模块中删除符号链接,只需使用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'
    
    Run Code Online (Sandbox Code Playgroud)

    但是,对于像这样的每一个激烈的行动,逆转是很好的...

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

    git submodule foreach --recursive git checkout-symlinks
    
    Run Code Online (Sandbox Code Playgroud)

    用法:mklink,撤消add-symlink,有效地将存储库恢复到其自然状态(除了您的更改,保持完整).

    对于子模块:

    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
    #
    
    Run Code Online (Sandbox Code Playgroud)
  4. 限制:

    1. 只能从repo的根目录运行,否则,奇怪的事情会发生......
    2. 键入其中一个别名时,基于选项卡的自动完成功能会中断
    3. 如果人们rm-symlink在做某事之前忘了rm-symlinks,他们就会污染回购!

      使用之前的"示例回购":

      . ./git-win-symlinks.sh
      
      Run Code Online (Sandbox Code Playgroud)

      哎呦...

      因此,将这些别名作为在构建项目之前和之后为Windows用户执行的步骤而不是在结帐之后或推送之前包含这些别名是很好的.但每种情况都不同.这些别名对我来说足够有用,因此没有必要使用真正的结账后解决方案.

希望有所帮助!

参考文献:

http://git-scm.com/book/en/Git-Internals-Git-Objects

http://technet.microsoft.com/en-us/library/cc753194

UPDATE 2014年11月12日:因为我个人只做过大量使用的checkout-symlinksgit checkout上面的别名,我设法忽视在一个相当讨厌的错误120000的别名.以前,git add-symlink <source_file_or_dir> <target_symlink>没有传递给ln负责创建git symlink文件的语句,该文件稍后将作为操作的一部分添加到临时区域git checkout-symlinks [symlink] [symlink] [...].这意味着git rm-symlinks在使用创建的所有git符号链接中添加了一个尾随换行符(在Windows主机上)git checkout-symlinks.虽然这些git符号链接在Windows主机上仍然是"可移动的" git add -A,但是如果它们曾经被提交到公共存储库并且后来克隆在真正的基于posix的系统上,那么这些链接总是会在另一方面被破坏.此问题已得到修复,mklink现在应按预期工作.

  • 非凡的答案,非常感谢! (8认同)
  • 有什么办法可以使用钩子自动执行这些脚本吗? (2认同)

Jos*_*Lee 97

您可以通过查找具有以下模式的文件来查找符号链接120000:

git ls-files -s | awk '/120000/{print $4}'
Run Code Online (Sandbox Code Playgroud)

更换链接后,我建议将它们标记为未更改git update-index --assume-unchanged,而不是将其列入.git/info/exclude.

  • 他是肯.你介意分享你的脚本来检查符号链接文本文件,并使用mklink将它们替换为Windows上的符号链接.虽然这实际上对我们有用,但是 - 保持不变的部分却没有.切换到另一个分支git说symlink文件已更改,需要先提交,而git status表示没有更改..任何想法? (6认同)
  • 这是我刚刚放在一起的PowerShell - https://gist.github.com/ferventcoder/7995025 (6认同)
  • @flungo打印第四列比使用GNU awk更便携.例如:`git ls-files -s | grep'^ 12'| cut -f2`(第二个制表符分隔的列;其他列以空格分隔) (3认同)
  • 我不得不为msysgit用gawk替换awk,但是否则它工作得很好.谢谢! (2认同)

sir*_*lot 65

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

在此输入图像描述

  • 在Windows 10 Creators Update的"开发人员模式"中,[管理员权限不是必需的](https://blogs.windows.com/buildingapps/2016/12/02/symlinks-windows-10/).感谢@dennis [他的评论](/sf/ask/414207461/#comment69184768_5930681). (9认同)
  • @JanAagaard您必须像这样克隆它:`git clone -c core.symlinks = true <URL>`在Windows上,您必须以管理员权限运行它. (8认同)
  • @ARF"启动gpedit.msc(即组策略编辑器)并将帐户添加到计算机配置\ Windows设置\安全设置\本地策略\用户权限分配\创建符号链接." (6认同)
  • @sirlunchalot感谢您的帮助.我已经意识到我的问题是我的用户是Administrators组的一部分,并且该属性对这些用户没有影响.他们需要UAC提升,git不这样做. (2认同)

Cam*_*ind 53

2020/2021 TL;DR 答案

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

切换分支将强制重新创建丢失的符号链接。

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

  • 如果您不想启用开发人员模式,只需为您的帐户设置符号链接权限:按 Win + R,输入“gpedit.msc”,然后单击“确定”。然后导航到 **本地比较策略 &gt; 计算机配置 &gt; Windows 设置 &gt; 安全设置 &gt; 本地策略 &gt; 用户权限分配 &gt; 创建符号链接**,打开该选项并添加您的用户帐户。 (9认同)
  • 无需重新克隆,修复 **repo 的** 配置就足够了。如果 `git config core.symlinks` 在您的存储库中仍然返回 **false**,而 `git config --global core.symlinks` 则返回 **true**,这会有所帮助。运行 `git config --unset core.symlinks`;注意:没有`​​--global`! (2认同)

djs*_*djs 15

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

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

我做了一个快速搜索,正在积极完成这项工作,请参阅问题224.

  • 2016年12月宣布,Windows 10中的符号链接不再是管理员操作.https://blogs.windows.com/buildingapps/2016/12/02/symlinks-windows-10/#oTepts0M8148xRoj.97 (8认同)
  • 更新:由于上述原因,该问题已作为wontfix关闭.讨论表明修补程序可以接受修补程序的更多工作(例如,只有在它们工作时使用符号链接). (2认同)
  • 答:)目前msysgit根本不支持符号链接 - 所以为什么不让它检测"哦你在使用NTFS的vista上让我使用符号链接"或"哦,你是在支持NTFS结点的操作系统上,让我使用那些",或者"哦,你在Windows 98/fat32上,让我后退到没有这个功能,而是给你一个警告!" 然后是B.)几乎所有的微软开发者.如果你不以管理员身份运行它们,那么工具就无法正常工作(至少不适用于所有功能) - IT中的每个人都知道开发人员需要在他们自己的盒子上成为管理员. (2认同)

Sim*_*mon 14

因为GIT已经改变了,因为这里发布了很多这些答案是正确的说明,使得符号链接在Windows中正常工作

2018年8月


1.确保安装了git并支持符号链接

在Windows上安装git期间

2.告诉Bash创建硬链接而不是符号链接

编辑 - (git文件夹)/etc/bash.bashrc

添加到底部 - MSYS=winsymlinks:nativestrict

3.设置git config以使用符号链接

git config core.symlinks true
Run Code Online (Sandbox Code Playgroud)

要么

git clone -c core.symlinks=true <URL>
Run Code Online (Sandbox Code Playgroud)

注意:我已经尝试将其添加到全局git配置中,目前它不适合我,所以我建议将其添加到每个repo ...

4.拉回购

注意:除非您在最新版本的Windows 10中启用了开发人员模式,否则您需要以管理员身份运行bash以创建符号链接

5.重置所有符号链接(可选)如果您有现有的存储库或正在使用子模块,您可能会发现未正确创建符号链接,因此要刷新存储库中的所有符号链接,您可以运行这些命令.

find -type l -delete
git reset --hard
Run Code Online (Sandbox Code Playgroud)

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


Ora*_*ech 11

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

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

现在,在Windows 10 Creators Update中,用户(具有管理员权限)可以首先启用开发人员模式,然后计算机上的任何用户都可以运行mklink命令而无需升级命令行控制台.

是什么推动了这一变化?符号链接的可用性和使用对于现代开发人员来说是一个大问题:

许多流行的开发工具,如git和包管理器,如npm,分别在创建repos或包时识别并保留符号链接.当那些repos或包然后在别处恢复时,符号链接也会被恢复,从而确保不浪费磁盘空间(以及用户的时间).

容易忽略"Creator更新"的所有其他声明,但如果启用开发者模式,则可以创建没有提升权限的符号链接.您可能必须重新安装并确保启用支持,因为默认情况下不支持.

默认情况下不启用符号链接

  • “gpedit.msc”-&gt;“本地计算机策略”-&gt;“计算机配置”-&gt;“Windows 设置”-&gt;“安全设置”-&gt;“本地策略”-&gt;“用户权限分配”是规范的分配方式像“SeCreateSymbolicLink”这样的用户权限和多年来的朋友。除了资源工具包或 PowerShell 中的“ntrights.exe”... (3认同)

the*_*man 10

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

因此,假设您正在使用回购'比较在*nix上托管您的网站与在win上托管.存储在您的回购协议"的内容,可以说/httpRepoContentc:\httpRepoContent这个被认为是通过GIT,SVN等同步的文件夹

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

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

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


Bri*_*ite 8

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

export CYGWIN="$CYGWIN winsymlinks:native"
Run Code Online (Sandbox Code Playgroud)

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

就个人而言,我将此用于由Windows应用程序(即非CygWin)导航的符号链接,因为这增加了难度.

有关此选项的更多信息,请参阅此问题:如何在Windows 7中与cygwin建立符号链接


Sta*_*uff 7

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

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

Git 维基条目

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

在此处输入图片说明

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

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

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

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

发行说明条目

符号链接也不断出现在发行说明中。从 2.30.0 开始,这里仍然列为“已知问题”:

在 1703 之前的 Windows 10 上,或关闭开发人员模式时,使用符号链接克隆存储库时需要特殊权限,因此默认情况下禁用对符号链接的支持。使用git clone -c core.symlinks=true <URL>,使之详情,请参阅这里


Qua*_*tax 6

这是一个基于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
Run Code Online (Sandbox Code Playgroud)

  • 当已经存在如此冗长和冗长的答案时,未记录的答案实际上没有多大用处。 (2认同)