OBJ_MAX,
};
+static inline enum object_type object_type(unsigned int mode)
+{
+ return S_ISDIR(mode) ? OBJ_TREE :
+ S_ISGITLINK(mode) ? OBJ_COMMIT :
+ OBJ_BLOB;
+}
+
#define GIT_DIR_ENVIRONMENT "GIT_DIR"
#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
* is the default.
*/
- if (!S_ISREG(src->mode) || !S_ISREG(dst->mode))
- return 0; /* leave symlink rename alone */
+ if (S_ISREG(src->mode) != S_ISREG(dst->mode)) {
+ *merge_score_p = (int)MAX_SCORE;
+ return 1; /* even their types are different */
+ }
if (src->sha1_valid && dst->sha1_valid &&
!hashcmp(src->sha1, dst->sha1))
struct diff_filepair *p = q->queue[i];
int score;
- /* We deal only with in-place edit of non directory.
+ /*
+ * We deal only with in-place edit of blobs.
* We do not break anything else.
*/
if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two) &&
- !S_ISDIR(p->one->mode) && !S_ISDIR(p->two->mode) &&
+ object_type(p->one->mode) == OBJ_BLOB &&
+ object_type(p->two->mode) == OBJ_BLOB &&
!strcmp(p->one->path, p->two->path)) {
if (should_break(p->one, p->two,
break_score, &score)) {
'run diff with -B -M' \
'git diff-index -B -M "$tree" >current'
-# This should not mistake file0 as the copy source of new file1
-# due to type differences.
+# file0 changed from regular to symlink. file1 is very close to the preimage of file0.
+# because we break file0, file1 can become a rename of it.
cat >expected <<\EOF
:100644 120000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 67be421f88824578857624f7b3dc75e99a8a1481 T file0
-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M100 file1
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 R file0 file1
EOF
test_expect_success \
--- /dev/null
+#!/bin/sh
+
+test_description='typechange rename detection'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ rm -f foo bar &&
+ cat ../../COPYING >foo &&
+ ln -s linklink bar &&
+ git add foo bar &&
+ git commit -a -m Initial &&
+ git tag one &&
+
+ rm -f foo bar &&
+ cat ../../COPYING >bar &&
+ ln -s linklink foo &&
+ git add foo bar &&
+ git commit -a -m Second &&
+ git tag two &&
+
+ rm -f foo bar &&
+ cat ../../COPYING >foo &&
+ git add foo &&
+ git commit -a -m Third &&
+ git tag three &&
+
+ mv foo bar &&
+ ln -s linklink foo &&
+ git add foo bar &&
+ git commit -a -m Fourth &&
+ git tag four &&
+
+ # This is purely for sanity check
+
+ rm -f foo bar &&
+ cat ../../COPYING >foo &&
+ cat ../../Makefile >bar &&
+ git add foo bar &&
+ git commit -a -m Fifth &&
+ git tag five &&
+
+ rm -f foo bar &&
+ cat ../../Makefile >foo &&
+ cat ../../COPYING >bar &&
+ git add foo bar &&
+ git commit -a -m Sixth &&
+ git tag six
+
+'
+
+test_expect_success 'cross renames to be detected for regular files' '
+
+ git diff-tree five six -r --name-status -B -M | sort >actual &&
+ {
+ echo "R100 foo bar"
+ echo "R100 bar foo"
+ } | sort >expect &&
+ diff -u expect actual
+
+'
+
+test_expect_success 'cross renames to be detected for typechange' '
+
+ git diff-tree one two -r --name-status -B -M | sort >actual &&
+ {
+ echo "R100 foo bar"
+ echo "R100 bar foo"
+ } | sort >expect &&
+ diff -u expect actual
+
+'
+
+test_expect_success 'moves and renames' '
+
+ git diff-tree three four -r --name-status -B -M | sort >actual &&
+ {
+ echo "R100 foo bar"
+ echo "T100 foo"
+ } | sort >expect &&
+ diff -u expect actual
+
+'
+
+test_done
unsigned int mode;
};
-static inline enum object_type object_type(unsigned int mode)
-{
- return S_ISDIR(mode) ? OBJ_TREE :
- S_ISGITLINK(mode) ? OBJ_COMMIT :
- OBJ_BLOB;
-}
-
struct tree_desc {
const void *buffer;
struct name_entry entry;