如何让 Git 遵循符号链接?

Posted

技术标签:

【中文标题】如何让 Git 遵循符号链接?【英文标题】:How can I get Git to follow symlinks? 【发布时间】:2010-09-10 07:50:58 【问题描述】:

我最好是一个用副本替换符号链接的 shell 脚本,还是有另一种方法告诉 Git 遵循符号链接?

PS:我知道这不是很安全,但我只想在少数特定情况下这样做。

【问题讨论】:

对这样的事情使用硬链接有什么缺点吗? 在 Windows 7 中,“mklink /d”(目录符号链接)不适用于 git,但“mklink /j”(连接)可以正常工作。 如果文件是由应用程序自动生成的,该应用程序以删除文件并创建新文件的方式重新生成文件,那么是的,这是文件硬链接无法解决的问题。 @EhteshChoudhury 你不能为目录建立硬链接 @EhteshChoudhury 硬链接要求链接和目标在同一个磁盘上 【参考方案1】:

为了将符号链接中的文件添加到 Git 中所做的添加(我没有使用符号链接,但是):

sudo mount --bind SOURCEDIRECTORY TARGETDIRECTORY

在 Git 管理的目录中执行此命令。 TARGETDIRECTORY 必须在 SOURCEDIRECTORY 挂载到其中之前创建。

它在 Linux 上运行良好,但在 OS X 上却不行!这个技巧也帮助了我使用 Subversion。我用它来包含来自 Dropbox 帐户的文件,网页设计师在其中做他/她的工作。

如果您想永久绑定此绑定,请将以下行添加到/etc/fstab

/sourcedir /targetdir none bind

【讨论】:

如果不需要 sudo 将是一个非常好的方法。 要撤消此绑定,请使用umount [mydir]。 (+1 给你的小费,@user252400) 这仅在会话期间有效。让它“永恒”的最好方法是什么? @Adobe:把它放在 /etc/fstab 中,像这样:/sourcedir /targetdir none bind sshfs 可以在不需要 sudo 的情况下实现这种技巧,在这里。【参考方案2】:

为什么不反过来创建符号链接呢?意思不是从 Git 存储库链接到应用程序目录,而是以相反的方式链接。

例如,假设我正在设置一个安装在~/application 中的应用程序,它需要一个配置文件config.conf

我将config.conf 添加到我的Git 存储库中,例如~/repos/application/config.conf。 然后我通过运行ln -s ~/repos/application/config.conf~/application 创建一个符号链接。

这种方法可能并不总是有效,但到目前为止对我来说效果很好。

【讨论】:

