diff --git c/git-1.5.5.6-mv-segfault.patch w/git-1.5.5.6-mv-segfault.patch
new file mode 100644
index 0000000..38990be
--- /dev/null
+++ w/git-1.5.5.6-mv-segfault.patch
@@ -0,0 +1,304 @@
+From 99472901bc962299bc545f84bb18dd6148bb0ac0 Mon Sep 17 00:00:00 2001
+From: Petr Baudis <pasky@suse.cz>
+Date: Wed, 16 Jul 2008 21:11:08 +0200
+Subject: [PATCH 1/2] git-mv: Remove dead code branch
+
+The path list builder had a branch for the case the source is not in index, but
+this can happen only if the source was a directory. However, in that case we
+have already expanded the list to the directory contents and set mode
+to WORKING_DIRECTORY, which is tested earlier.
+
+The patch removes the superfluous branch and adds an assert() instead. git-mv
+testsuite still passes.
+
+Signed-off-by: Petr Baudis <pasky@suse.cz>
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
+Backported-by: Todd Zullinger <tmz@pobox.com>
+---
+ builtin-mv.c |   16 +++++++---------
+ 1 files changed, 7 insertions(+), 9 deletions(-)
+
+diff --git a/builtin-mv.c b/builtin-mv.c
+index 94f6dd2..935aabf 100644
+--- a/builtin-mv.c
++++ b/builtin-mv.c
+@@ -227,15 +227,13 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
+ 		if (mode == WORKING_DIRECTORY)
+ 			continue;
+ 
+-		if (cache_name_pos(src, strlen(src)) >= 0) {
+-			path_list_insert(src, &deleted);
+-
+-			/* destination can be a directory with 1 file inside */
+-			if (path_list_has_path(&overwritten, dst))
+-				path_list_insert(dst, &changed);
+-			else
+-				path_list_insert(dst, &added);
+-		} else
++		assert(cache_name_pos(src, strlen(src)) >= 0);
++
++		path_list_insert(src, &deleted);
++		/* destination can be a directory with 1 file inside */
++		if (path_list_has_path(&overwritten, dst))
++			path_list_insert(dst, &changed);
++		else
+ 			path_list_insert(dst, &added);
+ 	}
+ 
+-- 
+1.6.3.3
+
+From 046e8538147e4c26e7a799c49c5177b491a25c6c Mon Sep 17 00:00:00 2001
+From: Petr Baudis <pasky@suse.cz>
+Date: Mon, 21 Jul 2008 02:25:56 +0200
+Subject: [PATCH 2/2] git-mv: Keep moved index entries inact
+
+The rewrite of git-mv from a shell script to a builtin was perhaps
+a little too straightforward: the git add and git rm queues were
+emulated directly, which resulted in a rather complicated code and
+caused an inconsistent behaviour when moving dirty index entries;
+git mv would update the entry based on working tree state,
+except in case of overwrites, where the new entry would still have
+sha1 of the old file.
+
+This patch introduces rename_index_entry_at() into the index toolkit,
+which will rename an entry while removing any entries the new entry
+might render duplicate. This is then used in git mv instead
+of all the file queues, resulting in a major simplification
+of the code and an inevitable change in git mv -n output format.
+
+Also the code used to refuse renaming overwriting symlink with a regular
+file and vice versa; there is no need for that.
+
+A few new tests have been added to the testsuite to reflect this change.
+
+Signed-off-by: Petr Baudis <pasky@suse.cz>
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
+Backported-by: Todd Zullinger <tmz@pobox.com>
+---
+ builtin-mv.c  |   63 +++++++++-----------------------------------------------
+ cache.h       |    2 +
+ read-cache.c  |   16 ++++++++++++++
+ t/t7001-mv.sh |   52 +++++++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 80 insertions(+), 53 deletions(-)
+
+diff --git a/builtin-mv.c b/builtin-mv.c
+index 935aabf..20e20d6 100644
+--- a/builtin-mv.c
++++ b/builtin-mv.c
+@@ -36,17 +36,6 @@ static const char **copy_pathspec(const char *prefix, const char **pathspec,
+ 	return get_pathspec(prefix, result);
+ }
+ 
+-static void show_list(const char *label, struct path_list *list)
+-{
+-	if (list->nr > 0) {
+-		int i;
+-		printf("%s", label);
+-		for (i = 0; i < list->nr; i++)
+-			printf("%s%s", i > 0 ? ", " : "", list->items[i].path);
+-		putchar('\n');
+-	}
+-}
+-
+ static const char *add_slash(const char *path)
+ {
+ 	int len = strlen(path);
+@@ -75,11 +64,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
+ 	const char **source, **destination, **dest_path;
+ 	enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
+ 	struct stat st;
+-	struct path_list overwritten = {NULL, 0, 0, 0};
+ 	struct path_list src_for_dst = {NULL, 0, 0, 0};
+-	struct path_list added = {NULL, 0, 0, 0};
+-	struct path_list deleted = {NULL, 0, 0, 0};
+-	struct path_list changed = {NULL, 0, 0, 0};
+ 
+ 	git_config(git_default_config);
+ 
+@@ -184,12 +169,11 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
+ 				 * only files can overwrite each other:
+ 				 * check both source and destination
+ 				 */
+-				if (S_ISREG(st.st_mode)) {
++				if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
+ 					fprintf(stderr, "Warning: %s;"
+ 							" will overwrite!\n",
+ 							bad);
+ 					bad = NULL;
+-					path_list_insert(dst, &overwritten);
+ 				} else
+ 					bad = "Cannot overwrite";
+ 			}
+@@ -218,6 +202,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
+ 	for (i = 0; i < argc; i++) {
+ 		const char *src = source[i], *dst = destination[i];
+ 		enum update_mode mode = modes[i];
++		int pos;
+ 		if (show_only || verbose)
+ 			printf("Renaming %s to %s\n", src, dst);
+ 		if (!show_only && mode != INDEX &&
+@@ -227,44 +212,16 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
+ 		if (mode == WORKING_DIRECTORY)
+ 			continue;
+ 
+-		assert(cache_name_pos(src, strlen(src)) >= 0);
+-
+-		path_list_insert(src, &deleted);
+-		/* destination can be a directory with 1 file inside */
+-		if (path_list_has_path(&overwritten, dst))
+-			path_list_insert(dst, &changed);
+-		else
+-			path_list_insert(dst, &added);
++		pos = cache_name_pos(src, strlen(src));
++		assert(pos >= 0);
++		if (!show_only)
++			rename_cache_entry_at(pos, dst);
+ 	}
+ 
+-	if (show_only) {
+-		show_list("Changed  : ", &changed);
+-		show_list("Adding   : ", &added);
+-		show_list("Deleting : ", &deleted);
+-	} else {
+-		for (i = 0; i < changed.nr; i++) {
+-			const char *path = changed.items[i].path;
+-			int j = cache_name_pos(path, strlen(path));
+-			struct cache_entry *ce = active_cache[j];
+-
+-			if (j < 0)
+-				die ("Huh? Cache entry for %s unknown?", path);
+-			refresh_cache_entry(ce, 0);
+-		}
+-
+-		for (i = 0; i < added.nr; i++) {
+-			const char *path = added.items[i].path;
+-			add_file_to_cache(path, verbose);
+-		}
+-
+-		for (i = 0; i < deleted.nr; i++)
+-			remove_file_from_cache(deleted.items[i].path);
+-
+-		if (active_cache_changed) {
+-			if (write_cache(newfd, active_cache, active_nr) ||
+-			    commit_locked_index(&lock_file))
+-				die("Unable to write new index file");
+-		}
++	if (active_cache_changed) {
++		if (write_cache(newfd, active_cache, active_nr) ||
++		    commit_locked_index(&lock_file))
++			die("Unable to write new index file");
+ 	}
+ 
+ 	return 0;
+diff --git a/cache.h b/cache.h
+index 2a1e7ec..e054883 100644
+--- a/cache.h
++++ b/cache.h
+@@ -255,6 +255,7 @@ extern struct index_state the_index;
+ #define unmerged_cache() unmerged_index(&the_index)
+ #define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
+ #define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option))
++#define rename_cache_entry_at(pos, new_name) rename_index_entry_at(&the_index, (pos), (new_name))
+ #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
+ #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
+ #define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose))
+@@ -358,6 +359,7 @@ extern int index_name_pos(const struct index_state *, const char *name, int name
+ #define ADD_CACHE_JUST_APPEND 8		/* Append only; tree.c::read_tree() */
+ extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
+ extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
++extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
+ extern int remove_index_entry_at(struct index_state *, int pos);
+ extern int remove_file_from_index(struct index_state *, const char *path);
+ extern int add_file_to_index(struct index_state *, const char *path, int verbose);
+diff --git a/read-cache.c b/read-cache.c
+index a92b25b..0cc0fe8 100644
+--- a/read-cache.c
++++ b/read-cache.c
+@@ -97,6 +97,22 @@ int index_name_exists(struct index_state *istate, const char *name, int namelen)
+ 	return 0;
+ }
+ 
++void rename_index_entry_at(struct index_state *istate, int nr, const char *new_name)
++{
++	struct cache_entry *old = istate->cache[nr], *new;
++	int namelen = strlen(new_name);
++
++	new = xmalloc(cache_entry_size(namelen));
++	copy_cache_entry(new, old);
++	new->ce_flags &= ~(CE_STATE_MASK | CE_NAMEMASK);
++	new->ce_flags |= (namelen >= CE_NAMEMASK ? CE_NAMEMASK : namelen);
++	memcpy(new->name, new_name, namelen + 1);
++
++	cache_tree_invalidate_path(istate->cache_tree, old->name);
++	remove_index_entry_at(istate, nr);
++	add_index_entry(istate, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
++}
++
+ /*
+  * This only updates the "non-critical" parts of the directory
+  * cache, ie the parts that aren't tracked by GIT, and only used
+diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
+index fa382c5..41378c9 100755
+--- a/t/t7001-mv.sh
++++ b/t/t7001-mv.sh
+@@ -156,4 +156,56 @@ test_expect_success 'absolute pathname outside should fail' '(
+ 
+ )'
+ 
++test_expect_success 'git mv should not change sha1 of moved cache entry' '
++
++	rm -fr .git &&
++	git init &&
++	echo 1 >dirty &&
++	git add dirty &&
++	entry="$(git ls-files --stage dirty | cut -f 1)"
++	git mv dirty dirty2 &&
++	[ "$entry" = "$(git ls-files --stage dirty2 | cut -f 1)" ] &&
++	echo 2 >dirty2 &&
++	git mv dirty2 dirty &&
++	[ "$entry" = "$(git ls-files --stage dirty | cut -f 1)" ]
++
++'
++
++rm -f dirty dirty2
++
++test_expect_success 'git mv should overwrite symlink to a file' '
++
++	rm -fr .git &&
++	git init &&
++	echo 1 >moved &&
++	ln -s moved symlink &&
++	git add moved symlink &&
++	test_must_fail git mv moved symlink &&
++	git mv -f moved symlink &&
++	! test -e moved &&
++	test -f symlink &&
++	test "$(cat symlink)" = 1 &&
++	git diff-files --quiet
++
++'
++
++rm -f moved symlink
++
++test_expect_success 'git mv should overwrite file with a symlink' '
++
++	rm -fr .git &&
++	git init &&
++	echo 1 >moved &&
++	ln -s moved symlink &&
++	git add moved symlink &&
++	test_must_fail git mv symlink moved &&
++	git mv -f symlink moved &&
++	! test -e symlink &&
++	test -h moved &&
++	git diff-files --quiet
++
++'
++
++rm -f moved symlink
++
+ test_done
+-- 
+1.6.3.3
+
diff --git c/git.spec w/git.spec
index 2ad950c..3b4dab2 100644
--- c/git.spec
+++ w/git.spec
@@ -1,7 +1,7 @@
 # Pass --without docs to rpmbuild if you don't want the documentation
 Name: 		git
 Version: 	1.5.5.6
-Release: 	4%{?dist}
+Release: 	4%{?dist}.1
 Summary:  	Core git tools
 License: 	GPL
 Group: 		Development/Tools
@@ -17,6 +17,8 @@ Patch1:		CVE-2008-5517.patch
 Patch2:		git-1.5.5.6-reflog-permissions.patch
 # http://git.kernel.org/?p=git/git.git;a=commitdiff;h=73bb33a9
 Patch3:         git-1.5.5.6-daemon-extra-args.patch
+# Backport an upstream fix which corrects a git mv segfault
+Patch4:         git-1.5.5.6-mv-segfault.patch
 BuildRequires:	perl, zlib-devel >= 1.2, openssl-devel, curl-devel, expat-devel, emacs, gettext %{!?_without_docs:, xmlto, asciidoc > 6.0.3}
 BuildRoot:	%{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
@@ -129,6 +131,7 @@ Requires:      git = %{version}-%{release}, emacs-common
 %patch1 -p1 -b .CVE-2008-5517
 %patch2 -p1 -b .reflog-perms
 %patch3 -p1 -b .daemon-extra-args
+%patch4 -p1 -b .mv-segfault
 
 %build
 make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" \
@@ -253,6 +256,9 @@ rm -rf $RPM_BUILD_ROOT
 # No files for you!
 
 %changelog
+* Mon Jul 06 2009 Todd Zullinger <tmz@pobox.com> - 1.5.5.6-4.1
+- Backport an upstream fix which corrects a git mv segfault
+
 * Fri Jun 19 2009 Todd Zullinger <tmz@pobox.com> - 1.5.5.6-4
 - Fix git-daemon hang on invalid input (CVE-2009-2108, bug 505761)
 
