107

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?

Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
iconoclast
  • 21,213
  • 15
  • 102
  • 138

12 Answers12

84

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
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Camilo Martin
  • 37,236
  • 20
  • 111
  • 154
  • 6
    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. – Wolph Jan 03 '15 at 16:36
  • 3
    @Wolph 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`. – Camilo Martin Jan 04 '15 at 17:29
  • 1
    Unfortunately, this gives me `You do not have sufficient privilege to perform this operation.` – Jacob Jun 19 '15 at 17:11
  • @Jacob Are you trying that on a regular folder on your profile? Or some system directory? Also, which version of Windows? – Camilo Martin Jun 24 '15 at 16:27
  • @Jacob That's wierd because that's what I'm using. Do you use MSYSGIT or some other Bash port? – Camilo Martin Jun 27 '15 at 04:56
  • Yes, it's the msysgit port. Maybe should add that I don't disable UAC. – Jacob Jul 04 '15 at 18:25
  • @Jacob That could be it! I disable UAC and run under administrator. – Camilo Martin Jul 05 '15 at 19:49
  • I used to think ln's parameters were backwards too, until I realized they were in the same order as cp's. – gknauth Nov 05 '15 at 18:33
  • I tried your more comprehensive function, and it worked like a charm. THANK YOU so much! – gknauth Nov 05 '15 at 19:35
  • 2
    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.) – Steven Lu Jul 14 '16 at 02:23
  • @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 :) – Camilo Martin Jul 15 '16 at 07:50
  • `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! – Kyle Strand Mar 24 '17 at 18:51
  • 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})"` – Kyle Strand Mar 24 '17 at 20:22
  • @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`? – Camilo Martin Mar 26 '17 at 07:59
  • 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`. – Kyle Strand Mar 26 '17 at 17:41
  • 3
    I'm from the future but I forgot to buy Windows 9 on the way. – Greg Oct 31 '17 at 13:06
  • yes, ln's parameters are backward, even though it mimics cp's parameter order. – Joseph Cheek Jan 03 '18 at 15:12
  • I don't think this has been mentioned, but it isn't best to use `rm` on a symlink. Instead `unlink` should be used. https://stackoverflow.com/questions/210120/remove-a-symlink-to-a-directory – geedew Mar 14 '18 at 11:52
  • 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. Thanks – Rafareino Apr 01 '18 at 10:45
  • 9
    I'm in 2019, still no Windows 9 :) – Maxim Mazurok Jan 07 '19 at 10:28
  • 2023 here, still no Windows 9 :) – alo Malbarez Apr 08 '23 at 01:26
  • @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 permitted_ – jifb Aug 29 '23 at 12:19
82

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
CervEd
  • 3,306
  • 28
  • 25
  • 2
    it's the real answer~ – fatfatson Mar 26 '19 at 10:21
  • 10
    you don't need to run as administrator if you turn on windows developer mode (requires Windows 10 version 1703 or later) https://learn.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development – Sebastian Jan 14 '20 at 06:57
  • `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! – Josiah Yoder Jun 16 '20 at 16:23
  • I've developed an example with samples and images here: https://stackoverflow.com/a/63325536/1315009 - Also back-linked from there to here. – Xavi Montero Aug 09 '20 at 11:15
  • real deal answer. – Amjo May 11 '21 at 20:07
30

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.

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Tony O'Hagan
  • 21,638
  • 3
  • 67
  • 78
  • 2
    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. – Camilo Martin Aug 19 '14 at 23:22
  • Oh and good luck with the `/d` argument. Buy a netbook with no disc drive and unplug external HDDs to run that :P – Camilo Martin Aug 19 '14 at 23:29
  • 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. – Tony O'Hagan Aug 19 '14 at 23:57
  • 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 :) http://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. – Camilo Martin Aug 20 '14 at 00:45
  • 2
    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. – Justin Dunlap Aug 28 '14 at 19:48
  • 2
    @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. – Tony O'Hagan Aug 29 '14 at 06:16
  • 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. – Justin Dunlap Aug 29 '14 at 22:05
  • 7
    @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 m`mklink` 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.txt` – Grant Humphries Sep 25 '15 at 19:03
  • 1
    @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. – Camilo Martin Oct 08 '15 at 02:08
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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Austin Hastings
  • 617
  • 4
  • 13
  • 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? – iconoclast Oct 25 '13 at 18:11
  • 3
    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. – Austin Hastings Nov 01 '13 at 21:44
  • 1
    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)? – trysis Nov 19 '13 at 02:42
  • 2
    Yes, it seems like a glaring omission, and not one they can blame on Microsoft. – iconoclast Feb 14 '14 at 02:10
  • 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. :) – Austin Hastings Feb 14 '14 at 14:27
  • 1
    @AustinHastings, Linux doesn't allow hard links on directories either, so that part is still in line with `ln`. – trysis May 16 '14 at 23:09
  • 1
    @AustinHastings No, FAT is not *really* supported. You can't install currently-supported versions of Windows on FAT anymore. – Camilo Martin Aug 19 '14 at 23:10
  • 1
    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. – Ross Ridge Aug 21 '14 at 22:26
  • 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."* – Peter Mortensen May 07 '22 at 22:04
13

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.

CervEd
  • 3,306
  • 28
  • 25
  • 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. – xpt Jul 14 '23 at 20:14
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
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Oliver Zendel
  • 2,695
  • 34
  • 29
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>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Xiaolei Cheng
  • 51
  • 1
  • 1
  • 1
    Any idea why? export should set the env variable for all children – DownloadPizza Sep 15 '21 at 11:22
  • What is the reason? Why is it necessary? How does it work? Please respond by [editing (changing) your answer](https://stackoverflow.com/posts/64844782/edit), not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Jun 07 '22 at 15:30
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
swee lim
  • 41
  • 3
  • 1
    I tried it and msys still copies and doesn't create actual link- I'm on Win10 – 0fnt Jun 13 '16 at 11:40
  • @sweelim Please clarify which `git-cmd.exe` you are using when this works for you. – clacke Aug 30 '16 at 09:28
  • 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. – clacke Aug 30 '16 at 10:47
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
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Christian Fosli
  • 1,646
  • 9
  • 14
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
lgwest
  • 1,347
  • 5
  • 16
  • 26
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'.

0

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, 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://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.

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.

Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265