@matheusbsbs / matheus-bagliano / commits / 765c22588d
Fork 0

fix git branch -m in presence of cross devices

When you have for example a bare repository stored on NFS, and that you create new workdirs locally (using contrib's git-new-workdir), logs/refs is a symlink to a different device. Hence when the reflogs are renamed, all must happen below logs/refs or one gets cross device rename errors like: git branch -m foo error: unable to move logfile logs/refs/heads/master to tmp-renamed-log: Invalid cross-device link fatal: Branch rename failed The fix is hence to use logs/refs/.tmp-renamed-log as a temporary log name, instead of just tmp-renamed-log. Signed-off-by: Pierre Habouzit <madcoder@debian.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Pierre Habouzit committed Jul 7, 2010 at 09:47 UTC 765c22588d9e410bf0100c4991b1945134da6959
1 file changed +15 -6
refs.c
+15 -6
@@ -1086,6 +1086,15 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
1086 return ret;
1087 }
1088
1089 +/*
1090 + * People using contrib's git-new-workdir have .git/logs/refs ->
1091 + * /some/other/path/.git/logs/refs, and that may live on another device.
1092 + *
1093 + * IOW, to avoid cross device rename errors, the temporary renamed log must
1094 + * live into logs/refs.
1095 + */
1096 +#define TMP_RENAMED_LOG "logs/refs/.tmp-renamed-log"
1097 +
1098 int rename_ref(const char *oldref, const char *newref, const char *logmsg)
1099 {
1100 static const char renamed_ref[] = "RENAMED-REF";
@@ -1119,8 +1128,8 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
1128 if (write_ref_sha1(lock, orig_sha1, logmsg))
1129 return error("unable to save current sha1 in %s", renamed_ref);
1130
1122 - if (log && rename(git_path("logs/%s", oldref), git_path("tmp-renamed-log")))
1123 - return error("unable to move logfile logs/%s to tmp-renamed-log: %s",
1131 + if (log && rename(git_path("logs/%s", oldref), git_path(TMP_RENAMED_LOG)))
1132 + return error("unable to move logfile logs/%s to "TMP_RENAMED_LOG": %s",
1133 oldref, strerror(errno));
1134
1135 if (delete_ref(oldref, orig_sha1, REF_NODEREF)) {
@@ -1146,7 +1155,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
1155 }
1156
1157 retry:
1149 - if (log && rename(git_path("tmp-renamed-log"), git_path("logs/%s", newref))) {
1158 + if (log && rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", newref))) {
1159 if (errno==EISDIR || errno==ENOTDIR) {
1160 /*
1161 * rename(a, b) when b is an existing
@@ -1159,7 +1168,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
1168 }
1169 goto retry;
1170 } else {
1162 - error("unable to move logfile tmp-renamed-log to logs/%s: %s",
1171 + error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s",
1172 newref, strerror(errno));
1173 goto rollback;
1174 }
@@ -1199,8 +1208,8 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
1208 error("unable to restore logfile %s from %s: %s",
1209 oldref, newref, strerror(errno));
1210 if (!logmoved && log &&
1202 - rename(git_path("tmp-renamed-log"), git_path("logs/%s", oldref)))
1203 - error("unable to restore logfile %s from tmp-renamed-log: %s",
1211 + rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", oldref)))
1212 + error("unable to restore logfile %s from "TMP_RENAMED_LOG": %s",
1213 oldref, strerror(errno));
1214
1215 return 1;