Windows 中的 Git 符号链接

2024-02-16

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

The (updated)我对此的解决方案是:

  • 编写一个检出后脚本,该脚本将递归地查找“符号链接”文本文件。
  • 将它们替换为 Windows 符号链接(使用mklink https://en.wikipedia.org/wiki/NTFS_links#Command-line_tools)与虚拟“符号链接”具有相同的名称和扩展名
  • 通过在文件中添加条目来忽略这些 Windows 符号链接.git/信息/排除

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

  1. 您认为这种方法有哪些缺点(如果有的话)?
  2. 这个结帐后脚本是否可以实现?即,我可以递归地找出 Git 创建的虚拟“符号链接”文件吗?

更新说明

对于大多数为符号链接而苦苦挣扎的 Windows 开发人员来说git在 Windows 上以及与 *nix 系统共享存储库的问题,这个主题是一个已解决的问题 - 一旦您更新了对 Windows 的理解mklink一点并打开开发者模式。

看到这个更现代的答案 https://stackoverflow.com/a/59761201/237059在深入研究以下 git hacks 的深入讨论之前。

旧系统:

我不久前问过这个完全相同的问题(不是在这里,只是一般性的),最后得出了一个与OP的提议非常相似的解决方案。 我将发布我最终使用的解决方案。

但首先我将直接回答 OP 的 3 个问题:

问:“您认为这种方法有哪些缺点(如果有的话)?”

答:所提出的解决方案确实有一些缺点,主要是增加存储库污染的可能性,或者在文件处于“Windows 符号链接”状态时意外添加重复文件。 (更多信息请参见下面的“限制”。)

问:“这个结帐后脚本是否可以实现?即我可以递归地找出 git 创建的虚拟“符号链接”文件吗?”

答:是的,结帐后脚本是可以实现的!也许不是字面上的后git checkout步骤,但下面的解决方案已经足够满足我的需求,不需要字面的结帐后脚本。

问:“有人已经写过这样的剧本了吗?”

A: Yes!

解决方案:

我们的开发人员的情况与 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
     )"
    

    Usage: 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.
    

    Usage:

     git rm-symlinks [symlink] [symlink] [...]
    

    此别名可以一举删除 git 符号链接或一次性删除所有符号链接。符号链接将替换为 NTFS 硬链接(对于文件)或 NTFS 连接(对于目录)。使用硬链接+连接而不是“真正的”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'
    

    Usage: git checkout-symlinks [symlink] [symlink] [...],这会撤消git rm-symlinks,有效地将存储库恢复到其自然状态(除了您的更改,这should保持完好无损)。

    对于子模块:

     git submodule foreach --recursive git checkout-symlinks
    
  4. 限制:

    • 路径中带有空格的目录/文件/符号链接应该可以工作。但是制表符还是换行符? YMMV…(我的意思是:不要那样做,因为它will not work.)

    • 如果您自己或其他人忘记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://git-scm.com/book/en/Git-Internals-Git-Objects

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

最后更新: 2019-03-13

  • POSIX 合规性(好吧,除了那些mklink当然是打电话)——没有了Bashisms https://wiki.ubuntu.com/DashAsBinSh#I_am_a_developer._How_can_I_avoid_this_problem_in_future.3F!
  • 支持其中包含空格的目录和文件。
  • 现在可以正确保留/返回零和非零退出状态代码(分别用于传达所请求命令的成功/失败)。
  • The add-symlink别名现在更像ln(1) http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ln.html#tag_20_67并且可以从存储库中的任何目录使用,而不仅仅是存储库的根目录。
  • The rm-symlink别名(单数)已被取代rm-symlinks别名(复数),现在接受多个参数(或根本不接受参数,它像以前一样找到整个存储库中的所有符号链接),用于有选择地将 git 符号链接转换为 NTFS 硬链接+连接。
  • The checkout-symlinks别名也已更新为接受多个参数(或根本没有,==一切),以选择性地反转上述转换。

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

