如何让Git跟随符号链接?

271

我的最佳选择是用一个Shell脚本替换符号链接为拷贝,还是有其他的方法告诉Git跟随符号链接?

PS:我知道这样做不太安全,但我只想在一些特定情况下这么做。


7
像这样使用硬链接有什么不利之处吗? - Ehtesh Choudhury
14
在Windows 7中,使用命令"mklink /d"(目录符号链接)无法在git中正常工作,但是"mklink /j"(联接)可以顺利运行。 - yoyo
3
如果文件是由一个应用程序自动生成的,并以这样一种方式重新生成,即删除文件并创建一个新文件,则是的,这是一个硬链接无法解决的问题。 - Martin Pecka
7
@EhteshChoudhury,你不能为目录创建硬链接。 - Gaurav Kansal
2
@EhteshChoudhury 硬链接要求链接和目标在同一磁盘上。 - Duncan MacIntyre
显示剩余2条评论
14个回答

180

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

sudo mount --bind SOURCEDIRECTORY TARGETDIRECTORY

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

它在Linux上运行良好,但在OS X上不行!这个诀窍也帮助我解决了Subversion的问题。我用它来包含从Dropbox帐户中的Web设计师那里获取的文件。

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

/sourcedir /targetdir none bind

14
如果不需要sudo权限,那将是一种非常好的方法。 - MestreLion
16
要解除这个绑定,请使用 umount [mydir] 命令。(感谢 @user252400 的好建议,加1赞!) - JellicleCat
11
仅限于当前会话有效,如何使其“永久”? - Adobe
19
@Adobe: 将它放入 /etc/fstab,像这样: /sourcedir /targetdir none bind - Alexander Garden
10
sshfs可以实现这种操作,而无需使用sudo。 - PypeBros
显示剩余9条评论

