{"thread":{"id":"14236","subject":"[PATCH/RFC] Emulating symlinks on Windows with file copies","startedAt":"2008-06-30T23:54:13Z","lastAt":"2008-06-30T23:54:13Z","messageCount":1,"participants":["Patrick Higgins"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"81811","messageId":"1214870053-11537-1-git-send-email-patrick.higgins@cexp.com","threadId":"14236","inReplyTo":null,"subject":"[PATCH/RFC] Emulating symlinks on Windows with file copies","fromName":"Patrick Higgins","fromEmail":"patrick.higgins@cexp.com","sentAt":"2008-06-30T23:54:13Z","receivedAt":"2008-06-30T23:54:13Z","isPatch":true,"sender":{"key":"patrick.higgins@cexp.com","avatar":null},"body":"Signed-off-by: Patrick Higgins <patrick.higgins@cexp.com>\n---\n\nThis is a very basic script to test out the idea of using copies to emulate\nsymlinks on Windows. I don't track any changes in the index yet which seems\nnecessary as a next step.\n\nSymlinks get deleted and replaced with copies. Existing unchanged copies\nare ignored. Copies that have been modified get merged into the target of\nthe link.\n\nThis might have some messy cases that I haven't considered yet, but it seems\nlike a fairly straightforward way to make a repository containing file\nsymlinks useful on Windows.\n\nI have not yet started on directories. This technique would probably get\nmessy with directories. Perhaps junction (reparse) points would be better\nfor directories?\n\n git-sync-symlinks.perl |   44 ++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 44 insertions(+), 0 deletions(-)\n create mode 100755 git-sync-symlinks.perl\n\ndiff --git a/git-sync-symlinks.perl b/git-sync-symlinks.perl\nnew file mode 100755\nindex 0000000..223ae83\n--- /dev/null\n+++ b/git-sync-symlinks.perl\n@@ -0,0 +1,44 @@\n+#!/usr/bin/perl -w\n+\n+use strict;\n+use File::Copy;\n+use Fcntl ':mode';\n+\n+my $filepipe = open(FILELIST, '-|', 'git', 'ls-tree', '-z', '-r', 'HEAD')\n+    or die(\"Cannot call git ls-tree : $!\");\n+\n+local $/ = \"\\0\";\n+\n+while ( <FILELIST> ) {\n+    chomp;\n+    if (/^120000 blob ([0-9a-f]{40})\\t(.*)$/) {\n+        my ($id, $path) = ($1, $2);\n+        my $target = `git cat-file blob $id`;\n+        chomp($target);\n+        my @path_stat = lstat($path);\n+        my @target_stat = stat($target);\n+        \n+        if (S_ISLNK($path_stat[2])) {\n+            printf(\"Copying '%s' to '%s'\\n\", $target, $path);\n+            unlink($path);\n+            copy($target, $path);\n+        }\n+        elsif ($path_stat[7] != $target_stat[7] ||\n+               `git hash-object $path` ne `git hash-object $target`)\n+        {\n+            printf(\"Merging '%s' to '%s'\\n\", $path, $target);\n+            my @target_parts = split(/\\s+/, `git ls-tree HEAD $target`);\n+            my $target_id = $target_parts[2];\n+            my $original = $target . \".BASE\";\n+            open(ORIGINAL, '-|', 'git', 'cat-file', 'blob', $target_id);\n+            open(ORIGINAL_OUT, '>', $original);\n+            while ( <ORIGINAL> ) {\n+                print ORIGINAL_OUT $_;\n+            }\n+            close ORIGINAL_OUT;\n+            close ORIGINAL;\n+            system('git', 'merge-file', $target, $original, $path);\n+        }\n+    }\n+}\n+close FILELIST;\n-- \n1.5.6.dirty\n"}]}