. ./git-win-symlinks.sh
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Windows 中的 Git 符号链接 的相关文章

  • 使用 TFS REST API 获取 Git 提交的最新关联工作项

    我正在尝试获取关联的工作项使用 TFS REST API 进行 GIT 提交 https www visualstudio com en us docs integrate api git commits 我的请求 URL 如下所示 htt
  • http.h:6:23: 致命错误:curl/curl.h:没有该文件/目录

    我在 CentOS 7 中下载 git 包 wget https www kernel org pub software scm git git 2 0 1 tar gz tar xzf git 2 0 1 tar gz 当我编译git时
  • 如何让 git 显示作者日期指定日期范围内的提交?

    显然this https stackoverflow com a 11189286 281545 git log all after
  • 如何停用 Xcode git 功能? (删除 git 集成)

    我的 Xcode 项目位于 git 上 但我不喜欢 Xcode git 集成 有时 我有来自 Xcode 的错误 https stackoverflow com questions 7388560 error fatal not a git
  • Sencha Cmd 5 + Java 8 错误

    在我的 Windows 构建服务器上安装 Java 8 JDK 后 执行以下命令时遇到以下错误sencha命令 C gt sencha Error Registry key Software JavaSoft Java Runtime En
  • 如何在自托管 WCF 中获取多部分表单数据?

    我已经搜索了很长一段时间 但没有找到我要找的东西 我在 Windows 应用程序中自行托管了一个 http WCF 现在 在我的服务方法之一中 我需要接收一个文件和一些表单数据字段 在类似的问题中 情况要么发送一个文件 这是通过流数据然后转
  • 检查 Git 中是否需要 pull

    如何检查远程存储库是否已更改并且需要拉取 现在我使用这个简单的脚本 git pull dry run grep q v Already up to date changed 1 但它比较重 有没有更好的办法 理想的解决方案是检查所有远程分支
  • 如何迭代所有注册表项?

    我正在尝试迭代所有注册表项以查找 包含 并删除 jre1 5 0 14 值 有办法做到吗 下面的代码只是在特定键下找到jre1 5 0 14 我确实想迭代所有的键 顺便说一句 if 子句获取是否等于 jre1 5 0 14 但如果它包含 j
  • 生成尽可能最快的可执行文件

    我有一个非常大的程序 我一直在 Visual Studio 下编译 v6 然后迁移到 2008 我需要可执行文件尽可能快地运行 该程序大部分时间都花在处理各种大小的整数上 并且执行很少的 IO 显然 我会选择最大优化 但似乎可以做很多不属于
  • 使用管理员权限打开cmd(Windows 10)

    我有自己的 python 脚本来管理我的计算机上的 IP 地址 它主要在命令行 Windows 10 中执行netsh命令 您必须具有管理员权限 这是我自己的计算机 我是管理员 运行脚本时我已经使用管理员类型的用户 Adrian 登录 我无
  • Smartgit:自动插入提交消息

    有没有办法使用钩子脚本在 Smartgit 中自动插入提交消息 重击 如果用户提交了他的更改 我想预加载提交消息字段 我没有看到任何SmartGit配置 http www syntevo com smartgit documentation
  • Windows 上使用 g++ 的 Makefile,链接库

    我已经厌倦了 MSVC 6 以及每个人总是告诉我它是一个蹩脚的编译器等等 所以现在我决定尝试使用 vim 加 g 和 makefile 这是我的问题 我有以下 makefile This is supposed to be a commen
  • 创建多个 git 分支的联合分支

    我希望能够在现有分支之上分层其他分支 并独立修改这些分支 这很有用 例如 允许将各个子项目的二进制文件统一到同一个项目中bin目录 一般来说 给定的文件仅存在于一层中 理想情况下 我想我会使用 unionfs 来完成此任务 但它必须以某种方
  • 将 OpenBLAS 链接到 MinGW

    我正在尝试链接OpenBLAS https www openblas net 图书馆与明GW w64 https mingw w64 org Windows 上的编译器 这是我的代码 include
  • 结帐时出现 Git 错误:“致命:引用不是树”

    当我决定弄清楚为什么我正在从事的项目如此重要时 这一切就开始了 我运行了以下脚本 git rev list objects all git cat file batch check objecttype objectname objects
  • 在种子项目上构建时如何组织 git 存储库

    我正在基于从 github 克隆的种子项目 MEAN io 构建一个网站 如何将这些文件与我自己的文件分开 由于该种子提供了广泛的文件框架 因此我自己的文件分布在整个项目中 我希望能够从种子中提取更新 但不能将其与我添加的文件混合 我知道我
  • 在 python 中找不到 git 可执行文件

    我试图使用访问密钥克隆 git 存储库 但是当我尝试运行它时 它抛出一个异常 说找不到 git 可执行文件 但我已经安装了 git 并且 in it py 显示了正确的路径 C Program Files Git bin 我还安装了 git
  • 如何在现有裸存储库中创建引用日志信息

    您可能已经知道 默认情况下 git 不会为新的裸存储库启用引用日志更新 问题是 我有一个很长的历史存储库 但它是在我设置 logAllRefUpdates 标志之前创建的 现在我希望其他应用程序可以使用该信息 如何通过对现有存储库进行最少的
  • 在 Vista 上调用 RPC 时出现“不支持操作”

    我的应用程序使用 Microsoft RPC 进行进程间通信 当两个进程在同一台机器上运行并且一个进程尝试调用声明为 IDL 表示法 的方法时 error status t rpcMethod in pipe byte parameter
  • 为什么同一个curl命令在windows和linux下输出不同的东西?

    为什么同样的curl o file https www link com 命令输出不同的东西 例如 如果我运行命令curl o source txt https www youtube com playlist list PLIx6Fwnp

