Windows中的Git符号链接
我们的开发人员使用基于Windows和Unix的操作系统。 因此,在Unix机器上创建的符号链接成为Windows开发人员的一个问题。 在Windows(msysgit)中,符号链接被转换为文本文件,并带有它指向的文件的路径。 相反,我想将符号链接转换为实际的Windows符号链接。
我需要的( 更新的 )解决方案是:
我没有实现这一点,但我相信这是解决这个问题的可靠方法。
问题:
您可以通过查找具有120000
模式的文件来找到符号链接,可能使用以下命令:
git ls-files -s | awk '/120000/{print $4}'
一旦你更换了链接,我建议用git update-index --assume-unchanged
将它们标记为git update-index --assume-unchanged
,而不是将它们列在.git/info/exclude
。
我一直在问这个完全相同的问题(不是在这里,一般情况下),并最终提出了一个与OP的主张非常类似的解决方案。 首先我会直接回答问题1 2和3,然后我会发布我最终使用的解决方案。
git checkout
步骤,但下面的解决方案已经很好地满足了我的需求,以至于不需要字面的结账后脚本。 解决方案:
我们的开发人员与OP的情况大致相同:Windows和基于Unix的主机,存储库和子模块混合使用许多git符号链接,并且在MsysGit发行版本中没有本地支持(用于智能地处理Windows主机上的这些符号链接) 。
感谢Josh Lee指出git使用特殊文件模式120000
提交符号链接的事实。 有了这些信息,可以添加一些git别名,以便在Windows主机上创建和操作git符号链接。
在Windows上创建git符号链接
2014-11-12更新 (见下文)
git config --global alias.add-symlink '!__git_add_symlink(){
argv=($@)
argc=${#argv[@]}
# Look for options
options=(" -h")
o_help="false"
case "${argv[@]}" in *" -h"*) o_help="true" ;; esac
if [ "$o_help" == "true" -o "$argc" -lt "2" ]; then
echo "
Usage: git add-symlink <target> <link>
* <target> is a RELATIVE PATH, respective to <link>.
* <link> is a RELATIVE PATH, respective to the repository'''s root dir.
* Command must be run from the repository'''s root dir."
return 0
fi
target_arg=${argv[0]}
link_arg=${argv[1]}
if [ ! -e "$target_arg" ]; then
echo "ERROR: Target $target_arg does not exist; not creating invalid symlink."
return 1
fi
hash=$(echo -n "$target_arg" | git hash-object -w --stdin)
git update-index --add --cacheinfo 120000 "$hash" "$link_arg"
git checkout -- "$link_arg"
}; __git_add_symlink "$@"'
用法: git add-symlink <src> <dst>
,其中<src>
是要链接到的文件或目录的当前位置的相对引用 (相对于<dst>
), <dst>
是相对引用 (相对于存储库的根)到链接所需的目的地。
例如,存储库树:
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"
用NTFS硬链接+连接替换git符号链接
git config --global alias.rm-symlink '!__git_rm_symlink(){
git checkout -- "$1"
link=$(echo "$1")
POS=$'''/'''
DOS=$'''\'''
doslink=${link//$POS/$DOS}
dest=$(dirname "$link")/$(cat "$link")
dosdest=${dest//$POS/$DOS}
if [ -f "$dest" ]; then
rm -f "$link"
cmd //C mklink //H "$doslink" "$dosdest"
elif [ -d "$dest" ]; then
rm -f "$link"
cmd //C mklink //J "$doslink" "$dosdest"
else
echo "ERROR: Something went wrong when processing $1 . . ."
echo " $dest may not actually exist as a valid target."
fi
}; __git_rm_symlink "$1"'
git config --global alias.rm-symlinks '!__git_rm_symlinks(){
for symlink in $(git ls-files -s | egrep "^120000" | cut -f2); do
git rm-symlink "$symlink"
git update-index --assume-unchanged "$symlink"
done
}; __git_rm_symlinks'
用法:
git rm-symlink dir/foo/bar/lnk_file
git rm-symlink lnk_bar
git update-index --assume-unchanged dir/foo/bar/lnk_file
git update-index --assume-unchanged lnk_bar
这将逐个删除git符号链接,将其替换为NTFS硬链接(在文件的情况下)或NTFS连接点(在目录的情况下)。 使用硬连接+连接而不是“真正的”NTFS符号连接的好处是不需要提升UAC权限以便创建它们。 最后,在您自己的闲暇时间,您可以选择使用git update-index
取消修改(或不修改)符号链接。
为了方便起见,你也可以运行:
git rm-symlinks
这将删除当前存储库中的所有git符号链接,并根据需要将其替换为硬链接+连接,并自动标记git status
将忽略的更改。
要从子模块中删除符号链接,只需使用git的内置支持来遍历它们即可:
git submodule foreach --recursive git rm-symlinks
但是,对于这样的每一个激烈的行动,逆转是很好的... ...
在Windows上恢复git符号链接
git config --global alias.checkout-symlinks '!__git_checkout_symlinks(){
POS=$'''/'''
DOS=$'''\'''
for symlink in $(git ls-files -s | egrep "^120000" | cut -f2); do
git update-index --no-assume-unchanged "$symlink"
dossymlink=${symlink//$POS/$DOS}
cmd //C rmdir //Q "$dossymlink" 2>/dev/null
git checkout -- "$symlink"
echo "Restored git symlink $symlink <<===>> $(cat $symlink)"
done
}; __git_checkout_symlinks'
用法: git checkout-symlinks
,它撤销git rm-symlinks
,有效地将存储库恢复到它的自然状态(除了你的更改,它应该保持不变)。
对于子模块:
git submodule foreach --recursive git checkout-symlinks
限制:
如果人们在做一些像git add -A
类的工作之前忘记了git checkout-symlinks
,他们可能会污染回购!
使用我们之前的“示例回购”:
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://technet.microsoft.com/en-us/library/cc753194
UPDATE 2014-11-12:因为我个人只是大量使用上面的rm-symlinks
和checkout-symlinks
别名,所以我设法忽略了add-symlink
别名中相当讨厌的错误。 之前, -n
没有被传递给echo
语句,该语句负责创建git symlink文件,该文件稍后将作为add-symlink
操作的一部分添加到暂存区域中。 这意味着一个尾随的换行符(Windows主机上的0x0D 0x0A
)被添加到使用add-symlink
创建的所有git符号add-symlink
。 虽然这些git符号链接仍然可以在Windows主机上使用rm-symlinks
“可移动”,但如果它们曾经被提交到公共仓库并且后来在基于posix的真正系统上克隆,那么这些链接总是会在另一个系统上出现侧。 此问题已得到解决,并且add-symlink
现在应按预期工作。
最新版本的git scm(testet 2.11.1)允许启用符号链接。 但是您必须再次使用符号链接克隆存储库git clone -c core.symlinks=true <URL>
。 您需要使用管理员权限运行此命令。 使用mklink也可以在Windows上创建符号链接。 看看wiki。