Git和硬链接

130

考虑到Git无法识别指向仓库之外的符号链接,使用硬链接是否存在问题?

Git会破坏它们吗? 请指出详细信息。


2
你想要做什么以及为什么要这样做?硬链接与普通文件没有区别。如果你曾经从另一个仓库拉取新版本,它会覆盖你所拥有的-链接到存储库外部的内容有什么意义呢? - Carl Norum
3
Git能够识别符号链接,即使它们指向了仓库之外的路径。 - mipadi
不要使用 mipadi,唯一的方法是在存储库中挥动文件并在它们的“真实”位置中使用符号链接。 - Alfredo Palhares
针对一个使用案例,我有存储在Google Drive中可以在diagrams.net中打开的文件,这个文件夹已经与我的电脑同步。如果我还可以在我的git repo中拥有这些文件,并且检出或更改可以来回同步,但仍然可以通过Web应用程序从Drive打开它们,那将是很酷的事情。 - endolith
4个回答

113

'Tree'对象在Git中代表目录,存储文件名和(部分的)权限信息。它不存储inode号码(或其他类型的文件ID)。因此硬链接不能在Git中表示,至少没有第三方工具如metastoregit-cache-meta的帮助下是不可能的(即使使用了这些工具也不确定是否能实现)。

Git尽力避免修改不需要更新的文件,但是你需要注意的是Git不会保留硬链接,所以Git有可能会破坏它们。


关于指向代码库之外的符号链接:Git对它们没有问题,并且应该会保留符号链接的内容...但是这样的链接的实用性对我来说有些可疑,因为这些符号链接是否被破坏取决于git代码库之外的文件系统布局,而不受git控制。


5
符号链接到仓库外的路径可能很有用。我曾在 Web 应用中使用它们,将指向没有被仓库跟踪的数据库或媒体文件的路径。这样,Web 应用的配置文件可以指向一个静态路径,但该路径的实际位置可以在本地开发和服务器环境之间变化。 - mipadi
@mipadi:顺便说一下,现代的gitweb有一个特殊情况,可以显示指向仓库外规范化后的符号链接。 - Jakub Narębski
6
没问题,repo外的符号链接是可以的。我用它们来指向一个我不需要(也不想)把版本控制纳入其中的大量数据目录。通常我使用相对路径链接。因此在我的情况下,repo和数据目录必须坐落在某个父目录中。你可以通过符号链接到../foo达到惊人的效果。 - Adrian Ratnapala
很遗憾,metastore 的 Git 仓库 (git://git.hardeman.nu/metastore.git) 现在无法访问。 - Derek Mahar
1
https://github.com/danny0838/git-store-meta 是 git-cache-meta 的一种替代方案。 - Derek Mahar

26

我发现使用hooks可以捕获git pull事件(当有东西可拉取时...),将事件处理程序脚本写入.git/hooks/post-merge文件中。

首先,您需要对其进行chmod +x授权。

然后,在其中放置ln命令以在每次拉取时重新创建硬链接。很不错吧!

它可以工作,我只需要为我的项目做到这一点,ls -i显示在pull之后文件会自动链接。


.git/hooks/post-merge的示例:

#!/bin/sh
ln -f $GIT_DIR/../apresentacao/apresentacao.pdf $GIT_DIR/../capa/apresentacao.pdf
ln -f $GIT_DIR/../avaliacoesMono/avaliacao_monografias_2011_Nilo.pdf $GIT_DIR/../capa/avaliacoes.pdf
ln -f $GIT_DIR/../posters/poster_Nilo_sci.pdf $GIT_DIR/../capa/poster.pdf
ln -f $GIT_DIR/../monografia/monografia_Nilo.pdf $GIT_DIR/../capa/monografia_Nilo.pdf

重要提示:如您所见,您的存储库中任何文件的路径都应从$GIT_DIR开始,然后再添加文件的部分相对路径。

还要注意:-f是必需的,因为您正在重新创建目标文件。

编辑

现代Git客户端似乎天然支持在存储库内使用符号链接和硬链接,即使将其推送到远程位置并从中克隆。虽然我再也没有需要连接Git存储库之外的文件了...

$ mkdir tmp
$ cd tmp
$ git --version
git version 2.24.3 (Apple Git-128)
$ git init .
Initialized empty Git repository in /Users/teixeira/tmp/.git/
$ mkdir x
$ cd x
$ echo 123 > original
$ cat original
123
$ cd ..
$ ln -s x/original symlink
$ cat symlink
123
$ ln x/original hardlink
$ cat hardlink
123
$ git add .
$ git commit -m 'Symlink and hardlink commit'
[master (root-commit) 8df3134] Symlink and hardlink commit
 3 files changed, 3 insertions(+)
 create mode 100644 hardlink
 create mode 120000 symlink
 create mode 100644 x/original

从本地Git仓库克隆

$ cd
$ git clone tmp/ teste_tmp
Cloning into 'teste_tmp'...
done.
$ cd teste_tmp/
$ ls
hardlink  symlink  x
$ cat symlink
123
$ cat hardlink
123

从远程仓库克隆

$ cd ~/tmp
$ git remote add origin https://github.com/myUser/myRepo.git
$ git push origin master
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (5/5), 361 bytes | 361.00 KiB/s, done.
Total 5 (delta 0), reused 0 (delta 0)
To https://github.com/myUser/myRepo.git
 + 964dfce...8df3134 master -> master
$ cd ../
$ git clone https://github.com/myUser/myRepo.git
Cloning into 'myRepo'...
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 5 (delta 0), reused 5 (delta 0), pack-reused 0
Unpacking objects: 100% (5/5), done.
$ cd myRepo/
$ cat symlink
123
$ cat hardlink
123

https://github.com/mokacoding/symlinks 还指出了一个重要的事情:符号链接必须是相对定义的。


13
每次向您的仓库添加硬链接时,似乎您需要手动将一行添加到合并后钩子脚本中。如果您的预提交钩子可以自动完成这个过程就好了——检测提交中的硬链接和符号链接,并将适当的行写入您的合并后文件。Git不必在仓库中存储 inode 信息,而是将其存储在钩子中的位!但如果链接文件在另一个 Git 仓库中被跟踪会出现问题...在一个地方对文件进行编辑是否会平滑地传播到其他仓库?永远的合并与循环推/拉? - hobs
巧妙的方法,但很不幸,Git无法跟踪硬链接,即使在那些不支持硬链接的文件系统上,它们也可能被表示为副本。 - Derek Mahar
1
@hobs 抱歉回复晚了7年 :) 你说得对。如果一个代码库以外的文件被2个不同的代码库链接,我认为在一个代码库中更改链接就不会在另一个代码库中显示。 - Niloct
1
我正在寻找“post-checkout”钩子,但除此之外,这是一个非常好的灵感。谢谢! - undefined

