Git Bash shell fails to create symbolic links
Asked Answered
L

12

112

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 SecurityAdvanced 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?

Larrabee answered 5/9, 2013 at 16:50 Comment(0)
V
86

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:

  1. I just wrote this and briefly tested it on Windows 7 and Ubuntu, give it a try first if you're from 2015 and using Windows 9.
  2. NTFS has reparse points and junction points. I chose reparse points, because it's more of an actual symbolic link and works for files or directories, but junction points would have the benefit of being an usable solution in Windows XP, except it's just for directories.
  3. Some filesystems, the FAT ones in particular, do not support symbolic links. Modern Windows versions do not support booting from them anymore, but Windows and Linux can mount them.

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
}
Vaudois answered 19/8, 2014 at 23:56 Comment(22)
The parameters for ln aren't really backwards since it supports multiple parameters, you can just list a bunch of files and a directory as a destination and it will work :) Also, with the normal order it is similar to cp and mv which is less confusing.Hydatid
@Hydatid See, it's great that I didn't try to create a "drop-in" solution, lol. I had no idea you could pass more than two paths to ln.Vaudois
Unfortunately, this gives me You do not have sufficient privilege to perform this operation.Capriccio
@Capriccio Are you trying that on a regular folder on your profile? Or some system directory? Also, which version of Windows?Vaudois
@Capriccio That's wierd because that's what I'm using. Do you use MSYSGIT or some other Bash port?Vaudois
Yes, it's the msysgit port. Maybe should add that I don't disable UAC.Capriccio
@Capriccio That could be it! I disable UAC and run under administrator.Vaudois
I used to think ln's parameters were backwards too, until I realized they were in the same order as cp's.Moire
I tried your more comprehensive function, and it worked like a charm. THANK YOU so much!Moire
Hey man, this is a really sweet answer, and this feels like nitpicking, but... WHY did you have to go and reverse the parameter order? it's "link target linkname" not the other way around. Pls. (Edit: OK I realize it's because you want the one-parameter overload to have it check the linkname if it is a link or not whereas unix ln does not do this. Point still stands though.)Shirashirah
@StevenLu In retrospect, maybe I should have made it consistent with ln's parameter order, and used shift for the overload. Thanks for the compliment :)Vaudois
ln's parameters seem intuitively "backwards" to me as well, though I can't think of any concrete benefit of having them the other way around. But, jeeze, this is an SO answer, not a library. If people want to switch the parameter order, they can switch the parameter order; no one is forcing them to copy&paste without modification!Sugar
Also, it appears that this doesn't really handle absolute paths correctly. Now that cygpath is bundled with git-bash, I'd suggest using it to correct this issue by creating a variable target: target="$(cygpath -d ${1})"Sugar
@KyleStrand Could you tell me what sort of absolute path was giving you troubles? Was it in the shape /foo/bar or /c/Users/foobar or C:\Users\foobar?Vaudois
The first one. I didn't thoroughly check that this happened for all such paths, but it appeared that /c/Users/foobar somehow became /c/c/Users/foobar.Sugar
I'm from the future but I forgot to buy Windows 9 on the way.Epineurium
yes, ln's parameters are backward, even though it mimics cp's parameter order.Cambogia
I don't think this has been mentioned, but it isn't best to use rm on a symlink. Instead unlink should be used. #210620Gaffrigged
The provided link function failed when the windows path has spaces. But It taught me how to use mklink native windows command, so did the trick. ThanksTrefoil
I'm in 2019, still no Windows 9 :)Dioecious
2023 here, still no Windows 9 :)Milagrosmilam
@geedew, @CamiloMartin: I have problems with deleting the symlink: rmdir deleteme -> rmdir: failed to remove 'deleteme': Directory not empty // unlink deleteme -> unlink: cannot unlink 'deleteme': Operation not permittedPerr
C
91

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:

  1. Run Git Bash shell as an administrator
  2. Run export MSYS=winsymlinks:nativestrict
  3. Run tar
