{"thread":{"id":"14793","subject":"[PATCH] git mv: try harder to keep index entries intact","startedAt":"2008-08-01T16:49:57Z","lastAt":"2008-08-01T16:49:57Z","messageCount":1,"participants":["Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"85907","messageId":"alpine.DEB.1.00.0808011847560.9611@pacific.mpi-cbg.de.mpi-cbg.de","threadId":"14793","inReplyTo":null,"subject":"[PATCH] git mv: try harder to keep index entries intact","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-08-01T16:49:57Z","receivedAt":"2008-08-01T16:49:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nSome filesystems change the ctime during a rename(), for technical\nreasons.  Since this is the only change, the contents need not be\nrehashed.  So just update the ctime after renaming the entry.\n\nThis change requires rename_index_entry_at() to return the new\nposition.\n\nAs git-mv assumes that it runs in a non-bare repository, and is\nmarked as such in the cmd_struct in git.c, the changes do not have\nto be guarded against running in a bare repository.\n\nTo test this properly, you need to run t7001 with the environment\nvariable TEST_CTIME_WITH_SLEEP set non-empty, since there is no way\nto manipulate the ctime directly; we have to sleep.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n builtin-mv.c  |   15 +++++++++++++--\n cache.h       |    2 +-\n read-cache.c  |    4 ++--\n t/t7001-mv.sh |   10 ++++++++--\n 4 files changed, 24 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin-mv.c b/builtin-mv.c\nindex 4f65b5a..166a019 100644\n--- a/builtin-mv.c\n+++ b/builtin-mv.c\n@@ -214,8 +214,19 @@ int cmd_mv(int argc, const char **argv, const char *prefix)\n \n \t\tpos = cache_name_pos(src, strlen(src));\n \t\tassert(pos >= 0);\n-\t\tif (!show_only)\n-\t\t\trename_cache_entry_at(pos, dst);\n+\t\tif (!show_only) {\n+\t\t\tstruct stat st;\n+\t\t\tpos = rename_cache_entry_at(pos, dst);\n+\n+\t\t\t/*\n+\t\t\t * Renaming can update the ctime.  Do not force\n+\t\t\t * a complete rehash just because of that.\n+\t\t\t */\n+\t\t\tif (!lstat(dst, &st))\n+\t\t\t\tactive_cache[pos]->ce_ctime = st.st_ctime;\n+\t\t\telse if (!ignore_errors)\n+\t\t\t\tdie (\"Could not stat '%s'\", dst);\n+\t\t}\n \t}\n \n \tif (active_cache_changed) {\ndiff --git a/cache.h b/cache.h\nindex 8155ab8..4b6876b 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -371,7 +371,7 @@ extern int index_name_pos(const struct index_state *, const char *name, int name\n #define ADD_CACHE_JUST_APPEND 8\t\t/* Append only; tree.c::read_tree() */\n extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);\n extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);\n-extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);\n+extern int rename_index_entry_at(struct index_state *, int pos, const char *new_name);\n extern int remove_index_entry_at(struct index_state *, int pos);\n extern int remove_file_from_index(struct index_state *, const char *path);\n #define ADD_CACHE_VERBOSE 1\ndiff --git a/read-cache.c b/read-cache.c\nindex c5aa5bc..4454686 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -43,7 +43,7 @@ static void replace_index_entry(struct index_state *istate, int nr, struct cache\n \tistate->cache_changed = 1;\n }\n \n-void rename_index_entry_at(struct index_state *istate, int nr, const char *new_name)\n+int rename_index_entry_at(struct index_state *istate, int nr, const char *new_name)\n {\n \tstruct cache_entry *old = istate->cache[nr], *new;\n \tint namelen = strlen(new_name);\n@@ -56,7 +56,7 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n\n \n \tcache_tree_invalidate_path(istate->cache_tree, old->name);\n \tremove_index_entry_at(istate, nr);\n-\tadd_index_entry(istate, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);\n+\treturn add_index_entry(istate, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);\n }\n \n /*\ndiff --git a/t/t7001-mv.sh b/t/t7001-mv.sh\nindex 910a28c..5f6cee5 100755\n--- a/t/t7001-mv.sh\n+++ b/t/t7001-mv.sh\n@@ -180,12 +180,15 @@ test_expect_success 'git mv should overwrite symlink to a file' '\n \techo 1 >moved &&\n \tln -s moved symlink &&\n \tgit add moved symlink &&\n+\tif test ! -z \"$TEST_CTIME_WITH_SLEEP\"\n+\tthen\n+\t\tsleep 1\n+\tfi &&\n \ttest_must_fail git mv moved symlink &&\n \tgit mv -f moved symlink &&\n \t! test -e moved &&\n \ttest -f symlink &&\n \ttest \"$(cat symlink)\" = 1 &&\n-\tgit update-index --refresh &&\n \tgit diff-files --quiet\n \n '\n@@ -199,11 +202,14 @@ test_expect_success 'git mv should overwrite file with a symlink' '\n \techo 1 >moved &&\n \tln -s moved symlink &&\n \tgit add moved symlink &&\n+\tif test ! -z \"$TEST_CTIME_WITH_SLEEP\"\n+\tthen\n+\t\tsleep 1\n+\tfi &&\n \ttest_must_fail git mv symlink moved &&\n \tgit mv -f symlink moved &&\n \t! test -e symlink &&\n \ttest -h moved &&\n-\tgit update-index --refresh &&\n \tgit diff-files --quiet\n \n '\n-- \n1.6.0.rc1.55.g69db8\n"}]}