8

从这个msysgit问题中可以看出:

联接点不是符号链接,因此在 msysGit 中不支持符号链接。

此外,硬链接从未被 Git 跟踪

关于硬链接的评论涉及到 Git 的一般情况。具体问题是与 Microsoft Windows 相关的(因为它涉及到 msysgit),讨论了对符号链接的潜在支持。


3

搜索“git保留硬链接”并显示git不知道如何保留硬链接结构,据我所知,可能是出于设计考虑。

我的网络项目使用硬链接如下:

www/products/index.php
www/products/dell_latitude_id577/index.php #(hard linked to above)
www/products/dell_inspiron_id323/index.php #(hard linked again to above)

me@server:www/products$ ls -l index.php
-rwxr-xr-x 3 me me 1958 Aug 22 22:10 index.php*

如果我想更改index.php,我只需要在一个地方进行更改,硬链接(产品详情页)也指向更改后的文件。但是,在其他计算机上克隆和拉取项目时,git不会保留这种关系。
me@server:www$ git pull

在另一台机器上创建硬链接将为每个硬链接创建一个新的index.php。

8
你应该在你的Web应用程序中实现某种路由功能。硬链接是奇怪的。 - Nowaker
5
好的,至少使用符号链接。 :) - Andres Riofrio
3
这实际上是我想要的,我不希望git保留硬链接。我有一个Jenkins目录,里面有大量的工作区目录,由于多分支管道,存在很多重复。因此,我每晚都会在“/var/lib/jenkins”上运行“hardlink --ignore-time”,以回收一些磁盘空间。白天有些文件在运行“git pull”或“mvn compile”后会取消硬链接,但这没关系,我希望会发生这种情况。如果git保留硬链接,那么我的磁盘空间回收策略就不起作用了。 - Amedee Van Gasse

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