What happens when I clone a repository with symlinks on Windows? - windows

There's been a lot of questions about adding support for symlinks on Windows. But, what actually happens when I clone a repository with symlinks on Windows?

Since version 1.5.3 of the native Git client git clone and git init will probe the target file system for symlink support and set the local repository configuration for core.symlinks accordingly, i.e. to false for FAT or NTFS. This makes symlinks created and committed e.g. under Linux appear as plain text files that contain the link text under Windows (see the git config documentation on core.symlinks for details).
Since Git for Windows version 2.10.2 the installer has an explicit option to enable symbolic link support.
In older versions of Git for Windows you can manually set core.symlinks to true which enabled Git to create symbolic links under the following constraints:
Symbolic links are only available on Windows Vista and later.
Symbolic links will only work on NTFS, not on FAT.
You need to be an admin and / or have the SeCreateSymbolicLinkPrivilege privilege.
Symbolic links on remote filesystems are disabled by default.
Windows' symbolic links are typed.
Many programs do not understand symbolic links (that includes older version of Windows Explorer).
More details are available in the Git for Windows wiki.
In older versions of Git for Windows manually setting core.symlinks manually to true after cloning and reset your working tree, you would get error messages similar to
$ git reset --hard HEAD
error: unable to create symlink directory (Function not implemented)
error: unable to create symlink linux-links/this_is_a_symbolic_link_to_file (Function not implemented)
fatal: Could not reset index file to revision 'HEAD'.
As a side note, the JGit client did not probe the target file system for symlink support until its version 3.3, so the core.symlinks setting was falling back to whatever the system / global Git configuration was. Starting with version 3.3 JGit probes for symlink support but seems to be too conservative, setting core.symlinks = false in some cases where symlinks would be in fact supported.
You can checkout https://github.com/sschuberth/git-playground which contains a bunch of links created on Linux for testing.

One solution would have a filter in order to detect symlinks stored by Git and replace them with Windows symlink.
That is detailed in "Git Symlinks in Windows"
However, true symlink support isn't just for now:
See issue 224 and the very recent (July 2012) discussion on GitHub (which you looked at):
here are three types of file system links on Windows: hardlinks, junctions, and symlinks.
Hardlinks and junctions are available since NT. Hardlinks can only point to files, junctions only to directories (on the same volume).
The symlinks available since Vista can point to either files or directories, also on different volumes.
mklink, which ships since Vista, can create all of the above. But the way it is called in the script makes it only create symlinks (which is good, IMHO, as they most closely resemble Linux symbolic links).
For pre-Vista, we'd need a fallback that either creates hardlinks for files using "fsutil hardlink" (but probably only if "ln" is called without "-s") and creates junctions for directories using "fsutils reparsepoint", or simply calls the original ln.exe.
In addition to breaking Windows XP setups, a change like this will also break standard Windows 7 setups, because mklink requires administrator privileges by default. This can be fixed by checking if it worked or not, and reverting to copying in such cases.
Just for the record: I played around a bit with trying to make symlink support in Git for Windows itself recently, but ended up concluding that the "symlink support" in Windows 7 and up is pretty close to useless for emulating Unix symlinks.
There is a project claiming "Open Source, 100% Compatible ln for Windows (and Junction Point library)", but:
Unfortunately, normal users doesn't have the required permissions to create symlinks by default on Windows. Combine this with the fact that you cannot change what a symlink points to in the same way that POSIX requires, makes them more or less useless for us. This I already wrote above.
Now, the authors can claim all they want that this is "100% compatible" for all I care, but a quick look at the source code reveals that it's not. They do not provide any fallbacks, they don't even load the CreateSymbolicLink function dynamically. So the result on non-symlink capable Windows versions will be a crash with a missing symbol error.

