fix git branch -m in presence of cross devices
authorPierre Habouzit <madcoder@debian.org>
Wed, 7 Jul 2010 07:47:20 +0000 (09:47 +0200)
committerJunio C Hamano <gitster@pobox.com>
Wed, 7 Jul 2010 16:17:00 +0000 (09:17 -0700)
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>
refs.c

diff --git a/refs.c b/refs.c
index d3db15a76cc46f6f6a31d4448816c09e6c48e543..d6307ae29c14d73654039bf8a92483302f4e3e15 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1086,6 +1086,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";
@@ -1119,8 +1128,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)) {
@@ -1146,7 +1155,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
@@ -1159,7 +1168,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;
                }
@@ -1199,8 +1208,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;