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 --- refs.c | 21 +++++++++++++++------ 1 files changed, 15 insertions(+), 6 deletions(-) diff --git a/refs.c b/refs.c index 6f486ae..b540067 100644 --- a/refs.c +++ b/refs.c @@ -1090,6 +1090,15 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt) return ret; } +/* + * People using contrib's git-new-workdir have .git/logs/refs -> + * /some/other/path/.git/logs/refs, and that may live on another device. + * + * IOW, to avoid cross device rename errors, the temporary renamed log must + * live into logs/refs. + */ +#define TMP_RENAMED_LOG "logs/refs/.tmp-renamed-log" + int rename_ref(const char *oldref, const char *newref, const char *logmsg) { static const char renamed_ref[] = "RENAMED-REF"; @@ -1123,8 +1132,8 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg) if (write_ref_sha1(lock, orig_sha1, logmsg)) return error("unable to save current sha1 in %s", renamed_ref); - if (log && rename(git_path("logs/%s", oldref), git_path("tmp-renamed-log"))) - return error("unable to move logfile logs/%s to tmp-renamed-log: %s", + if (log && rename(git_path("logs/%s", oldref), git_path(TMP_RENAMED_LOG))) + return error("unable to move logfile logs/%s to "TMP_RENAMED_LOG": %s", oldref, strerror(errno)); if (delete_ref(oldref, orig_sha1, REF_NODEREF)) { @@ -1150,7 +1159,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg) } retry: - if (log && rename(git_path("tmp-renamed-log"), git_path("logs/%s", newref))) { + if (log && rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", newref))) { if (errno==EISDIR || errno==ENOTDIR) { /* * rename(a, b) when b is an existing @@ -1163,7 +1172,7 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg) } goto retry; } else { - error("unable to move logfile tmp-renamed-log to logs/%s: %s", + error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s", newref, strerror(errno)); goto rollback; } @@ -1203,8 +1212,8 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg) error("unable to restore logfile %s from %s: %s", oldref, newref, strerror(errno)); if (!logmoved && log && - rename(git_path("tmp-renamed-log"), git_path("logs/%s", oldref))) - error("unable to restore logfile %s from tmp-renamed-log: %s", + rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", oldref))) + error("unable to restore logfile %s from "TMP_RENAMED_LOG": %s", oldref, strerror(errno)); return 1; -- 1.7.2.rc1.212.g850a.dirty -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html