Commit Diff


commit - 0208f208304c36921fbcd86d33751b877aab1e96
commit + ac5f2b268fa7b1547b452a2d2234c03a33a1edf6
blob - ea5232abfd828a7f52894757aaa990fca8c07c9b
blob + 60884259fffeeadac52f16b3ef8d210656e4c539
--- lib/object.c
+++ lib/object.c
@@ -1622,6 +1622,25 @@ done:
 	if (tree)
 		got_object_tree_close(tree);
 	return err;
+}
+
+/*
+ * Normalize file mode bits to avoid false positive tree entry differences
+ * in case tree entries have unexpected mode bits set.
+ */
+static mode_t
+normalize_mode_for_comparison(mode_t mode)
+{
+	/*
+	 * For directories, the only relevant bit is the IFDIR bit.
+	 * This allows us to detect paths changing from a directory
+	 * to a file and vice versa.
+	 */
+	if (S_ISDIR(mode))
+		return mode & S_IFDIR;
+
+	/* For files, the only change we care about is the executable bit. */
+	return mode & S_IXUSR;
 }
 
 const struct got_error *
@@ -1650,6 +1669,7 @@ got_object_tree_path_changed(int *changed,
 	seglen = 0;
 	while (*s) {
 		struct got_tree_object *next_tree1, *next_tree2;
+		mode_t mode1, mode2;
 
 		if (*s != '/') {
 			s++;
@@ -1670,7 +1690,9 @@ got_object_tree_path_changed(int *changed,
 			goto done;
 		}
 
-		if (te1->mode != te2->mode) {
+		mode1 = normalize_mode_for_comparison(te1->mode);
+		mode2 = normalize_mode_for_comparison(te2->mode);
+		if (mode1 != mode2) {
 			*changed = 1;
 			goto done;
 		}