Git Symlinks in Windows

2018-12-31 18:30发布

Our developers use a mix of Windows and Unix based OS's. Therefore, symlinks created on Unix machines become a problem for Windows developers. In windows (msysgit), the symlink is converted to a text file with a path to the file it points to. Instead, I'd like to convert the symlink into an actual Windows symlink.

The (updated) solution I have to this is:

  • Write a post-checkout script that will recursively look for "symlink" text files.
  • Replace them with windows symlink (using mklink) with same name and extension as dummy "symlink"
  • Ignore these windows symlink by adding entry into .git/info/exclude

I have not implemented this, but I believe this is a solid approach to this problem.

Questions:

  1. What, if any, downsides do you see to this approach?
  2. Is this post-checkout script even implementable? i.e. can I recursively find out the dummy "symlink" files git creates?
  3. Has anybody already worked on such script?

12条回答
不流泪的眼
2楼-- · 2018-12-31 18:44

Short answer: They are now supported nicely, if you can enable developer mode.

From https://blogs.windows.com/buildingapps/2016/12/02/symlinks-windows-10/

Now in Windows 10 Creators Update, a user (with admin rights) can first enable Developer Mode, and then any user on the machine can run the mklink command without elevating a command-line console.

What drove this change? The availability and use of symlinks is a big deal to modern developers:

Many popular development tools like git and package managers like npm recognize and persist symlinks when creating repos or packages, respectively. When those repos or packages are then restored elsewhere, the symlinks are also restored, ensuring disk space (and the user’s time) isn’t wasted.

Easy to overlook with all the other announcements of the "Creator's update", but if you enable Developer Mode, you can create symlinks without elevated privileges. You might have to re-install and make sure support is enabled, as it's not by default.

Symbolic Links aren't enabled by default

查看更多
低头抚发
3楼-- · 2018-12-31 18:44

I was looking for an easy solution to deal with the unix symbolic links on windows. Thank you very much for the above Git aliases. There is one little optimization that can be done to the rm-symlinks so that it doesn't delete the files in the destination folder in case the alias is run a second time accidentally. Please observe the new if condition in the loop to make sure the file is not already a link to a directory before the logic is run.

git config --global alias.rm-symlinks '!__git_rm_symlinks(){
for symlink in $(git ls-files -s | egrep "^120000" | cut -f2); do
    *if [ -d "$symlink" ]; then
      continue
    fi*
    git rm-symlink "$symlink"
    git update-index --assume-unchanged "$symlink"
done
}; __git_rm_symlinksenter 
查看更多
君临天下
4楼-- · 2018-12-31 18:46

I use sym links all the time between my document root and git repo directory. I like to keep them separate. On windows I use mklink /j option. The junction seems to let git behave normally:

>mklink /j <location(path) of link> <source of link>

for example:

>mklink /j c:\gitRepos\Posts C:\Bitnami\wamp\apache2\htdocs\Posts

查看更多
不流泪的眼
5楼-- · 2018-12-31 18:48

I would suggest you don't use symlinks within the repo'. Store the actual content inside the repo' and then place symlinks out side the repo' that point to the content.

So lets say you are using a repo' to compare hosting your site on *nix with hosting on win. Store the content in your repo', lets say /httpRepoContent and c:\httpRepoContent with this being the folder that is synced via GIT, SVN etc.

Then, replace the content folder of you web server (/var/www and c:\program files\web server\www {names don't really matter, edit if you must}) with a symbolic link to the content in your repo'. The web servers will see the content as actually in the 'right' place, but you get to use your source control.

However, if you need to use symlinks with in the repo', you will need to look into something like some sort of pre/post commit scripts. I know you can use them to do things, such as parse code files through a formatter for example, so it should be possible to convert the symlinks between platforms.

if any one knows a good place to learn how to do these scripts for the common source controls, SVN GIT MG, then please do add a comment.

查看更多
墨雨无痕
6楼-- · 2018-12-31 18:49

It ought to be implemented in msysgit, but there are two downsides:

  • Symbolic links are only available in Windows Vista and later (should not be an issue in 2011, and yet it is...), since older versions only support directory junctions.
  • (the big one) Microsoft considers symbolic links a security risk and so only administrators can create them by default. You'll need to elevate privileges of the git process or use fstool to change this behavior on every machine you work on.

I did a quick search and there is work being actively done on this, see issue 224.

查看更多
像晚风撩人
7楼-- · 2018-12-31 18:54

Here is a batch script for converting symlinks in repository, for files only, based on Josh Lee's answer. Script with some additional check for administrator rights is at https://gist.github.com/Quazistax/8daf09080bf54b4c7641.

@echo off
pushd "%~dp0"
setlocal EnableDelayedExpansion

for /f "tokens=3,*" %%e in ('git ls-files -s ^| findstr /R /C:"^120000"') do (
     call :processFirstLine %%f
)
REM pause
goto :eof

:processFirstLine
@echo.
@echo FILE:    %1

dir "%~f1" | find "<SYMLINK>" >NUL && (
  @echo FILE already is a symlink
  goto :eof
)

for /f "usebackq tokens=*" %%l in ("%~f1") do (
  @echo LINK TO: %%l

  del "%~f1"
  if not !ERRORLEVEL! == 0 (
    @echo FAILED: del
    goto :eof
  )

  setlocal
  call :expandRelative linkto "%1" "%%l"
  mklink "%~f1" "!linkto!"
  endlocal
  if not !ERRORLEVEL! == 0 (
    @echo FAILED: mklink
    @echo reverting deletion...
    git checkout -- "%~f1"
    goto :eof
  )

  git update-index --assume-unchanged "%1"
  if not !ERRORLEVEL! == 0 (
    @echo FAILED: git update-index --assume-unchanged
    goto :eof
  )
  @echo SUCCESS
  goto :eof
)
goto :eof

:: param1 = result variable
:: param2 = reference path from which relative will be resolved
:: param3 = relative path
:expandRelative
  pushd .
  cd "%~dp2"
  set %1=%~f3
  popd
goto :eof
查看更多
登录 后发表回答