Consensus answered 1/12, 2016 at 15:19 Comment(5)
it's the real answer~Herzegovina
you don't need to run as administrator if you turn on windows developer mode (requires Windows 10 version 1703 or later) learn.microsoft.com/en-us/windows/uwp/get-started/…Phosphorylase
ln -s currentFile newLink also works with this approach (not just tar). The link created appears as a shortcut in windows explorer, but following the shortcut does not jump to the other folder's path, instead you see a path like C:\currentFolder\newLink. I have not seen a Windows link like this before!Alisaalisan
I've developed an example with samples and images here: https://mcmap.net/q/49000/-how-can-i-make-symlinks-made-from-inside-docker-linux-containers-to-be-seen-from-a-windows-host-maybe-involving-samba-if-needed - Also back-linked from there to here.Racecourse
real deal answer.Trituration
V
86

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:

  1. I just wrote this and briefly tested it on Windows 7 and Ubuntu, give it a try first if you're from 2015 and using Windows 9.
  2. NTFS has reparse points and junction points. I chose reparse points, because it's more of an actual symbolic link and works for files or directories, but junction points would have the benefit of being an usable solution in Windows XP, except it's just for directories.
  3. Some filesystems, the FAT ones in particular, do not support symbolic links. Modern Windows versions do not support booting from them anymore, but Windows and Linux can mount them.

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
}
Vaudois answered 19/8, 2014 at 23:56 Comment(22)
The parameters for ln aren't really backwards since it supports multiple parameters, you can just list a bunch of files and a directory as a destination and it will work :) Also, with the normal order it is similar to cp and mv which is less confusing.Hydatid
@Hydatid See, it's great that I didn't try to create a "drop-in" solution, lol. I had no idea you could pass more than two paths to ln.Vaudois
Unfortunately, this gives me You do not have sufficient privilege to perform this operation.Capriccio
@Capriccio Are you trying that on a regular folder on your profile? Or some system directory? Also, which version of Windows?Vaudois
@Capriccio That's wierd because that's what I'm using. Do you use MSYSGIT or some other Bash port?Vaudois
Yes, it's the msysgit port. Maybe should add that I don't disable UAC.Capriccio
@Capriccio That could be it! I disable UAC and run under administrator.Vaudois
I used to think ln's parameters were backwards too, until I realized they were in the same order as cp's.Moire
I tried your more comprehensive function, and it worked like a charm. THANK YOU so much!Moire
Hey man, this is a really sweet answer, and this feels like nitpicking, but... WHY did you have to go and reverse the parameter order? it's "link target linkname" not the other way around. Pls. (Edit: OK I realize it's because you want the one-parameter overload to have it check the linkname if it is a link or not whereas unix ln does not do this. Point still stands though.)Shirashirah
@StevenLu In retrospect, maybe I should have made it consistent with ln's parameter order, and used shift for the overload. Thanks for the compliment :)Vaudois
ln's parameters seem intuitively "backwards" to me as well, though I can't think of any concrete benefit of having them the other way around. But, jeeze, this is an SO answer, not a library. If people want to switch the parameter order, they can switch the parameter order; no one is forcing them to copy&paste without modification!Sugar
Also, it appears that this doesn't really handle absolute paths correctly. Now that cygpath is bundled with git-bash, I'd suggest using it to correct this issue by creating a variable target: target="$(cygpath -d ${1})"Sugar
@KyleStrand Could you tell me what sort of absolute path was giving you troubles? Was it in the shape /foo/bar or /c/Users/foobar or C:\Users\foobar?Vaudois
The first one. I didn't thoroughly check that this happened for all such paths, but it appeared that /c/Users/foobar somehow became /c/c/Users/foobar.Sugar
I'm from the future but I forgot to buy Windows 9 on the way.Epineurium
yes, ln's parameters are backward, even though it mimics cp's parameter order.Cambogia
I don't think this has been mentioned, but it isn't best to use rm on a symlink. Instead unlink should be used. #210620Gaffrigged
The provided link function failed when the windows path has spaces. But It taught me how to use mklink native windows command, so did the trick. ThanksTrefoil
I'm in 2019, still no Windows 9 :)Dioecious
2023 here, still no Windows 9 :)Milagrosmilam
@geedew, @CamiloMartin: I have problems with deleting the symlink: rmdir deleteme -> rmdir: failed to remove 'deleteme': Directory not empty // unlink deleteme -> unlink: cannot unlink 'deleteme': Operation not permittedPerr
T
32

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!