似乎是唯一的方法,而且还不错……我认为您的方法相当优雅。 git 跟踪内容,而不是文件。因此,将所有内容放在一起并从那里符号链接到其他地方是有意义的 在我的例子中,我想要一个从一个 git repo 到另一个的链接,所以我可以在任一位置编辑文件并提交回它们各自的远程。在 Windows 7 上,结点(“mklink /j”)起到了作用。 当然。有时答案就是这么简单。 如果你想同时 git 源和目标怎么办? (因为两者都属于您希望在不同存储库中拥有的不同代码) 不回答问题 :( 我希望我的存储库的一部分被同步到我的 iCloud。不幸的是,iCloud 不遵循符号链接,所以我想我可以让 git 遵循符号链接,并存储原始iCloud中的文件。结果没有人遵循符号链接:\【参考方案3】:

改用硬链接。这与软(符号)链接不同。所有程序,包括git 都会将该文件视为普通文件。请注意,可以通过更改源或目标来修改内容。

在 macOS 上(10.13 High Sierra 之前)

如果你已经安装了 git 和 Xcode,install hardlink。这是一个微观的tool to create hard links。

要创建硬链接,只需:

hln source destination

macOS High Sierra 更新

Apple 文件系统是否支持目录硬链接?

Apple 文件系统不支持目录硬链接。当您在 macOS 上从 HFS+ 转换为 APFS 卷格式时,所有目录硬链接都会转换为符号链接或别名。

来自APFS FAQ on developer.apple.com

关注https://github.com/selkhateeb/hardlink/issues/31 了解未来的替代方案。

在 Linux 和其他 Unix 风格上

ln 命令可以建立硬链接:

ln source destination

在 Windows 上(Vista、7、8……)

使用mklink 在 Windows 上创建一个联结:

mklink /j "source" "destination"

【讨论】:

请注意:这基本上是我一直在寻找的,但后来我了解到,在 Linux 上,不幸的是,硬链接不能跨越文件系统边界(这是我的用例)。 你不能硬链接到目录,对吗? ln source destination 也适用于 OS X。在 El Capitan 上测试。 @Nanne 不,但你可以这样做:cp -al source destination。 `-l' 表示硬链接文件而不是复制。 不幸的是,您不能硬链接目录,也不能跨越文件系统边界。这使得这个解决方案对我来说是双重不可行的。【参考方案4】:

注意: 根据 Git 1.6.1 以来的评论,此建议现已过时。 Git 曾经这样做过,现在不再这样做了。


Git 默认尝试存储符号链接而不是跟随它们(为了紧凑,这通常是人们想要的)。

但是,当符号链接是目录时,我不小心设法让它在符号链接之外添加文件。

即:

  /foo/
  /foo/baz
  /bar/foo --> /foo
  /bar/foo/baz

通过做

 git add /bar/foo/baz

当我尝试它时它似乎工作。然而,当时我并不希望这种行为,因此我无法向您提供除此之外的信息。

【讨论】:

提交 725b06050a083474e240a2436121e0a80bb9f175 和 806d13b1ccdbdde4bbdfb96902791c4b7ed125f6 引入了阻止您在符号链接目录之外添加文件的更改,因此这在 1.6.1 之后的 git 版本中不起作用> $ git add src/main/path/ConvertSymlinkToDir fatal: 'src/main/path/ConvertSymlinkToDir' 超出符号链接 @user1767316 阅读全文,以及 cmets。它曾经有效,现在不再有效。软件更改,但堆栈溢出接受的答案没有。我已经澄清这已经不起作用了。看看另一个答案。 是的@KentFrederic,但写入确切的错误消息返回帮助堆栈用户搜索其问题的解决方案。尝试取消投票但锁定抱歉。一方面,鉴于警告,您的答案是正确的,另一方面,应优先考虑现在而不是过去的工作 Commit e0d201b 看起来 git 维护者真的不希望解析符号链接。我不明白为什么,但我想我会求助于使用绑定安装。【参考方案5】:

这是一个 pre-commit hook,它将索引中的符号链接 blob 替换为这些符号链接的内容。

将其放入.git/hooks/pre-commit,并使其可执行:

#!/bin/sh
# (replace "find ." with "find ./<path>" below, to work with only specific paths)

# (these lines are really all one line, on multiple lines for clarity)
# ...find symlinks which do not dereference to directories...
find . -type l -exec test '!' -d  ';' -print -exec sh -c \
# ...remove the symlink blob, and add the content diff, to the index/cache
    'git rm --cached "$1"; diff -au /dev/null "$1" | git apply --cached -p1 -' \
# ...and call out to "sh".
    "process_links_to_nondir"  ';'

# the end

注意事项

我们尽可能使用符合 POSIX 标准的功能;但是,diff -a 不符合 POSIX,可能还有其他原因。

此代码中可能存在一些错误/错误,即使它经过了一些测试。

【讨论】:

很高兴看到有人尝试实际回答文件而不是目录的问题。但是,请注意,对于实际上是符号链接的文件,上面仍然会在 git status 中显示 typechange,尽管 git 现在它们不是。 谢谢你;只是想知道,process_links_to_nondir 是什么? @sdaau 它是名称/argv[0],用作sh 进程的命令名称。 (花了我一点时间才弄清楚,因为我也不记得那是什么☺?) @Abbafei 你能修改脚本使其在 Ubuntu (14.04) 上运行吗?它显示find: missing argument to -exec'。可能需要逐步执行命令,而不是通过管道将所有内容组合成一行。 @KhurshidAlam 对我来说,它可以删除命令行之间的注释行。但是,钩子没有按预期工作(我得到了@DavidFraser 一样的typechange,但链接的文件似乎不再上演了)【参考方案6】:

在 macOS 上(我有 Mojave/10.14,git 版本 2.7.1),使用 bindfs

brew install bindfs

cd /path/to/git_controlled_dir

mkdir local_copy_dir

bindfs &lt;/full/path/to/source_dir&gt; &lt;/full/path/to/local_copy_dir&gt;

其他 cmets 已暗示,但在其他答案中未明确提供。希望这可以节省一些时间。

【讨论】:

对于在 mac os 上运行的团队来说似乎很棒,我认为下面概述的硬链接应该适用于 windows 和 linux。 这很有帮助,我认为这是 MacOS 中有用但缺失的功能的最佳解决方案。但是请注意,我收到了Failed to resolve...No such file or directory 错误除非我在bindfs 命令中使用了完整路径名 在 macos Catalina 上效果很好! @PrashantShubham ,github.com/telepresenceio/telepresence/issues/… 可能会有所帮助(明确为brew install --cask macfuse @PrashantShubham @ijoseph 一旦你安装了macfuse,你必须下载最新版本的bindfs 并构建它。您必须使用gmake(与brew 一起安装的版本)而不是Mac 默认的make 才能使其工作。按照README 中的步骤进行制作和安装。安装后,请务必先创建空子目录,因为这本质上是一个挂载操作。如前所述,bindfs 需要两个参数的完整路径(并且在目标中确保将您创建的目录添加为挂载点作为路径中的最后一个元素)【参考方案7】:

我厌倦了这里的每个解决方案都过时或需要 root,所以I made an LD_PRELOAD-based solution(仅限 Linux)。

它连接到 Git 的内部,覆盖“这是一个符号链接吗?”函数,允许将符号链接视为其内容。默认情况下,所有指向 repo 外部的链接都是内联的;详情见链接。

【讨论】:

非常有创意的解决方案,使用LD_PRELOAD 覆盖库函数! 是的,但应该在这里详细说明。你能做到吗? 不确定详细说明会有所帮助;除非我复制粘贴整个源代码,否则此答案将始终依赖于该链接,其中还可以找到自述文件。但是,是的,我想我可以复制自述文件的重要部分。 这不能在 OS X (Mojave 10.14.2) 上编译。得到四个抱怨“strchrnul”的错误(你的意思是“strchr”吗?)和一个关于“__xstat64”的错误(你的意思是“__lxstat64”吗?)。最后我得到一个“成员访问不完整类型'dirent64'”错误。无论我使用“make”、“make OPT=1”还是“sh install.sh”都会发生。 @ErikVeland 我试了一下,但似乎 OSX 不支持 LD_PRELOAD,也不支持任何类似的东西。各种文档提出了各种各样的建议,但它们都有几年的历史了,Apple 喜欢弃用的东西;我无法让他们中的任何一个工作。对不起。【参考方案8】:

我过去很长一段时间都在添加符号链接之外的文件。这过去工作得很好,没有做任何特殊安排。由于我更新到 Git 1.6.1,这不再起作用了。

您也许可以切换到 Git 1.6.0 来完成这项工作。我希望未来版本的 Git 会有一个指向 git-add 的标志,允许它再次遵循符号链接。

【讨论】:

【参考方案9】:

对于 Git 2.3.2+(2015 年第一季度),还有另一种情况是 Git 不再遵循符号链接:请参阅 commit e0d201b by Junio C Hamano (gitster)(主要 Git 维护者)

apply: 请勿触摸符号链接以外的文件

因为 Git 将符号链接作为符号链接进行跟踪,因此路径在其前导部分具有符号链接(例如 path/to/dir/file,其中 path/to/dir 是指向其他地方的符号链接,无论是在工作树的内部还是外部)永远不能出现在有效应用的补丁中,除非同一个补丁首先删除符号链接以允许在那里创建目录。

检测并拒绝这样的补丁。

同样,当输入创建符号链接path/to/dir,然后创建文件path/to/dir/file,我们需要将其标记为错误,而无需在文件系统中实际创建path/to/dir 符号链接。

相反,对于输入中任何在结果中留下路径(即非删除)的补丁,我们通过检查输入中的所有补丁然后检查补丁将创建的结果树来检查所有前导路径补丁应用程序的目标(索引或工作树)。

这样,我们:

抓到恶作剧或错误同时添加符号链接path/to/dir和文件path/to/dir/file, 同时允许有效补丁删除符号link path/to/dir,然后添加文件path/to/dir/file

这意味着,在这种情况下,错误消息不会像"%s: patch does not apply" 这样的通用消息,而是更具体的消息:

affected file '%s' is beyond a symbolic link

【讨论】:

【参考方案10】:

嗯,mount --bind 似乎不适用于达尔文。

有没有人有这样的技巧?

[编辑]

好的,我发现在 Mac OS X 上的答案是建立硬链接。除了该 API 没有通过 ln 公开,因此您必须使用自己的小程序来执行此操作。这是该程序的链接:

Creating directory hard links in Mac OS X

享受吧!

【讨论】:

如果这个硬链接的目标目录是另一个 git repo 的子目录,这将是一个混乱。在硬链接中执行 git 操作将适用于其他 git 存储库。仔细检查你在做什么。 可以通过code.google.com/p/bindfs 实现,可以使用端口安装。【参考方案11】:

@user252400 建议的另一种实现是使用bwrap,这是一个小型 setuid 沙箱,可以在所有主要发行版中找到 - 通常默认安装。 bwrap 允许你绑定挂载目录没有 sudo 并在 git 或你的 shell 退出时自动解除绑定。

假设你的开发过程不是太疯狂(见下文),在私有命名空间中启动 bash 并将外部目录绑定到 git 目录下:

bwrap --ro-bind / / \
      --bind EXTERNAL-DIR MOUNTPOINT-IN-GIT-DIR \
      --dev /dev \
      bash

然后做你通常会做的所有事情,比如git addgit commit,等等。完成后,只需退出 bash。干净简单。

警告:为防止沙盒逃逸,bwrap 不允许执行其他 setuid 二进制文件。详情请见man bwrap

【讨论】:

【参考方案12】:

1.您应该使用硬链接,因为在硬链接中所做的更改是 由 git 上演。

2.创建硬链接的语法是ln file1 file2

3.这里file1是硬链接文件的位置 file2 是硬链接的位置。

4.希望对您有所帮助。

【讨论】:

【参考方案13】:

我正在使用 Git 1.5.4.3,如果它有一个尾部斜杠,它会跟随传递的符号链接。例如

# Adds the symlink itself
$ git add symlink

# Follows symlink and adds the denoted directory's contents
$ git add symlink/

【讨论】:

至少在 OSX 上这会导致 fatal: 'src/' is beyond a symbolic link 正如@Mark Longair 所解释的,这只在 git 1.6.1 之前有效 那是我的问题,谢谢!【参考方案14】:

从符号链接转换可能很有用。链接到 Git 文件夹而不是符号链接 by a script。

【讨论】:

想为您的答案添加更多详细信息?

以上是关于如何让 Git 遵循符号链接?的主要内容,如果未能解决你的问题,请参考以下文章

nginx 不遵循 www-data 用户的符号链接

为啥 PHP 在启用 SELinux 的情况下不遵循符号链接?

如何让 Apache 读取到不同分区的符号链接?

编译器不遵循 Visual Studio C++ 中的符号链接

使用 python zipfile 归档符号链接

是否可以让 SCP 在复制过程中忽略符号链接?