diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 39bfbca1ffe..15488bd92b2 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -758,6 +758,8 @@ with the above configuration, i.e. `j-c-diff`, with 7
 parameters, just like `GIT_EXTERNAL_DIFF` program is called.
 See linkgit:git[1] for details.
 
+When using a bare repository, the gitattributes from HEAD will be used.
+
 Setting the internal diff algorithm
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -785,6 +787,8 @@ This diff algorithm applies to user facing diff output like git-diff(1),
 git-show(1) and is used for the `--stat` output as well. The merge machinery
 will not use the diff algorithm set through this method.
 
+When using a bare repository, the gitattributes from HEAD will be used.
+
 NOTE: If `diff.<name>.command` is defined for path with the
 `diff=<name>` attribute, it is executed as an external diff driver
 (see above), and adding `diff.<name>.algorithm` has no effect, as the
diff --git a/diff.c b/diff.c
index 469e18aed20..51baf893bb0 100644
--- a/diff.c
+++ b/diff.c
@@ -29,6 +29,7 @@
 #include "promisor-remote.h"
 #include "dir.h"
 #include "strmap.h"
+#include "tree.h"
 
 #ifdef NO_FAST_WORKING_DIRECTORY
 #define FAST_WORKING_DIRECTORY 0
@@ -4443,6 +4444,27 @@ static void fill_metainfo(struct strbuf *msg,
 	}
 }
 
+static void get_userdiff(struct diff_options *o,
+			     struct userdiff_driver **drv,
+			     const char *attr_path)
+{
+	const char *commit = "HEAD";
+	struct object_id *tree_oid = NULL;
+
+	if (is_bare_repository() && o->repo->gitdir) {
+		struct object_id oid;
+
+		if (!get_oid(commit, &oid)) {
+			struct tree *t = parse_tree_indirect(&oid);
+
+			if (t)
+				tree_oid = &t->object.oid;
+		}
+	}
+
+	*drv = userdiff_find_by_tree_and_path(o->repo->index, tree_oid, attr_path);
+}
+
 static void run_diff_cmd(const char *pgm,
 			 const char *name,
 			 const char *other,
@@ -4458,8 +4480,10 @@ static void run_diff_cmd(const char *pgm,
 	int must_show_header = 0;
 	struct userdiff_driver *drv = NULL;
 
-	if (o->flags.allow_external || !o->ignore_driver_algorithm)
-		drv = userdiff_find_by_path(o->repo->index, attr_path);
+	if (o->flags.allow_external || !o->ignore_driver_algorithm) {
+
+		get_userdiff(o, &drv, attr_path);
+	}
 
 	if (o->flags.allow_external && drv && drv->external)
 		pgm = drv->external;
@@ -4586,8 +4610,9 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
 	const char *other;
 
 	if (!o->ignore_driver_algorithm) {
-		struct userdiff_driver *drv = userdiff_find_by_path(o->repo->index,
-								    p->one->path);
+		struct userdiff_driver *drv = NULL;
+
+		get_userdiff(o, &drv, p->one->path);
 
 		if (drv && drv->algorithm)
 			set_diff_algorithm(o, drv->algorithm);
diff --git a/t/lib-diff-alternative.sh b/t/lib-diff-alternative.sh
index a8f5d3274a5..0d99af83dd2 100644
--- a/t/lib-diff-alternative.sh
+++ b/t/lib-diff-alternative.sh
@@ -121,6 +121,16 @@ EOF
 		test_cmp expect output
 	'
 
+	test_expect_success "$STRATEGY diff from attributes with bare repo" '
+		echo "file* diff=driver" >.gitattributes &&
+		git add file1 file2 .gitattributes &&
+		git commit -m "adding files" &&
+		git clone --bare --no-local . bare.git &&
+		git -C bare.git config diff.driver.algorithm "$STRATEGY" &&
+		git -C bare.git diff HEAD:file1 HEAD:file2 > output &&
+		test_cmp expect output
+	'
+
 	test_expect_success "$STRATEGY diff from attributes has valid diffstat" '
 		echo "file* diff=driver" >.gitattributes &&
 		git config diff.driver.algorithm "$STRATEGY" &&
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
index 42a2b9a13b7..451af08c611 100755
--- a/t/t4018-diff-funcname.sh
+++ b/t/t4018-diff-funcname.sh
@@ -63,6 +63,17 @@ do
 		test_i18ngrep ! fatal msg &&
 		test_i18ngrep ! error msg
 	'
+	test_expect_success "builtin $p pattern compiles on bare repo" '
+		test_when_finished "rm -rf bare.git" &&
+		echo "*.java diff=$p" >.gitattributes &&
+		git add . &&
+		git commit -am "adding files" &&
+		git clone --bare --no-local . bare.git &&
+		test_expect_code 1 git -C bare.git diff --exit-code \
+			HEAD:A.java HEAD:B.java 2>msg &&
+		test_i18ngrep ! fatal msg &&
+		test_i18ngrep ! error msg
+	'
 done
 
 test_expect_success 'last regexp must not be negated' '
diff --git a/userdiff.c b/userdiff.c
index 58a3d59ef8f..2305d363244 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -408,6 +408,13 @@ struct userdiff_driver *userdiff_find_by_name(const char *name)
 
 struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
 					      const char *path)
+{
+	return userdiff_find_by_tree_and_path(istate, NULL, path);
+}
+
+struct userdiff_driver *userdiff_find_by_tree_and_path(struct index_state *istate,
+						       const struct object_id *tree_oid,
+						       const char *path)
 {
 	static struct attr_check *check;
 
@@ -415,7 +422,7 @@ struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
 		check = attr_check_initl("diff", NULL);
 	if (!path)
 		return NULL;
-	git_check_attr(istate, NULL, path, check);
+	git_check_attr(istate, tree_oid, path, check);
 
 	if (ATTR_TRUE(check->items[0].value))
 		return &driver_true;
diff --git a/userdiff.h b/userdiff.h
index 24419db6973..f9cd7c238de 100644
--- a/userdiff.h
+++ b/userdiff.h
@@ -33,6 +33,10 @@ int userdiff_config(const char *k, const char *v);
 struct userdiff_driver *userdiff_find_by_name(const char *name);
 struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
 					      const char *path);
+struct userdiff_driver *userdiff_find_by_tree_and_path(struct index_state *istate,
+						       const struct object_id *tree_oid,
+						       const char *path);
+
 
 /*
  * Initialize any textconv-related fields in the driver and return it, or NULL