Link Shell Extension

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.

Tella answered 30/6, 2014 at 3:7 Comment(9)
Pervasively enough, MSYSGIT manages to further sodomize the user by making it impossible to run cmd /c because /c is always going to be replaced into C: (it's filesystem uses /c/Windows/... convention). Lucky thing you can do echo -n | cmd /k ... if you don't have a K: drive. Brilliant.Vaudois
Oh and good luck with the /d argument. Buy a netbook with no disc drive and unplug external HDDs to run that :PVaudois
Yes I agree it sucks that both Windows and msysgit still has rather poor support for symlinks. I'm just documenting the options not praising them ... so really no justification for down voting this answer.Loar
Downovte? I didn't downvote you nor imply you have anything to do with this issue, just ranting about my frustration. Actually, the voting breakdown for your answer shows no downvotes :) i.imgur.com/xVYFE5n.png Also: Windows supports symlinks just fine, it's just msysgit that should make its ln command wrap the WinAPI. I do propose a possible solution in my answer, though.Vaudois
Is there any other reason why people say "Windows has poor support for symlinks", other than the fact that it requires admin for security reasons? You can grant a user the right to create symlinks unelevated using secpol.msc.Consumption
@JustinDunlap Early implementation for linking in Windows NT/2000 was poor and just looked like a "hack" afterthought (Vista/Win7/8 implementations cleaned this up). Most Windows devs/sysadmins are not used to using them and MS documentation almost never refers to them. Consequently many Windows devs don't consider them in software solutions! I suspect many link related security scenarios have not been considered by product vendors hence MS made them "admin only". On Linux/OSX/Unix they are mainstream/encouraged so most apps are well tested with them and expected to work with them.Loar
Even on *nix issues pop up regarding symlinks and security. Apache has (or had) a serious vuln related to symlinks and I've seen several cases where *nix apps were vulnerable to symlink race issues. I imagine that MS had visions of all the ways that malware could exploit symlinks when making the decision to require admin by default. At any rate, they did make it possible to easily change this should you want to create them unelevated.Consumption
@CamiloMartin /c can be escaped like this //c to get the powershell command instead instead of the C: drive. I also wanted to add a couple of examples of exactly what the syntax needs to look like to use mklink in the git bash shell as it took me a while to figure it out. As Tony describes quoting the entire mmklink command will work, like this: cmd //c "mklink .\b_dir\test.txt .\a_dir\test.txt", or you can omit the quotes and escape the backslahes like this: cmd //c mklink .\\b_dir\\test.txt .\\a_dir\\test.txtInger
@GrantHumphries Great, didn't know about //c. By the way, for cases like that single quotes are usually more convenient because there's no parsing done inside these strings.Vaudois
D
19

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

Disoperation answered 23/10, 2013 at 19:27 Comment(9)
If I use it to make hard links they seem to work, at least in my very brief testing, so I assume you mean just with regard to symbolic links?Larrabee
You are correct. When I tried just doing ln file1 file2; ln file1 file3; ln file1 file4 I got what appeared to be valid link counts in the output of ls -l. But be aware that it fails with ln: hard link not allowed for directory. if you try that.Disoperation
Don't most modern Windows systems already have NTFS? Also, haven't they had them since Windows NT 3.1 in 1993 (according to trusty old Wikipedia)?Wiggly
Yes, it seems like a glaring omission, and not one they can blame on Microsoft.Larrabee
FAT is still supported, since it consumes less space on small systems. And I have the feeling that the mingw/msys team is not big on re-visiting decisions, since they are a little busy. :)Disoperation
@AustinHastings, Linux doesn't allow hard links on directories either, so that part is still in line with ln.Wiggly
@AustinHastings No, FAT is not really supported. You can't install currently-supported versions of Windows on FAT anymore.Vaudois
FAT is still supported on Windows, you just can't install Windows to a FAT partition. You can have all your other partitions be FAT if you want, and it's very common for external devices like USB drives and SD cards to be FAT formatted. But the main reason for not supporting symlinks in MSYS is that only Vista or newer supports them. And as Austin Hastings suggests the developers apparently haven't seen a pressing need to add symbolic link support since then.Bondwoman
The link is broken: "Hmm. We’re having trouble finding that site. We can’t connect to the server at mingw.5.n7.nabble.com."Lagos
T
15

