windows上使用git仓库的问题(换行符、文件权限、软链接)

更多文章欢迎访问 程序员小非 博客

团队开发过程中经常会使用git工具来管理代码,团队中成员使用的终端可能是windows和类Unix系统,由于操作系统的差异可能会给大家使用git带来一些困扰。尤其是将类Unix系统中建立的仓库在windows系统打开的时候。在windows上使用*nix建立的仓库主要处理好三个事情,换行符、文件权限、软连接。

1.处理换行符

换行符在各个系统中有不同的表示,一般使用两种符号,一种是LF\n换行,一种是CR\r回车。windows系统使用的是\r\n, unix系统使用的是\r,linux系统使用的是\n。这样就导致从不同系统checkout代码时出现文件未修改却出现modified的情况,git对此有自动转换的机制,设置core.autocrlftrue,在checkout和checkin时就会自动转换换行符。

git config --global core.autocrlf true

2.处理权限

*nix系统对拥有者、组、其他几种用户都有明确的读写执行权限,这些在windows上没有对应的机制,这个问题在使用git时表现为没有修改文件却出现很多modified的文件,git status显示 typechange。一般出现在增加可执行权限的文件上。git对此也有一定的策略,可以设置core.filemodefalse,这样就会忽略文件权限带来的改变。

git config --add core.filemode false

如果clone前没有设置,导致已经进行了修改,可以用下面的命令来批量恢复这些修改。

git status | grep typechange | awk '{print $2}' | xargs git checkout

3.处理软链接

这可能是最让人头疼的问题,在*nix系统里我们经常会使用ln来给文件和文件夹设置软链接,而这些软链接到windwos就失效了,不能进行导航,ide也不识别,如果有关键文件夹设置了软链接,仓库在windows上就基本不可用了。这也是笔者遇到的一大难题。
git暂时还不能像处理换行符一样优雅的处理软链接,需要我们自己手动来处理。windows中也有自己的软链接实现方式,建立软链接使用mklink命令,mklink的使用文档是这么写的:

MKLINK [[/D] | [/H] | [/J]] Link Target

/D creates a symbolic link, or a soft link.This essentially acts like a shortcut to a folder in prior versions of Windows, except you don’t have to use an actual shortcut.

/H creates a hard link, which points directly to the file.This option can’t be used for folders directly for some reason, you’ll have to use the next option.

/J creates a “Directory Junction”A Directory Junction is actually just a hard link to a directory. This is a feature that existed prior to Vista as well. If you are trying to symlink to a directory using a hard link, then you should use this option.

主要就分为三种,/D /J /H,/D/J用于目录,/H用于文件,对链接的修改对不会影响源文件,/D/J不会占用空间,/H会占用空间。具体可以参考mklink.

现在我们就要将*nix中的所有软链接换成windows的link,主要三步:

  • 找到软链接文件
    • 使用git ls-files -s,git把所有的软链接设置成120000类型
  • 修改软链接
    • 使用mklink命令
  • 屏蔽软链接的修改
    • 使用git update-index --assume-unchanged

上述步骤已经整理成一个python脚本,可以从我的gist获得。脚本只在Babun-windows上最好用的终端 测试过,其他终端可能无法使用,如有错误请自行修改。

考虑国内访问github速度,贴出脚本如下,在仓库根目录执行。

import os

def rindex(lst, value):
  try:
    return lst.rindex(value)
  except ValueError:
    return -1
# find symbol link files or dirs
fp = os.popen("git ls-files -s | awk '/120000/{print $4}'")
links = fp.read().strip().split("\n")
# get symbol links' parent dir
link_dir = set()
for link in links:
    index = rindex(link, "/")
    if (index != -1):
      link_dir.add(link[:index])
    else:
      link_dir.add(".")
work_dir = os.getcwd()
# make link for every symbol link
for d in link_dir:
  os.chdir("/".join([work_dir,d]))
  fp = os.popen("ls -la")
  items = fp.read().strip().split("\n")
  for item in items:
    if "->" in item:
      tks = item.split("->")
      src = tks[0].strip().split(" ")[-1]
      dst = tks[1].strip().split("/")
      if (len(dst) > 1):
        dst = "\\\\".join(dst)
      else:
        dst = dst[0]
      print ("link " + src + " -> " + dst)
      os.popen("rm " + src)
      if (os.path.isfile(dst)):
        os.popen("cmd /c mklink /H " + src + " " + dst)
      else:
        os.popen("cmd /c mklink /j " + src + " " + dst)
      # make links unchanged 
      os.popen("git update-index --assume-unchanged " + "/".join([os.getcwd(), src]))


欢迎关注微信公众号

版权声明:本文为njukingway原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。