89
为什么不反过来创建符号链接呢?也就是说,不是从Git仓库链接到应用程序目录,而是从应用程序目录反向链接。
例如,假设我正在设置一个安装在~/application中并需要配置文件config.conf的应用程序:
  • 我将config.conf添加到我的Git仓库中,例如在~/repos/application/config.conf处。
  • 然后我通过运行ln -s ~/repos/application/config.conf~/application中创建一个符号链接。
  • 这种方法可能并不总是适用,但它迄今为止对我来说效果很好。

    5
    看起来这似乎是唯一的方式,而且并不糟糕……我认为你的方法相当优雅。Git跟踪的是内容而不是文件。因此将所有内容保持在一起,并从那里创建符号链接到其他地方是有意义的。 - MestreLion
    17
    在我的情况下,我想要从一个Git仓库链接到另一个,这样我就可以在任意一个位置编辑文件并提交回它们各自的远程仓库。在Windows 7上,使用“mklink /j”命令创建联接符即可解决问题。 - yoyo
    3
    当然,有时答案就是如此简单。 - BBW Before Windows
    1
    另一方面,有一个 Nginx 拒绝从符号链接中提供文件的问题。 - andrej
    2
    不回答问题 :( 我想让我的存储库的一部分同步到iCloud中。 不幸的是,iCloud不遵循符号链接,所以我想我可以让git遵循符号链接,并将原始文件存储在iCloud中。 结果发现没有人遵循符号链接 :\ - Jerry Green
    显示剩余3条评论

    64

    使用硬链接代替软链接(symbolic link),所有程序,包括 git ,都将把该文件视为常规文件。请注意,可以通过更改源或目标来修改其内容。

    在 macOS 中(10.13 High Sierra 之前)

    如果已经安装了 git 和 Xcode,请安装hardlink。它是一个微小的创建硬链接的工具

    要创建硬链接,只需执行以下操作:

    hln source destination
    

    macOS High Sierra 更新

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

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

    来自 开发人员文档中的APFS常见问题解答

    请关注https://github.com/selkhateeb/hardlink/issues/31以获取未来的替代方案。

    在 Linux 和其他 Unix 系统上

    ln 命令可以创建硬链接:

    ln source destination
    

    如何在 Windows (Vista, 7, 8, ...) 上创建链接

    使用mklink命令来创建一个链接:

    mklink /j "source" "destination"
    

    8
    请注意:这基本上就是我需要的内容,但后来我了解到在Linux上,硬链接不幸不能跨越文件系统边界(而这正是我的用例)。 - sdaau
    45
    你不能为目录创建硬链接,对吗? - Nanne
    1
    ln source destination 在 OS X 上也可以使用。已在 El Capitan 上进行了测试。 - Mahdi Dibaiee
    9
    不,但你可以使用命令 cp -al 源路径 目标路径。其中 -l 表示创建硬链接而不是复制文件。 - Paolo
    11
    很遗憾,您无法在目录之间建立硬链接,也无法跨文件系统边界进行链接。这使得这种解决方案对我来说是行不通的。 - Konrad Rudolph
    显示剩余9条评论

    46

    注意:此建议已过时,根据Git 1.6.1的评论。 Git曾经以此方式运行,现在不再。


    默认情况下,Git尝试存储符号链接而不是遵循它们(出于紧凑性,并且这通常是人们想要的)。

    但是,当符号链接是目录时,我意外地设法使它添加超出符号链接的文件。

    即:

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

    通过执行

     git add /bar/foo/baz
    

    我尝试过,似乎有效。但当时我不需要那种行为,所以除此之外我无法提供更多信息。


    77
    提交记录725b06050a083474e240a2436121e0a80bb9f175和806d13b1ccdbdde4bbdfb96902791c4b7ed125f6引入了更改,使您无法向符号链接目录之外添加文件,因此这在Git版本1.6.1之后的版本中不起作用。 - Mark Longair
    1
    $ git add src/main/path/ConvertSymlinkToDir 致命错误: 'src/main/path/ConvertSymlinkToDir' 超出了符号链接。 - user1767316
    2
    @user1767316 请仔细阅读整个内容和评论。它曾经有效,但现在不再有效了。软件会发生变化,但是 Stack Overflow 接受的答案不会随之改变。我已经澄清了这个问题无法解决。请查看其他答案。 - Kent Fredric
    是的 @KentFrederic,但编写返回的确切错误消息可以帮助堆栈用户搜索解决问题的解决方案。尝试取消踩但被锁定了,很抱歉。一方面,您的答案是正确的,考虑到警告,另一方面,应该优先考虑现在有效的答案,而不是过去的答案。 - user1767316
    1
    提交e0d201b让Git维护者似乎真的不想解析符号链接。我不明白为什么,但我想我会使用绑定挂载来解决问题。 - Paul

    30
    这是一个可用于替换索引中符号链接块内容的预提交挂钩,您可以在 .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标准,还有其他一些可能。

    尽管代码经过了一些测试,但仍可能存在一些错误/问题。


    4
    看到有人试图回答关于文件而不是目录的问题,这真是太好了。但请注意,对于实际上是符号链接但现在被 Git 视为不是的文件,在上述情况下仍将在“git status”中显示“typechange”。 - David Fraser
    1
    谢谢您的回复!只是想知道,“process_links_to_nondir”是什么意思? - sdaau
    1
    @sdaau 是用作sh进程命令名的名称/argv[0]。(花了我一点时间才明白,因为我也记不得它是什么了☺) - Abbafei
    3
    @Abbafei,你能修改脚本让它在Ubuntu(14.04)上运行吗?它显示“find:missing argument to -exec”。也许需要逐步执行命令,而不是将所有内容都组合到单行中。 - Khurshid Alam
    1
    @KhurshidAlam 对于我来说,只需删除命令行之间的注释行即可使其正常工作。然而,钩子并没有按预期工作(我像 @DavidFraser 一样收到了 typechange,但是似乎链接的文件不再被暂存了)。 - Scz

    21

    在 macOS(我的版本是 Mojave/10.14,git 版本为 2.7.1)上,可以使用 bindfs

    brew install bindfs

    cd /path/to/git_controlled_dir

    mkdir local_copy_dir

    bindfs </full/path/to/source_dir> </full/path/to/local_copy_dir>

    其他评论中已经提到过,但在其他答案中没有明确提供。希望这能为某些人节省一些时间。


    1
    这很有帮助,我认为这是MacOS中一个有用但缺失功能的最佳解决方案。但请注意,除非我在bindfs命令中使用完整路径名,否则我会遇到Failed to resolve...No such file or directory错误。 - electromaggot
    1
    在macOS Catalina上运行得非常好! - Jerry Green
    1
    @PrashantShubham,https://github.com/telepresenceio/telepresence/issues/1654#issuecomment-873538291可能会有所帮助(明确`brew install --cask macfuse`)。 - ijoseph
    1
    @PrashantShubham @ijoseph,安装macfuse后,您必须下载最新版本的bindfs并构建它。您必须使用gmake(与brew一起安装的版本),而不是Mac默认的make才能使其正常工作。按照README中的步骤进行制作和安装。安装完成后,请确保首先创建空的子目录,因为这本质上是一个挂载操作。如注所述,bindfs在两个参数上都需要完整路径(并且在目标中,请确保将您创建的目录作为挂载点的最后一个元素添加)。 - Aron T
    1
    @ijoseph,确实很奇怪。也许还有其他问题,gmake绕过了这些问题,而较新版本的Mac OS X已经解决了。更奇怪的是,苹果公司在近两年内都没有升级其make版本! - Aron T
    显示剩余7条评论

    20

    我厌倦了这里的每一个解决方案都过时或需要root权限,所以我制作了一种基于LD_PRELOAD的解决方案(仅适用于Linux)。

    它钩入Git的内部,覆盖“这是符号链接吗?”函数,允许将符号链接视为它们的内容。默认情况下,所有指向仓库外部的链接都会被内联;有关详细信息,请参见链接。


    使用LD_PRELOAD覆盖库函数的非常有创意的解决方案! - iBug
    是的,但这里应该详细说明一下。你能做到吗? - Peter Mortensen
    不确定详细说明会有多大帮助;除非我复制整个源代码,否则这个答案将始终依赖于那个链接,那里也可以找到自述文件。但是,是的,我想我可以重现自述文件的重要部分。 - Alcaro
    这段代码在OS X(Mojave 10.14.2)上无法编译。出现了四个关于'strchrnul'的错误(你是不是想说'strchr'?),以及一个关于'__xstat64'的错误(你是不是想说'__lxstat64'?)。最后,我得到了一个“member access into incomplete type 'dirent64'”错误。无论我使用“make”,“make OPT=1”还是“sh install.sh”,都会发生这种情况。 - Erik Veland
    @ErikVeland 我尝试了一下,但似乎OSX不支持LD_PRELOAD或任何类似的东西。各种文档提供了各种建议,但它们都是几年前的,而且苹果喜欢废弃旧技术;我无法让它们中的任何一个起作用。抱歉。 - Alcaro
    这真是一个聪明而牛逼的解决方案。我喜欢它,而且它很有效。 - fishshrimp鱼虾爆栈

    15

    我过去一直用符号链接添加文件,而且一直都没有问题,不需要做任何特殊的安排。但自从我升级到 Git 1.6.1 后,这种方法就无法使用了。

    你可以尝试切换回 Git 1.6.0 来使其正常工作。我希望未来版本的 Git 会有一个标志,允许 git-add 命令再次跟随符号链接。


    8

    从Git 2.3.2+(2015年第一季度)开始,有另外一种情况下Git将不再跟随符号链接:请参见提交e0d201bJunio 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
    

    4

    嗯,mount --bind 在 Darwin 上似乎不起作用。

    有没有什么技巧可以实现呢?

    [编辑]

    好的,我在 Mac OS X 上找到了答案,就是使用硬链接。除了这个 API 不会通过 ln 公开,所以你需要使用自己的小程序来完成。下面是一个链接到该程序的链接:

    在 Mac OS X 中创建目录硬链接

    享受吧!


    1
    如果这个硬链接的目标目录是另一个git仓库的子目录,那将会造成混乱。在硬链接中进行git操作将会影响到这个其他的git仓库。请务必仔细确认你所做的操作。 - yegle
    4
    可以通过https://code.google.com/p/bindfs/来实现这一点,可以使用端口进行安装。 - Kit Sunde

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接