Do

Grant yourself privileges to create symbolic links.

  1. Search for local security policies
  2. Local Policies/User Rights Assignment/Create symbolic links
  3. Take a moment to scold Windows. "Bad OS! Bad!"
  4. Profit

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

Do not

Run your shell as an administrator just to create symbolic links.

Explanation

The error is somewhat self-explanatory, yet elusive.

You lack the appropriate privileges to run the command.

Why?

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.

Why?

Something, something "security"

¯\_(ツ)_/¯

Edit:

I just realized OP had admin rights. I leave this answer up, hoping it's useful to others.

Toothlike answered 4/6, 2021 at 9:25 Comment(1)
This is the best answer, and should be the accepted one, because one should not "run your shell as an administrator just to create symbolic links", thus this is a complete solution. And oh, I do have admin rights too, but still getting Operation not permitted when creating symbolic links.Caliph
C
6

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
Cleon answered 13/5, 2019 at 18:3 Comment(0)
C
5

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>
Chaffee answered 15/11, 2020 at 13:10 Comment(2)
Any idea why? export should set the env variable for all childrenDecosta
What is the reason? Why is it necessary? How does it work? Please respond by editing (changing) your answer, not here in comments (without "Edit:", "Update:", or similar - the answer should appear as if it was written today).Lagos
P
2

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.

Pillow answered 8/4, 2016 at 23:3 Comment(3)
I tried it and msys still copies and doesn't create actual link- I'm on Win10Hallucinate
@sweelim Please clarify which git-cmd.exe you are using when this works for you.Transmutation
I just tried setting MSYS2=winsymlinks:native, MSYS=winsymlinks:native and CYGWIN=winsymlinks:native, and used the official Git windows distribution version 2.8.3.windows.1. ln -s doesn't symlink, it copies recursively.Transmutation
G
1

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
Goulden answered 8/1, 2019 at 8:12 Comment(0)
M
1

How to make a symlink in the Windows Command Prompt with the mklink command

Git 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:

  1. Press the Windows key --> search for "Command Prompt" --> right-click it and go to "Run as administrator".
  2. Create symlinks:
    # 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>

A note about committing symlinks to git, and sharing them between Linux and Windows

If 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://mcmap.net/q/49001/-how-to-get-the-path-of-the-batch-script-in-windows
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.

References:

  1. Where I relearned about Windows symbolic links: https://github.com/git-for-windows/git/wiki/symbolic-links

  2. Where I learned how to make a Windows batch script: https://www.howtogeek.com/263177/how-to-write-a-batch-script-on-windows/

  3. 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 the del command [for symlinks to files].

  4. 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?

  5. 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 using del 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.

Mike answered 27/6, 2023 at 1:41 Comment(0)
D
0

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:

~/bin/npp

#!/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.

Diatribe answered 16/3, 2022 at 16:18 Comment(0)
A
0

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'.

Abshire answered 18/11, 2022 at 16:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.