{"thread":{"id":"16965","subject":"[PATCH RESEND] Introduce core.keepHardLinks","startedAt":"2009-01-02T18:07:46Z","lastAt":"2009-01-02T18:07:46Z","messageCount":1,"participants":["Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"99176","messageId":"alpine.DEB.1.00.0901021218000.27818@racer","threadId":"16965","inReplyTo":null,"subject":"[PATCH RESEND] Introduce core.keepHardLinks","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-01-02T18:07:46Z","receivedAt":"2009-01-02T18:07:46Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nWhen a tracked file was hard linked, we used to break the hard link\nwhenever Git writes to that file.\n\nIn some situations, this behavior is less-than-desirable, especially\ngiven the fact that some popular editors do not do that, such as\n(in alphabetical order) emacs and vi.\n\nSo teach Git not to break hard links when the config variable\ncore.keepHardLinks is set to true.  For backwards compatibility, this\nvariable defaults to false.\n\n>From a safety viewpoint, nothing really changes, as to keep hard links,\nGit will now open the files it updates with O_TRUNC instead of deleting\nthem first and then opening them with O_EXCL.\n\nTo keep the implementation simple, mode changes will still break the\nhard links.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tOnly the commit message was modified since last time, to\n\tprovide a better justification for the patch.\n\n Documentation/config.txt |    4 +++\n cache.h                  |    1 +\n config.c                 |    4 +++\n entry.c                  |    7 ++++-\n environment.c            |    1 +\n t/t0056-hardlinked.sh    |   58 ++++++++++++++++++++++++++++++++++++++++++++++\n 6 files changed, 73 insertions(+), 2 deletions(-)\n create mode 100644 t/t0056-hardlinked.sh\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 7408bb2..086d8a4 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -209,6 +209,10 @@ core.symlinks::\n \tfile. Useful on filesystems like FAT that do not support\n \tsymbolic links. True by default.\n \n+core.keepHardLinks::\n+\tIf true, do not break hard links by deleting and recreating the\n+\tfiles.  Off by default.\n+\n core.gitProxy::\n \tA \"proxy command\" to execute (as 'command host port') instead\n \tof establishing direct connection to the remote server when\ndiff --git a/cache.h b/cache.h\nindex 231c06d..a9c5812 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -538,6 +538,7 @@ enum rebase_setup_type {\n \n extern enum branch_track git_branch_track;\n extern enum rebase_setup_type autorebase;\n+extern int keep_hard_links;\n \n #define GIT_REPO_VERSION 0\n extern int repository_format_version;\ndiff --git a/config.c b/config.c\nindex 790405a..8ff2b4b 100644\n--- a/config.c\n+++ b/config.c\n@@ -492,6 +492,10 @@ static int git_default_core_config(const char *var, const char *value)\n \n \tif (!strcmp(var, \"core.preloadindex\")) {\n \t\tcore_preload_index = git_config_bool(var, value);\n+\t}\n+\n+\tif (!strcmp(var, \"core.keephardlinks\")) {\n+\t\tkeep_hard_links = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n \ndiff --git a/entry.c b/entry.c\nindex aa2ee46..dfddf83 100644\n--- a/entry.c\n+++ b/entry.c\n@@ -82,7 +82,8 @@ static void remove_subtree(const char *path)\n static int create_file(const char *path, unsigned int mode)\n {\n \tmode = (mode & 0100) ? 0777 : 0666;\n-\treturn open(path, O_WRONLY | O_CREAT | O_EXCL, mode);\n+\treturn open(path, O_WRONLY | O_CREAT |\n+\t\t\t(keep_hard_links ? O_TRUNC : O_EXCL), mode);\n }\n \n static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned long *size)\n@@ -225,7 +226,9 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t\n \t\t\tif (!state->force)\n \t\t\t\treturn error(\"%s is a directory\", path);\n \t\t\tremove_subtree(path);\n-\t\t} else if (unlink(path))\n+\t\t} else if ((!keep_hard_links || !S_ISREG(st.st_mode) ||\n+\t\t\t\t\tst.st_mode != ce->ce_mode) &&\n+\t\t\t\tunlink(path))\n \t\t\treturn error(\"unable to unlink old '%s' (%s)\", path, strerror(errno));\n \t} else if (state->not_new)\n \t\treturn 0;\ndiff --git a/environment.c b/environment.c\nindex e278bce..fc91809 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -42,6 +42,7 @@ enum safe_crlf safe_crlf = SAFE_CRLF_WARN;\n unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;\n enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;\n enum rebase_setup_type autorebase = AUTOREBASE_NEVER;\n+int keep_hard_links = 0;\n \n /* Parallel index stat data preload? */\n int core_preload_index = 0;\ndiff --git a/t/t0056-hardlinked.sh b/t/t0056-hardlinked.sh\nnew file mode 100644\nindex 0000000..934c2bc\n--- /dev/null\n+++ b/t/t0056-hardlinked.sh\n@@ -0,0 +1,58 @@\n+#!/bin/sh\n+\n+test_description='read-tree and checkout respect hardlinked files'\n+\n+. ./test-lib.sh\n+\n+cat > file << EOF\n+1. Nf3 Nf6 2. c4 g6 3. Nc3 Bg7 4. d4 O-O 5. Bf4 d5 6. Qb3 dxc4 7. Qxc4 c6\n+8. e4 Nbd7 9. Rd1 Nb6 10. Qc5 Bg4 11. Bg5 Na4 12. Qa3 Nxc3 13. bxc3 Nxe4\n+14. Bxe7 Qb6 15. Bc4 Nxc3 16. Bc5 Rfe8+ 17. Kf1 Be6 18. Bxb6 Bxc4+ 19. Kg1 Ne2+\n+20. Kf1 Nxd4+ 21. Kg1 Ne2+ 22. Kf1 Nc3+ 23. Kg1 axb6 24. Qb4 Ra4 25. Qxb6 Nxd1\n+26. h3 Rxa2 27. Kh2 Nxf2 28. Re1 Rxe1 29. Qd8+ Bf8 30. Nxe1 Bd5 31. Nf3 Ne4\n+32. Qb8 b5 33. h4 h5 34. Ne5 Kg7 35. Kg1 Bc5+ 36. Kf1 Ng3+ 37. Ke1 Bb4+\n+38. Kd1 Bb3+ 39. Kc1 Ne2+ 40. Kb1 Nc3+\n+EOF\n+\n+ln file link || {\n+\tsay \"Could not hard link; skipping test\"\n+\ttest_done\n+\texit\n+}\n+\n+test_expect_success setup '\n+\n+\tgit config core.keepHardLinks true &&\n+\ttest_cmp file link &&\n+\tcp file old &&\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -m initial &&\n+\techo \"41. Kc1 Rc2#\" >> file &&\n+\tgit add file &&\n+\ttest_tick &&\n+\tgit commit -m 2nd &&\n+\ttest_cmp file link &&\n+\t! test_cmp file old\n+\n+'\n+\n+test_expect_success 'checking a file out does not break the hard link' '\n+\n+\tgit checkout HEAD^ -- file &&\n+\ttest_cmp file link &&\n+\ttest_cmp file old\n+\n+'\n+\n+test_expect_success 'read-tree -u -m does not break the hard link' '\n+\n+\tgit reset --hard &&\n+\ttest_cmp file link &&\n+\tgit read-tree -u -m HEAD^ &&\n+\ttest_cmp file link &&\n+\ttest_cmp file old\n+\n+'\n+\n+test_done\n-- \n1.6.1.rc3.224.g95ac9\n"}]}