随机推荐

  • Angular 导出 Excel 客户端

    我正在使用 Angular v4 我想如何从组件中的对象开始构建 Excel 电子表格 我需要点击按钮下载 Excel 文件 并且我必须在客户端执行此操作 我有一个由数组组成的 json 文件 我需要将其传输到 Excel 文件上 可能可以
  • Strapi v4:填充时没有关系字段

    我正在尝试使用关系名称填充特定关系 categories 与 populate 参数结合使用 但它不会填充categories 当我查看架构时 我发现关系字段存在于属性对象中 但我的回复中仍然只得到非关系字段 我尝试了上面提到的所有组合St
  • 无需 Google 对话框的语音识别

    我将尝试使用带有 RecognitionListener 的语音识别 无需 Google 对话框 但不起作用 启动应用程序时只会发出蜂鸣声 我已将音频记录和互联网权限添加到清单文件中 我希望你告诉我并帮助我找到错误 我在 Log cat 上
  • 加水动画

    我正在尝试获取擦除动画以使圆圈看起来像它充满了水 我遇到了两个错误 甚至无法解决第三个错误 填错了方式 填充后重置为空 黑色 目前 我正在使用 img 标签 但我想将此效果移至body background image 并需要一些关于如何做
  • 这是使用 java 关键字“interface”的正确位置吗?

    我对 Java 相当陌生 在阅读了一些有关路径查找的信息后 我读到了有关使用空类作为 interface 对于未知的对象类型 我正在用 Java 开发一个基于医院主题的游戏 至此 用户可以搭建一个接待台和一个全科医生办公室 它们是两种不同类
  • java.lang.Thread 无法转换为 java.util.concurrent.ForkJoinWorkerThread

    我正在 Java SE 7 中使用 RecursiveTask 测试斐波那契示例http docs oracle com javase 7 docs api java util concurrent RecursiveTask html h
  • 是否可以在 CSS 中设置子字符串的样式?

    我想强调字符串的最后 3 个字符 例如 123456789 很容易将最后三个包裹起来 strong or span class 但我想知道是否可以仅使用 CSS 来完成 所以 html 会是这样的 span class mytext 123
  • 格式化 $SimpleXML->asXML() 的输出; [复制]

    这个问题在这里已经有答案了 标题基本概括了所有内容 如果我有类似的东西 来自 PHP 网站示例 xmlstr lt lt
  • 制作全幻灯片大小的 ggplot2 输出 {Xaringan}

    有没有办法让 ggplot 图表采用 xaringan 演示文稿的所有幻灯片 如果我将其导出为 png 并将其作为背景图片 我就可以做到这一点 但是直接从块输出呢 底部有一个最小的例子 解释 默认 CSS 样式为xaringan顶部和底部的
  • 在 Android 中创建自定义 ROM [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我是初级 Android 开发人员 我想通过编码而不是手动将库存 ROM 制作为定制 ROM 有没有可能做到这一点 有人请引导我走上正确的道路 我想通
  • 如何在其他模态弹出窗口上方显示模态弹出窗口

    我有一个模态弹出窗口 当我单击模态弹出窗口中的按钮时 会显示另一个模态弹出窗口 但是第二个模态弹出窗口的一些内容隐藏在第一个模态弹出窗口后面 如何使其显示在第一个模式弹出窗口上方 任何想法和建议表示赞赏 尝试设置z index您想要的弹出包
  • Javascript:如何停止点击事件排队?

    以下代码工作正常 唯一的问题是单击事件排队 例如setTimeout每次点击都会被调用 并且弹出窗口会出现多次 如何使弹出窗口仅在用户单击时出现 并确保每个弹出窗口之间的间隔为 4 秒 var showpopup 1 var check t
  • 如何将ajax添加到wordpress主题

    我有一个问题困扰了我好几天了 我正在尝试使用一个简单的 ajaxPOST 函数将数据发送到 MySQL 数据库 不是 WP 数据库 该代码位于主题中的 single post php 内 因为必须在每次发布之前检查它 ajax url li
  • 检索 pdf 时出现意外的服务器响应 (0)

    我们在使用 Amazon ec2 实例时特别遇到此错误 aws实例上的配置是Tomcat 7 Ubuntu 16 04 内存是8GB 当用户尝试查看 pdf 文件时会发生这种情况 在我们的应用程序中 我们有一项功能 用户只能在浏览器上查看
  • jQuery Mobile 输入和文本区域自定义样式

    我正在尝试设计我的 jQuery Mobile 表单输入和文本区域的样式 现在他们是这样定制的 http jquerymobile com demos 1 0b1 demos 1 0b1 docs forms forms text html
  • 在谷歌大查询中将表从一个数据集复制到另一个数据集

    我打算将一组表从一个数据集复制到同一项目中的另一个数据集 我在 Ipython 笔记本中执行代码 我使用以下代码获取要复制到变量 value 中的表名列表 list bq DataSet test TestDataset for x in
  • Android:id列表视图

    我在 xml 中声明了一个列表视图
  • 每次 ComboBox 更改(使用 SelectedIndexChanged)时,如果打开,则以其他形式显示新值的消息

    我想从 Form1 组合框中获取所选语言 并启用其他表单来查看该语言 该组合框大约有 20 种语言 所以是的 您可以说相当多的语言 我有一个名为 ComboBoxLang SelectedIndexChanged 的 方法 当组合框中的语言
  • 设置 codeDOM 文件的文件版本

    我正在寻找任何方法来设置使用 codeDOM 生成的 exe 文件的文件版本 我的总是显示为 0 0 0 0 以编程方式显然是首选 但在这一点上 任何事情都比没有好 已编译程序集的版本由 AssemblyFileVersion 属性控制 您
  • Windows 中的 Git 符号链接

    我们的开发人员混合使用基于 Windows 和 Unix 的操作系统 因此 在 Unix 机器上创建的符号链接成为 Windows 开发人员的一个问题 在 Windows 中 MSysGit https github com msysgit