I have been working on Symlink support in msysgit here:
https://github.com/frogonwheels/git (branch mrg/symlink-v* .. currently v2)
The tests do not run to completion yet, and I have limited time in which to work on it, and no real short-term goal to motivate me. It'd be nice to be able to use projects like git-annex under msysgit.
My work is hampered by the lack of symlink support in the msys shell as well.
There's a command-line for granting the privileges that is suggested by the cygwin ln command. (You will need to run this as administrator).
editrights -a SeCreateSymbolicLinkPrivilege -a $YOUR_USER
The whole problem of Directory vs file symlinks is a biggie.
At the moment I'm of the opinion that as much as we can, we limit ourselves to making File symlinks work... and not allow directory symlinks in msysgit. It's not ideal, but the reality is that any solution is a bit of a cludge, trying to impose possix linking onto the realities of the NTFS incompatibilities with possix linking is just painful.
We can try and detect whether the target is file or directory, but I can think of a few issues with that just off the top of my head - especially the whole problem of which order the entities get created.

Related

Why doesn't Linux git, when run in the Windows WSL and operating on a NTFS file system, create a proper Windows symlink?

When I run the WSL bash shell, I can use the "ln -s" command to create a symlink. No matter if I am working on an NTFS file system or on the WSL file system, the symbolic link gets created as expected. Specifically, the link in the WSL file system works just like a Linux symlink and the symlink in the NTFS file system is a Windows symlink and I can use it both from WSL and Windows.
Contrast that to how symlinks work in the Linux version of git running in WSL. I clone a repo that contains symlinks to the NTFS file system. I am not sure what the links are, but they are most definitely not Windows symlinks.
Some might say that I should use the Git for Windows which creates the proper Windows symlink when I clone the repo. The only issue is that we have tools written as bash scripts that everyone uses and these tools call git. They work fine on Linux but from WSL they do not perform as expected because of the symlink issue. I have found that I need all the command-line developer tools to run in WSL so that they can call each other and pass file paths and env vars and such. So it is really not an option for me to run Git for Windows.
Is there any means to get this addressed? If the WSL bash shell can function properly then surely a small change in the Linux version of git could also fix this issue. This smells of some sort of philosophical fight between Windows and Linux. Or is there a legacy to this that pre-dates Git for Windows... In which case, surely there is a way to enable a new treatment of symlinks for those who want to use Windows symlinks.
This is likely an issue in Windows and the Windows Subsystem for Linux.
The emulated Linux environment creates symlinks using the symlink(2) system call, much like the ln -s command does.
The reason this is tricky to support is because Windows symlinks are less capable than Unix symlinks. Unix symlinks can point to any location, regardless of the object at that location, and the destination need not exist. Windows symlinks, on the other hand, point to either a file or a directory, but are pinned to one type.
The standard Git, as shipped by upstream, checks to see if symlinks can be created pointing to a file that does not exist. That this works is important because Git need not check out the destination file before the symlink. If this fails, Git sets core.symlinks to false, and what's written instead are files with the contents of the symlinks. WSL can't create a symlink of the right type when this test occurs (because there's no destination), and consequently, the operation fails.
Git for Windows likely has some functionality to work around this issue with symlinks in Windows. It also has a huge number of patches which are not included in the version shipped by upstream, and hence not the version used by most Linux distros.
I should point out that there's a Linux distro that also shipped a patch that permitted symlinks to be created only if they pointed to a file the user (and not anyone else) owned, and it broke things in the same way.
You can try to run git config core.symlinks true in a fresh repository and see if that works as you expect; it may, or it may not. If it doesn't, you can see if the Git for Windows maintainer will send the patch they're using upstream, and it may eventually be included in a newer version of Git, and hence in a newer version of Linux distros. However, because Linux distros typically provide stable versions, this will take time, usually on the order of years, unless you pull down a newer package.
In general, though, you can't expect Unix code to work adequately on an NTFS file system because it's fundamentally different, and Microsoft even documents this behavior. Microsoft knew (or should have known) when they added symlinks how Unix symlinks functioned, and for some reason they chose to do it differently. I personally think that was a mistake, and here we are now.

How to use the same repo on external FAT drive on OSX and Windows?

I have to work on the same project in multiple systems for some reason. So I took external drive with FAT filesystem and copied my repo from OSX to the external drive. Now I want to be able to use GIT to work on that project from both systems OSX and Windows. But first I have problems with file permissions as FAT does not have permissions, the file permissions were changed when I was doing diff on both platforms. This was easily solved when I set:
git config core.fileMode false
But I cannot solve problems with symbolic links. I need them to be unchanged as I don't need them on Windows. As far as FAT does not support symbolic links, when I copied files form Mac to external FAT HDD something is changed in those files. Now I get diff only in Windows related to symbolic links. But I don't get any diff on OSX. How this can be? Symbolic link is also a file right? Now I don't understand the file is changed or no? Systems tell me different things? How to solve this problem?
Tries using this:
git config --global core.symlink false
but it didn't help, as, I guess, if works when you checkout. But I have only one repo and I don't do checkout to use it on Windows. I just use it from my external HDD.
The correct way to handle symlinks in git between multiple systems is to use it the way it was designed to be used, which is to have multiple clones and push and pull between them. So you'd have your original clone on the external disk, and one clone on each computer. When you push-pull between them, git will update the symlinks: How does git handle symbolic links?

Are there any portable versions of Git for Mac OSX?

I'd like to be able to do some development work on public/borrowed computers (where I have no root privileges) and I've managed to get most of my tools working off of a USB stick but I still haven't found a Git solution.
The portable versions of Git that I have found are Windows-only. Do you know of any Mac alternatives?
EDIT: I've gotten a few suggestions to just copy my current installation of git from /usr/bin and put that on a USB drive. That's a great idea but I don't have a current installation of git to copy from. I won't have my own computer back for a couple of weeks.
Would you happen to know where I could grab a pre-compiled version of git for OSX? I don't have access to homebrew or... anything really. Ideally I could just grab a zip file from somewhere and dump it on my USB stick.
I've managed to put together a solution based on ideas from several different people (thanks to all of you):
Download the Git binary for Mac OSX from git-scm.com. The downloaded DMG contains a PKG file.
Unpack the PKG file with unpkg or something similar. (This step is necessary because normally PKG files can only be installed with root privileges.)
Drop the etc and git folders that were just unpacked onto your USB stick.
Now, whenever you plug in your USB stick, just make sure to put the location of the git directory on your PATH (like this: export PATH=$PATH:/path/to/git/on/usb/stick) and you'll be good to go!
NOTE: I've tested many of the git commands using this method and most seem to work without any issues. However, git init will complain like this: warning: templates not found /usr/local/git/share/git-core/templates. Not surprising since the templates are actually on your USB stick and not /usr/local. Despite this warning my repos seem to be working just fine.
Add a environmental variable GIT_TEMPLATE_DIR
to quote the documentation
The template directory contains files and directories that will be
copied to the $GIT_DIR after it is created.
The template directory will be one of the following (in order):
the argument given with the --template option;
the contents of the $GIT_TEMPLATE_DIR environment variable;
the init.templatedir configuration variable; or
the default template directory: /usr/share/git-core/templates.
I've uploaded my /usr/bin/git and /usr/bin/git-shell to
http://www.club.cc.cmu.edu/~ajo/disseminate/git
http://www.club.cc.cmu.edu/~ajo/disseminate/git-shell
You can try those, but I have no idea if this approach is likely to work. (There are no interesting shared-library dependencies reported by otool -L, but I don't know what else might theoretically go wrong with the idea.)
FWIW, I use Mac OS X 10.8.2 and git version 1.7.10.2 (Apple Git-33).
Not sure if this is appropriate as an "answer", since I don't plan to leave those links working indefinitely. Maybe I (or someone) can delete this answer in a month or two.
Apple's command line tools for developers include git, I believe. You can find them on http://developer.apple.com/; you might need a free developer account. They package will want to install to /usr/bin etc., but you should be able to use pax to extract the contents wherever you want.

Can I version a path in git whose name is operating system dependent

I wish to store my Mac, Windows and Linux vim configuration files in git. On *nix systems, your vim configuration files go in ${HOME}/.vim but for the Windows binary, the same directory is named "vimfiles" Can I configure git to accommodate the different directory name?
You don't need to configure Git, just tell Vim to use ~/.vim for Windows, too, by putting the following fragment into your ~/.vimrc:
" On Windows, also use '.vim' instead of 'vimfiles'; this makes synchronization
" across (heterogeneous) systems easier.
if has('win32') || has('win64')
set runtimepath=$HOME/.vim,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after,$HOME/.vim/after
endif
My setup is very simple.
On Mac, the versioned directory is:
/Users/username/.vim
On Linux, it is:
/home/username/.vim
On Windows XP (yes), it is:
C:\Documents and Settings\username\vimfiles
They all point to the same GitHub repository.
My settings are stored in a vimrc (no . or _) file located at the root of the repository. Therefore its versioned and commited/pushed/pulled like all the rest.
The actual default user-specific vimrc,
/Users/username/.vimrc
/home/username/.vimrc
C:\Documents and Settings\username\_vimrc
is a real file, no need for a symlink. It contains only one line:
runtime vimrc
that tells vim to read, and execute, my vimrc.
Because of how :runtime works, I don't need to use a real absolute path which would be different on Unix-like platforms and on Windows.
Setting up a new machine or user is as simple as cloning my repo and typing two easy to remember words.
I also need to share config files for vim and other applications between multiple systems, and I found that git was not only overkill but also required manual syncing on each system to get the latest updates and to publish changes. A better solution for me is to put these config files into Dropbox, make all of my systems connect to my Dropbox account, and create symbolic links to these shared files.
For example, I put my vimrc file under Dropbox/conf/vimrc, and then did
ln -s ~/Dropbox/conf/vimrc ~/.vimrc
You should be able to use Windows' mklink to similar effect to create a _vimrc symlink to that same file. In the same way, a common Dropbox/conf/vim directory could be linked to locally as .vim or .vimfiles or whatever your OS' vim executable prefers.
Dropbox keeps a history of changes over the last 30 days, which is enough to handle recovering from most problems for which I needed git. The cool thing is that you can add that new macro or setting to your .vimrc and it is automatically available on all your systems.
Of course this approach is also handy for your other config files, too (.gitconfig, .gitignore, .bashrc, etc.).

How can I deal with a git repo containing symlinks on windows

As part of our code repository, we have a symlink which is internal to the working tree.
Zend -> ZendFramework1.10/library/Zend
This works fine for all the developers running Linux or OS X, but we're now getting some people trying to use the repository on Windows.
The functionality of the symlink can be replicated by deleting the link git creates, and using mklink to create the equivalent directory junction.
However, git now sees this as the deletion of the symbolic link, and the addition of a proper directory.
I'm looking for a way to have the two co-exist, is there a way to tell the Windows machines to ignore the Zend directory, even though it's technically versioned. If this breaks when the files in that directory change then so be it, but it'd be nice to be able to work with the rest of the repo without having to worry about the link.
You can use git-update-index to tell git to ignore changes to the file:
git update-index --assume-unchanged Zend
You could probably use cygwin on the machines running windows.
As Magnus Skog has suggested, git under cygwin copes correctly with the symlinks. I switched away from Git for Windows for this reason alone. However you need to weigh up this advantage against the overhead of setting up the cygwin environment for your Windows users (particularly for those unfamiliar with *nix and the command line; for example there are a number of outstanding issues when trying to use Cygwin and TortoiseGit.)

Resources