84

What is the correct way to handle symlinks in git?

I have the following structure:

Vendors
  Module A
  Module B
  Module C
App
 Code
   Modules
     Core Module 1
     Core Module 2
     Module A (symlinked to vendors)
     Module B (symlinked to vendors)
     Module C (symlinked to vendors)

There is a a main App directory which contains all of the core code in the application. Additionally there is a vendor directory that contains modules which get symlinked into the main app directory and therefore integrated.

Importantly, both the vendor directory and the main app directory are both versioned in the same repository.

Therefore, should i let git keep storing the symlinks, or find a way to have it ignore symlinks?

Marty Wallace
  • 34,046
  • 53
  • 137
  • 200
  • hm I made it relative and it does not work on github :( can you please help me? https://github.com/lingohub/Example-Resource-Files/blob/master/AngularJS/example.en.json – Betty St Apr 12 '16 at 06:50
  • Your request is entirely reasonable, and you've uncovered an encapsulation violation on GitHub's part. Symlinks are a detail of the storage layer — where do I want to keep various folders physically on different volumes? GitHub should be concerned only with the filesystem navigation layer: if something looks and acts like a subdirectory, then treat it like one (duck typing), without over-policing the user by peeking lower down to see whether it's a symlink to another volume or not. – Canuck Aug 31 '20 at 17:06

3 Answers3

153

Git can handle symlinks just fine as long as the operating system used by all developers supports them. Since you depend on having these symlinks present I will assume that your development environments all support symlinks.

To decide if something should be included in your git repository or not (symlink or otherwise) consider the following:

  • Is it a file generated by some tool or other process in the repository? If so, it's best to ignore it and let each user generate the file, so that they will always have the latest version.
  • Is the file specific to a particular user's development environment, or one that is used in all environments? If it's a quirk of a particular user's environment, like a configuration to ignore Emacs backup files, it doesn't belong in the repo. If it's something all developers will need, and/or something that's needed to build the application for production, it should go in the repository.

In your case it seems like the symlinks are not generated and they are needed in all environments, so putting them in the repository should be fine.

However, when creating them be sure to create them as relative symlinks rather than absolute symlinks, so that they'll work regardless of where the repository is cloned. The easiest way to do this is to change directory into the Modules directory and create the symlink from there:

cd App/Code/Modules
ln -s "../../../Vendors/Module A" "Module A"
Martin Atkins
  • 62,420
  • 8
  • 120
  • 138
  • hm I made it relative and it does not work on github :( can you please help me? https://github.com/lingohub/Example-Resource-Files/blob/master/AngularJS/example.en.json – Betty St Apr 08 '16 at 08:34
  • Does it mean that if I make any changes in Vendors/Module A, and push it to Git, then Git will automatically update the contents of App/Code/Modules/Module A? – HasnainMamdani Feb 17 '17 at 10:22
  • 9
    Use the `-r` option to create relative symlinks `ln -r -s /path/to/target` – Fred Schoen May 10 '17 at 07:44
  • 4
    Make sure your git config doesn't have `symlinks = false`. This can happen if you copied a repository from Windows or with TortiseGit. – phyatt Dec 12 '17 at 17:06
14

Git stores the symlink, just like any other file in its version control - except for a symlink it would only store the information about the path it is symlinking to, and the file type as symlink instead of a regular file.

If the symlink points to a directory, git does not store the contents under the symlinked directory.

So there should be no harm in storing symlinks versioned under git for your case.

One more thing you need to be aware of with symlinks is that, git would only recreate the symlinks on a fresh clone, not the file or directory it is pointing at. There are chances that the symlinked path would be non-existent (say when using absolute paths).

Community
  • 1
  • 1
Tuxdude
  • 47,485
  • 15
  • 109
  • 110
  • 1
    Is there a way to add files that are in a symlink directory? – kraftydevil Apr 18 '16 at 22:24
  • 4
    @kraftydevil - git will let you add files under the symlink directory only as long as the target directory of the symlink itself is under git version control. Otherwise git will not (and should not) version the contents of the symlink directory. If you're trying to version files under the symlink directory outside of the main git repository directory, then you're doing something wrong. You might want to check out `git submodules` (or better `git subtrees`). – Tuxdude Apr 19 '16 at 06:31
  • I'm putting my Jenkins instance under source control. I'd like to keep the lastSuccessful build contents for each job. I'll take a look at it a little later for more explanation. – kraftydevil Apr 19 '16 at 06:43
  • @Tuxdude See http://stackoverflow.com/questions/15465436/git-how-to-handle-symlinks/37426014#answer-37426014 (couldn't post it as a comment, because of too many characters). – ioCron May 25 '16 at 00:32
1

@Tuxdude Can't agree with you at all with "...then you're doing something wrong". As example, if you need to place a media folder onto a different drive on the webserver or even a NFS then you have to put it outside of the version control. So the contents inside the symlinked media folder won't be accessible through versioning as you explained. But that's a scenario where you have to do it like that. And it's really a pain in the b... My scenario is even more complex (i won't go into detail), what i am actually looking for is to add the subfolders of the symlinked folder into versioning but not its contents, but i need to have a option where i can ignore any changes of the subfolder type itself in git. As example, the basic structure:

  • app/media/bla
  • app/media/blubb

I need those folders in the git versioning, without its contents.

On the webserver (same versioning) those folders look like this (symlinks):

  • app/media/bla => somewhere entirely else
  • app/media/blubb => somewhere entirely else again

The Dev's should have on their local environment just the original versioned structure as explained in the first step (no symlinks). But the webserver has symlinks to different NFS Systems.

If anyone has an idea how to solve this i would really appreciate it, because i haven't found any solution to it yet.

The only way i am doing it now is to have a builder that creates the correct / different structure for local environments and the servers and the media subfolders are currently ignored entirely by gitignore. But that can be sometimes tricky / hard to maintain.

ioCron
  • 873
  • 8
  • 9
  • 1
    This is a common issue, and as I said before, it is not related to version control. One solution is to have an initialization script to set up the symlinks based on the target environment. The initialization script should be run to bootstrap your setup right after a `git clone`. Add the initialization script into git's version control. BTW `git` will not version empty directories, git only versions files and knows the path. This will be used to recreate the directory structure when you run any of the `git` commands. – Tuxdude May 25 '16 at 00:37
  • Thank you for your explanation, thats actually nearly exactly as I do it on my current setups. I just thought there might have been a more "comfortable" way. If git would have some sort of flag of differentiation between normal files and symlinks in the gitignore rules, etc., then some things would be at least a bit easier. That's why i think there is a relation to "version control", it might be implemented as a "new feature" / through a feature request, there are acutally also some other things i am missing in git as well when it comes to more complex scenarios. – ioCron May 25 '16 at 01:07
  • 1
    I can actually say why git treats symlinks this way. For git, symlink is equivalent to a text file whose contents represent the target of the symlink. This target is added as part of git's history when you add the symlink into git. – Tuxdude May 28 '16 at 00:20