Reputation: 22610
When I try to create a symbolic link from the Git Bash shell, it fails every time all the time:
ln -s /c/Users/bzisad0/Work testlink
Output:
ln: creating symbolic link `testlink' to `/c/Users/bzisad0/Work': Permission denied
The only thing it does, besides giving the error message, is create an empty directory named (in this case) testlink
.
I don't see any problem with the ln
executable. For instance, it is owned by me and marked as executable:
which ln
ls -hal /bin/ln
Output:
/bin/ln
-rwxr-xr-x 1 BZISAD0 Administ 71k Sep 5 11:55 /bin/ln
I also own the current directory (~
, which is /c/Users/bzisad0
):
ls -dhal .
Output:
drwxr-xr-x 115 BZISAD0 Administ 40k Sep 5 12:23 .
I have administrative rights, and I've tried opening the Git Bash shell with "Run as Administrator", but that makes no difference.
I've tried opening the Windows properties for ln.exe
and setting the Privilege Level to "Run this program as an administrator" but that doesn't help.
I've gone into the Security → Advanced properties in Windows and made myself (rather than the Administrators group) the owner, but that doesn't fix anything either.
I'm at a loss. I don't know whether this error message is ultimately coming from ln
, from Bash, or from Windows, or how I could possibly lack the permission. How can I get to the bottom of this?
Upvotes: 116
Views: 97607
Reputation: 592
Doing the first two steps was enough for me. In case it doesn't work. can do the third step.
Upvotes: 0
Reputation: 52499
mklink
commandGit Bash shell fails to create symbolic links
In Git Bash in Windows, even if ln -s
"succeeds", it doesn't actually create a symlink! Rather, it just copies the entire target! That's lame.
So, to make a symlink in Windows, you have to use the mklink
command in the Windows Command Prompt (cmd.exe
), as an admin, or with "Developer Mode" turned on (see my step 1 in my question here to turn on Developer Mode), like this:
# view the help menu
mklink
# Absolute symlinks
# Make a symlink to a **directory** in Windows
mklink /d my_windows_symlink_to_a_dir "C:\path\to\some\target\dir"
# Make a symlink to a **file** in Windows
mklink my_windows_symlink_to_a_file "C:\path\to\some\target\file"
# Relative symlinks
# Make a symlink to a **directory** in Windows
mklink /d my_windows_symlink_to_a_dir "..\..\path\to\some\target\dir"
# Make a symlink to a **file** in Windows
mklink my_windows_symlink_to_a_file "..\..\path\to\some\target\file"
From the Git for Windows community wiki: https://github.com/git-for-windows/git/wiki/symbolic-links:
Creating symbolic links
By default, the
ln -s
command in Git Bash does not create symbolic links. Instead, it creates copies.To create symbolic links (provided your account has permission to do so), use the built-in
mklink
command, like so:mklink /d this-link-points-to c:\that-directory mklink this-link-points-to c:\that-file
Remember that you must run the mklink
command from the Windows Command Prompt (cmd.exe
), not from the Power Shell! From the Power Shell you'll get this error:
> mklink
mklink : The term 'mklink' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:1
+ mklink
+ ~~~~~~
+ CategoryInfo : ObjectNotFound: (mklink:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
But if you type mklink
in the Command Prompt, you'll get this help menu:
C:\WINDOWS\system32>mklink
Creates a symbolic link.
MKLINK [[/D] | [/H] | [/J]] Link Target
/D Creates a directory symbolic link. Default is a file
symbolic link.
/H Creates a hard link instead of a symbolic link.
/J Creates a Directory Junction.
Link Specifies the new symbolic link name.
Target Specifies the path (relative or absolute) that the new link
refers to.
C:\WINDOWS\system32>
git
, and sharing them between Linux and WindowsIf you create and commit a symlink (which points to ../some_dir
) in your repo on Linux with:
ln -si ../some_dir some_dir
and then pull that change down to your repo on Windows, this symlink will just show up as a plain text file named some_dir
and with the text ../some_dir
stored in it.
To fix this broken symlink on Windows, run the following in a Command Prompt as an administrator (not in PowerShell nor Git Bash):
# Delete this Windows symlink directory if it exists and you need to recreate
# and fix it.
rmdir some_dir
# Delete the Linux symlink file which was cloned to Windows as a plain text
# file.
del some_dir
# Now make the Windows symlink
mklink /d some_dir "..\some_dir"
Now, the symlink is recreated and fixed in Windows! git status
will not show this as a change to be committed, because apparently it is syntactically equivalent to the already-committed Linux symlink.
So, if someone clones this repo on Linux (which has the Linux-created symlinks committed to it), their symlinks are all intact and fine.
If someone clones this repo on Windows, however, they must use the Command Prompt as an administrator to manually delete and recreate all symlinks. This can instead be done via a batch script on Windows (learn how to write one, here) to make this easy. Just commit the batch script and instruct Windows users to right-click it and choose "Run as administrator" after cloning the repo in order to repair all symlinks in the repo on Windows. Here is an example batch script that can be committed to the Linux/Windows shared repo and run on Windows:
fix_windows_symlinks.bat Windows batch script to fix symlinks on Windows (reminder: comments begin with ::
or rem
):
:: Delete the Linux symlinks, and replace them with Windows ones.
:: (Fix or make required symlinks on Windows)
:: - This script must be run as an administrator! Right-click it and go to "Run
:: as administrator".
:: get the path to this dir; see: https://stackoverflow.com/a/3827582/4561887
SET SCRIPT_DIRECTORY=%~dp0
cd "%SCRIPT_DIRECTORY:~0,-1%"
:: 1. Delete any directory by this name. This also removes a Windows symlink
:: directory by this name if it exists.
:: - Run this *before* running the `del` cmd below, because the `del` command
:: accidentally used on a **directory** instead of a **file** will ask for a
:: strange "Are you sure (Y/N)?" confirmation instead of just failing. So,
:: instead, first remove the **directory** by this name using `rmdir`,
:: and *then* try to delete a file by this name using `del`, below.
rmdir some_dir
:: 2. Delete any file by this name. This could include the Linux symlink file
:: which was cloned to Windows as a plain text file.
:: - The `del` command will fail normally and without issue if such a file does
:: not exist. It will ask a strange "Are you sure (Y/N)?" confirmation if you
:: accidentally use `del` on a **directory** or symlink to a directory,
:: however, instead of on a **file**.
del some_dir
:: 3. Now make the fixed Windows symlink
mklink /d some_dir "..\some_dir"
:: Another example
rmdir another_dir
del another_dir
mklink /d another_dir "..\..\..\some\other\path\another_dir"
echo "Done!"
PAUSE
In this way, you can easily share repos between Windows and Linux users, even when they rely on relative or absolute symlinks in the repo for your build system, and even when these symlinks may vary between Linux and Windows.
Where I relearned about Windows symbolic links: https://github.com/git-for-windows/git/wiki/symbolic-links
Where I learned how to make a Windows batch script: https://www.howtogeek.com/263177/how-to-write-a-batch-script-on-windows/
Where I learned how to remove a directory in Windows, or a symlink to a directory in Windows: https://www.computerhope.com/mklink.htm#:~:text=To%20delete%20a%20symbolic%20link,link%20use%20the%20del%20command.
Emphasis added:
How do I delete a symbolic link?
To delete a symbolic link, treat it like any other directory or file. If you created a symbolic link using the command shown above, move to the root directory since it is "\Docs" and use the
rmdir
command [for symlinks to directories]. If you created a symbolic link (<SYMLINK>
) of a file, to delete a symbolic link use thedel
command [for symlinks to files].
Where I learned how to get a path to a batch script in Windows: How to get the path of the batch script in Windows?
I just learned that accidentally using del
on a directory or symlink to a directory will try to delete all contents of the directory, rather than the symlink (to the directory) itself! Watch out! See: How can I delete a symbolic link in Windows?:
Be very careful.
If you have a symbolic link that is a directory (made with
mklink /d
) then usingdel
will delete all of the files in the target directory (the directory that the link points to), rather than just the link.SOLUTION:
rmdir
on the other hand will only delete the directory link, not what the link points to.
Upvotes: 4
Reputation: 22692
A workaround is to run mklink
from Bash. This also allows you to create either a symbolic link or a junction point.
Take care to send the mklink
command as a single argument to cmd
...
cmd /c "mklink link target"
Here are the options for mklink
...
cmd /c mklink
Output:
Creates a symbolic link.
MKLINK [[/D] | [/H] | [/J]] Link Target
/D Creates a directory symbolic link. Default is a file
symbolic link.
/H Creates a hard link instead of a symbolic link.
/J Creates a Directory Junction.
Link specifies the new symbolic link name.
Target specifies the path (relative or absolute) that the new link
refers to.
Creating a symbolic link using mklink
requires special privileges (admin user) whereas creating a junction only requires access to the file system. The order of the arguments for ln -s
is also reversed when using mklink
.
If you want to create links via a GUI instead ... I recommend Link Shell Extension that is a Windows Explorer plugin for creating symbolic links, hard links, junction points, and volume mount points. I've been using it for years!
Symbolic links can be a life saver if you have a smaller SSD drive on your system C: drive and need to symbolic link some bloated folders that don't need to be on SSD, but off onto other drives. I use the free WinDirStat to find the disk space hogs.
Upvotes: 32
Reputation: 1093
For my setup, that is Git for Windows 2.11.0 installed on Windows 8.1, export MSYS=winsymlinks:nativestrict
does the trick as
The Git Bash shell may need to be run as an administrator, as by default on Windows only administrators can create the symbolic links.
So, in order to make tar -xf
work and create the required symbolic links:
export MSYS=winsymlinks:nativestrict
Upvotes: 96
Reputation: 1
git bash honors the symbolic links created by cygwin. The caveat is that the symbolic link not use, e.g., '/cygdrive/c/directory' and instead use '/c/directory'.
Upvotes: 0
Reputation: 4243
Grant yourself privileges to create symbolic links.
local security policies
Local Policies/User Rights Assignment/Create symbolic links
This grants you the privilege to create symbolic links. Note, this takes effect on the next login.
The next step is to figure out how ln
is configured:
env | grep MSYS
We are looking for MSYS=winsymlink:
which controls how ln
creates symbolic links.
If the variable doesn't exist, create it. Note, this will overwrite the existing MSYS environment variable.
setx MSYS winsymlinks:nativestrict
Run your shell as an administrator just to create symbolic links.
The error is somewhat self-explanatory, yet elusive.
You lack the appropriate privileges to run the command.
Be default, Windows only grants symlink creation rights to Administrators.
Cygwin has to do a song and dance to get around Windows subpar treatment of symbolic links.
Something, something "security"
¯\_(ツ)_/¯
I just realized OP had admin rights. I leave this answer up, hoping it's useful to others.
Upvotes: 18
Reputation: 1347
Instead of symbolic links on Windows, I found it easier to write a small Bash script that I place in my ~/bin directory.
To start Notepad++ with the npp
command, I have this file:
#!/usr/bin/bash
'/c/Program Files (x86)/Notepad++/notepad++.exe' $@
And I get the path syntax right by dragging and dropping the file from Windows Explorer into Vim.
The Windows command mklink /J Link Target
doesn't seem to work any more.
Upvotes: 0
Reputation: 51
For anyone who's interested in how to accomplish this in Windows 10 Git Bash 2.28.0.0.1:
You have to prefix the ln -s
command with the MSYS=..
instead of executing export MSYS=..
first, namely it's just one command:
MSYS=winsymlinks:nativestrict ln -s <TARGET> <NEW_LINK_NAME>
Upvotes: 5
Reputation: 2901
Extending Camilo Martin's answer as you need to use the /j parameter switch for Windows 10; otherwise the call will just return "You do not have sufficient privilege to perform this operation."
This works for Git Bash 2.20.1.windows.1/MINGW64 (Windows 10) without administrator rights (if you can read/write both /old/path and /link/path:
original_folder=$(cygpath -w "/old/path")
create_link_new_folder=$(cygpath -w "/link/path")
cmd <<< "mklink /j \"${create_link_new_folder}\" \"${original_folder}\"" > /dev/null
Upvotes: 6
Reputation: 1847
I prefer PowerShell to CMD, and thought I'd share the PowerShell version of this.
In my case it consists of making symbolic links linking ~/.$file to ~/dotfiles/$file, for dotfile configurations. I put this inside a .sh
script and ran it with Git Bash:
powershell New-Item -ItemType SymbolicLink\
-Path \$Home/.$file\
-Target \$Home/dotfiles/$file
Upvotes: 1
Reputation: 41
Since this is one of the top links that come up when searching for creating symbolic links in MSYS or Git Bash, I found the answer was to add
set MSYS=winsymlinks:native
when calling git-cmd.exe
(I run ConEmu) or uncomment the same line in the msys2_shell.bat
file.
Upvotes: 2
Reputation: 37898
It is possible, albeit extremely awkward, to create a symbolic link in MSysGit.
First, we need to make sure we are on Windows. Here's an example function to check that:
windows() { [[ -n "$WINDIR" ]]; }
Now, we can't do cmd /C
, because MSysGit will fornicate with this argument and turn it into C:
. Also, don't be tempted to use /K
; it only works if you don't have a K:
drive.
So while it will replace this value on program arguments, it won't on heredocs. We can use this to our advantage:
if windows; then
cmd <<< "mklink /D \"${link%/}\" \"${target%/}\"" > /dev/null
else
ln -s "$target" "$link"
fi
Also: note that I included /D
because I'm interested in directory symlinks only; Windows has that distinction. With plenty of effort, you could write a ln() { ... }
function that wraps the Windows API and serves as a complete drop-in solution, but that's... left as an exercise for the reader.
As a thank-you for the accepted answer, here's a more comprehensive function.
# We still need this.
windows() { [[ -n "$WINDIR" ]]; }
# Cross-platform symlink function. With one parameter, it will check
# whether the parameter is a symlink. With two parameters, it will create
# a symlink to a file or directory, with syntax: link $linkname $target
link() {
if [[ -z "$2" ]]; then
# Link-checking mode.
if windows; then
fsutil reparsepoint query "$1" > /dev/null
else
[[ -h "$1" ]]
fi
else
# Link-creation mode.
if windows; then
# Windows needs to be told if it's a directory or not. Infer that.
# Also: note that we convert `/` to `\`. In this case it's necessary.
if [[ -d "$2" ]]; then
cmd <<< "mklink /D \"$1\" \"${2//\//\\}\"" > /dev/null
else
cmd <<< "mklink \"$1\" \"${2//\//\\}\"" > /dev/null
fi
else
# You know what? I think ln's parameters are backwards.
ln -s "$2" "$1"
fi
fi
}
Also note a few things:
Bonus function: remove a link.
# Remove a link, cross-platform.
rmlink() {
if windows; then
# Again, Windows needs to be told if it's a file or directory.
if [[ -d "$1" ]]; then
rmdir "$1";
else
rm "$1"
fi
else
rm "$1"
fi
}
Upvotes: 86
Reputation: 627
I believe that the ln
that shipped with MSysGit simply tries to copy its arguments, rather than fiddle with links. This is because links only work (sort of) on NTFS filesystems, and the MSYS team didn't want to reimplement ln.
See, for example, http://mingw.5.n7.nabble.com/symbolic-link-to-My-Documents-in-MSYS-td28492.html
Upvotes: 19