git commit symlink as a regular file
Asked Answered
S

4

42

Suppose I have a file fname which is a symlink to a file from some other repository/project, say ../../proj2/fname.

Is there a way to add/commit fname as a regular file?

It seems that, by default, git gives the file mode 120000 and sets the path to the linked file as the blob content.

I know this because git ls-tree shows mode 120000 for the file, and git cat-file -p shows ../../proj2/fname as the blob's content.

Stuffed answered 12/12, 2011 at 5:34 Comment(1)
Hi can you please update the steps about symlinkinf a file using Git?Willi
F
23

Nope, Git knows it's a symlink. It'd be kind of dangerous for Git to pretend otherwise, since it would then end up writing to files outside the repo. Tracking it as a symlink is exactly the intended behavior.

Fungus answered 12/12, 2011 at 5:42 Comment(3)
I'd expect the write behavior in this case is to remove the symlink and replace it with a regular file.Stuffed
@hasenj: If that's what you want, then remove the symlink and replace it with a copy of the linked file. Your "expected" behavior involves Git modifying a file upon... add? commit? That's not a good thing either.Fungus
It's more likely to be useful to have symlinks pointing into your git repository. That would be the case if you had a repository for your configuration of a system and then you'd replaced the git managed config files with links to the files in the checked out repository.Cornwell
P
62

If you want the file to appear instead of the link, you should probably use the ln command to create a hard-link instead of a sym-link (ln -s).

Making a hard-link, you can make the same file to appear under two different directories, so changing it via any of the links will reflect changes via both links, and the file will live in both directories, so it will be tracked by git.

I hope Windows' mklink /j command in Bukov's answer does this, but I really don't know at all.

Piquant answered 6/3, 2013 at 5:42 Comment(6)
This answer seems more helpful to me than the one from @Jefromi.Advisedly
It should be noted that hard links with ln only works for files, but not directories.Crustaceous
Exactly what I searched for! Kudos to UNIX systems!Cassella
❗️Hard links are not guaranteed to work reliably on OSX — often when an app updates the source file the link is cut and you end up with two separate files with the ex-link now pointing to old content.Unwish
This doesn't work if the file is deleted and overwritten, a la the current xdg-mime bug. :'( A git precommit hook seems to be the most sane option from here.Workingwoman
sucky answer!!!!!!!Osy
F
23

Nope, Git knows it's a symlink. It'd be kind of dangerous for Git to pretend otherwise, since it would then end up writing to files outside the repo. Tracking it as a symlink is exactly the intended behavior.

Fungus answered 12/12, 2011 at 5:42 Comment(3)
I'd expect the write behavior in this case is to remove the symlink and replace it with a regular file.Stuffed
@hasenj: If that's what you want, then remove the symlink and replace it with a copy of the linked file. Your "expected" behavior involves Git modifying a file upon... add? commit? That's not a good thing either.Fungus
It's more likely to be useful to have symlinks pointing into your git repository. That would be the case if you had a repository for your configuration of a system and then you'd replaced the git managed config files with links to the files in the checked out repository.Cornwell
S
4

In Windows, you can do what you want with a Junction

For example, programs often keep a settings file somewhere on the system, but I'd like to version control it in my repository. I can't move the file, and I don't want to make duplicates or anything

If we put a Windows Shortcut in the repository directory though, he'll see it as a binary single file; not a directory pointing to all the actual files you want to include

What we need is the ability to put something like a Windows shortcut in the repository, but that git will treat as just another folder:

cd /location/of/my/repo/  
mklink /j "_linkTo_VimSettings" "C:\Program Files (x86)\Vim"
Stucco answered 6/3, 2013 at 5:30 Comment(1)
What happens if you merge a change from upstream? Will Git modify the source content? Like Jefromi mentions in another answer, that would be dangerous.Sutler
P
1

I had the same problem... Uploading a directory with symlinked files into a GIT repository, for public release.

The GIT repository is secondary store as I use gitlab as a web export, not as a repository for these files, as such I do not want to replace the symlinks on my local machine. Hardlinks are no good as these get broken, and it is not obvious when looking at the directory that they are links for files that are elsewhere.

My current solution was a script that temporarily replaces the symlinks, with hard links does the git commit/push, then restores the symlinks. Ideally the script would read, then restore the symlink info, but right now it just uses built in data on what the symlinks should be...

Of course this is only an 'example' of the script I use for git upload.

#!/bin/perl
#
# Git Upload...
#
# 1/ Replace all symbolic links with hard links
# 2/ upload files into a GIT repository
# 3/ Restore symbolic links again.
#
# Only the list of symbolic links given in the DATA section are effected.
#
use strict;

# the relative location of files being included in git repository
my $source_prefix="../real_project/";

# note start of data
my $data_start=tell(DATA);

# Link all files needed for upload
while ( <DATA> ) {
  s/#.*$//;         # ignore comments
  s/\s+$//;         # remove end of line spaces
  next if /^$/;     # skip blank lines
  my($file, $source) = split;

  unlink($file);
  link("$source_prefix$source", $file)
     or warn("failed to find: $source");
}

system("git add -A");
system("git commit -a -m 'Software Export Update'");
system("git push");

# rewind data section
seek DATA, $data_start, 0;

# unlink all files that have now been uploaded
while (<DATA>) {
  s/#.*$//;         # ignore comments
  s/\s+$//;         # remove end of line spaces
  next if /^$/;     # skip blank lines
  my($file, $source) = split;

  unlink($file);
  symlink("$source_prefix$source", $file);
  #  or warn("failed to find: $source");
}

__DATA__

### Example symbolic links (to replace and restore)
  script.pl.txt            scripts/script
  data_file.txt            lib/data_file.dat

# this file is not a symlink as a it slightly modified
# but listed to keep a record of its original source
# config_example.txt       extra/config
Pizzicato answered 21/2, 2020 at 0:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.