From 52fda312dfd2ed93e512c8e210c5646e4ae53dbc Mon Sep 17 00:00:00 2001
From: Filip Navara <filip.navara@gmail.com>
Date: Thu, 12 Sep 2019 03:14:41 +0200
Subject: [PATCH] Fix reading git notes from nested trees (#8026)

* Fix reading notes from nested trees

The GIT documentation for notes states "Permitted pathnames have the
form ab/cd/ef/.../abcdef...: a sequence of directory names of two
hexadecimal digits each followed by a filename with the rest of
the object ID."

* Add test case

* Fix new lines
---
 modules/git/notes.go                          |  46 +++++++++++-------
 modules/git/notes_test.go                     |  14 ++++++
 .../tests/repos/repo3_notes/COMMIT_EDITMSG    |   1 +
 modules/git/tests/repos/repo3_notes/HEAD      |   1 +
 modules/git/tests/repos/repo3_notes/config    |   7 +++
 .../git/tests/repos/repo3_notes/description   |   1 +
 modules/git/tests/repos/repo3_notes/index     | Bin 0 -> 145 bytes
 modules/git/tests/repos/repo3_notes/logs/HEAD |   2 +
 .../repos/repo3_notes/logs/refs/heads/master  |   2 +
 .../29/7128d6553180486c780e2f747cb6d0014bf1f6 | Bin 0 -> 55 bytes
 .../2f/7e2ea1e905c14c8a98e7ce47b395592834b9ef | Bin 0 -> 81 bytes
 .../3e/668dbfac39cbc80a9ff9c61eb565d944453ba4 |   3 ++
 .../42/716fdb6f261867472899d785123e6ecaa5ca02 | Bin 0 -> 44 bytes
 .../56/a6051ca2b02b04ef92d5150c9ef600403cb1de | Bin 0 -> 16 bytes
 .../61/6c62e75fce60d806f4afe993211705a00a2544 | Bin 0 -> 21 bytes
 .../65/4c8b6b63c08bf37f638d3f521626b7fbbd4d37 |   1 +
 .../ba/0a96fa63532d6c5087ecef070b0250ed72fa47 | Bin 0 -> 125 bytes
 .../c9/34d51cee361fdee21a3f3bb1a285f5ea9bc225 | Bin 0 -> 55 bytes
 .../d8/263ee9860594d2806b0dfd1bfd17528b0ba2a4 | Bin 0 -> 16 bytes
 .../f3/6ad903e408cb8f4ed90bda02e3a1fd2fab7907 | Bin 0 -> 21 bytes
 .../fe/c9fe57e9864fe537f02f825e377c4a8a65ad2e | Bin 0 -> 113 bytes
 .../tests/repos/repo3_notes/refs/heads/master |   1 +
 .../repos/repo3_notes/refs/notes/commits      |   1 +
 23 files changed, 63 insertions(+), 17 deletions(-)
 create mode 100644 modules/git/tests/repos/repo3_notes/COMMIT_EDITMSG
 create mode 100644 modules/git/tests/repos/repo3_notes/HEAD
 create mode 100644 modules/git/tests/repos/repo3_notes/config
 create mode 100644 modules/git/tests/repos/repo3_notes/description
 create mode 100644 modules/git/tests/repos/repo3_notes/index
 create mode 100644 modules/git/tests/repos/repo3_notes/logs/HEAD
 create mode 100644 modules/git/tests/repos/repo3_notes/logs/refs/heads/master
 create mode 100644 modules/git/tests/repos/repo3_notes/objects/29/7128d6553180486c780e2f747cb6d0014bf1f6
 create mode 100644 modules/git/tests/repos/repo3_notes/objects/2f/7e2ea1e905c14c8a98e7ce47b395592834b9ef
 create mode 100644 modules/git/tests/repos/repo3_notes/objects/3e/668dbfac39cbc80a9ff9c61eb565d944453ba4
 create mode 100644 modules/git/tests/repos/repo3_notes/objects/42/716fdb6f261867472899d785123e6ecaa5ca02
 create mode 100644 modules/git/tests/repos/repo3_notes/objects/56/a6051ca2b02b04ef92d5150c9ef600403cb1de
 create mode 100644 modules/git/tests/repos/repo3_notes/objects/61/6c62e75fce60d806f4afe993211705a00a2544
 create mode 100644 modules/git/tests/repos/repo3_notes/objects/65/4c8b6b63c08bf37f638d3f521626b7fbbd4d37
 create mode 100644 modules/git/tests/repos/repo3_notes/objects/ba/0a96fa63532d6c5087ecef070b0250ed72fa47
 create mode 100644 modules/git/tests/repos/repo3_notes/objects/c9/34d51cee361fdee21a3f3bb1a285f5ea9bc225
 create mode 100644 modules/git/tests/repos/repo3_notes/objects/d8/263ee9860594d2806b0dfd1bfd17528b0ba2a4
 create mode 100644 modules/git/tests/repos/repo3_notes/objects/f3/6ad903e408cb8f4ed90bda02e3a1fd2fab7907
 create mode 100644 modules/git/tests/repos/repo3_notes/objects/fe/c9fe57e9864fe537f02f825e377c4a8a65ad2e
 create mode 100644 modules/git/tests/repos/repo3_notes/refs/heads/master
 create mode 100644 modules/git/tests/repos/repo3_notes/refs/notes/commits

diff --git a/modules/git/notes.go b/modules/git/notes.go
index aea54ab202..e825923682 100644
--- a/modules/git/notes.go
+++ b/modules/git/notes.go
@@ -6,6 +6,8 @@ package git
 
 import (
 	"io/ioutil"
+
+	"gopkg.in/src-d/go-git.v4/plumbing/object"
 )
 
 // NotesRef is the git ref where Gitea will look for git-notes data.
@@ -25,13 +27,28 @@ func GetNote(repo *Repository, commitID string, note *Note) error {
 		return err
 	}
 
-	entry, err := notes.GetTreeEntryByPath(commitID)
-	if err != nil {
-		return err
+	remainingCommitID := commitID
+	path := ""
+	currentTree := notes.Tree.gogitTree
+	var file *object.File
+	for len(remainingCommitID) > 2 {
+		file, err = currentTree.File(remainingCommitID)
+		if err == nil {
+			path += remainingCommitID
+			break
+		}
+		if err == object.ErrFileNotFound {
+			currentTree, err = currentTree.Tree(remainingCommitID[0:2])
+			path += remainingCommitID[0:2] + "/"
+			remainingCommitID = remainingCommitID[2:]
+		}
+		if err != nil {
+			return err
+		}
 	}
 
-	blob := entry.Blob()
-	dataRc, err := blob.DataAsync()
+	blob := file.Blob
+	dataRc, err := blob.Reader()
 	if err != nil {
 		return err
 	}
@@ -43,26 +60,21 @@ func GetNote(repo *Repository, commitID string, note *Note) error {
 	}
 	note.Message = d
 
-	commit, err := repo.gogitRepo.CommitObject(notes.ID)
-	if err != nil {
-		return err
-	}
-
 	commitNodeIndex, commitGraphFile := repo.CommitNodeIndex()
 	if commitGraphFile != nil {
 		defer commitGraphFile.Close()
 	}
 
-	commitNode, err := commitNodeIndex.Get(commit.Hash)
-	if err != nil {
-		return nil
-	}
-
-	lastCommits, err := getLastCommitForPaths(commitNode, "", []string{commitID})
+	commitNode, err := commitNodeIndex.Get(notes.ID)
 	if err != nil {
 		return err
 	}
-	note.Commit = convertCommit(lastCommits[commitID])
+
+	lastCommits, err := getLastCommitForPaths(commitNode, "", []string{path})
+	if err != nil {
+		return err
+	}
+	note.Commit = convertCommit(lastCommits[path])
 
 	return nil
 }
diff --git a/modules/git/notes_test.go b/modules/git/notes_test.go
index a954377f54..bf010b9a71 100644
--- a/modules/git/notes_test.go
+++ b/modules/git/notes_test.go
@@ -22,3 +22,17 @@ func TestGetNotes(t *testing.T) {
 	assert.Equal(t, []byte("Note contents\n"), note.Message)
 	assert.Equal(t, "Vladimir Panteleev", note.Commit.Author.Name)
 }
+
+func TestGetNestedNotes(t *testing.T) {
+	repoPath := filepath.Join(testReposDir, "repo3_notes")
+	repo, err := OpenRepository(repoPath)
+	assert.NoError(t, err)
+
+	note := Note{}
+	err = GetNote(repo, "3e668dbfac39cbc80a9ff9c61eb565d944453ba4", &note)
+	assert.NoError(t, err)
+	assert.Equal(t, []byte("Note 2"), note.Message)
+	err = GetNote(repo, "ba0a96fa63532d6c5087ecef070b0250ed72fa47", &note)
+	assert.NoError(t, err)
+	assert.Equal(t, []byte("Note 1"), note.Message)
+}
diff --git a/modules/git/tests/repos/repo3_notes/COMMIT_EDITMSG b/modules/git/tests/repos/repo3_notes/COMMIT_EDITMSG
new file mode 100644
index 0000000000..0cfbf08886
--- /dev/null
+++ b/modules/git/tests/repos/repo3_notes/COMMIT_EDITMSG
@@ -0,0 +1 @@
+2
diff --git a/modules/git/tests/repos/repo3_notes/HEAD b/modules/git/tests/repos/repo3_notes/HEAD
new file mode 100644
index 0000000000..cb089cd89a
--- /dev/null
+++ b/modules/git/tests/repos/repo3_notes/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/modules/git/tests/repos/repo3_notes/config b/modules/git/tests/repos/repo3_notes/config
new file mode 100644
index 0000000000..d545cdabdb
--- /dev/null
+++ b/modules/git/tests/repos/repo3_notes/config
@@ -0,0 +1,7 @@
+[core]
+	repositoryformatversion = 0
+	filemode = false
+	bare = false
+	logallrefupdates = true
+	symlinks = false
+	ignorecase = true
diff --git a/modules/git/tests/repos/repo3_notes/description b/modules/git/tests/repos/repo3_notes/description
new file mode 100644
index 0000000000..498b267a8c
--- /dev/null
+++ b/modules/git/tests/repos/repo3_notes/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/modules/git/tests/repos/repo3_notes/index b/modules/git/tests/repos/repo3_notes/index
new file mode 100644
index 0000000000000000000000000000000000000000..783158b928792af214ea5690c287a1ff1b0bb19c
GIT binary patch
literal 145
zcmZ?q402{*U|<4b#@HfTN9oi51AuhAw&r9OpcoW1E`hOtG~*35yO(XOQ!X`R^Zu3o
zD<0I%y=VyoS5azWN^YuNNks`vZAg%-E08V8V5nfgrCF$PE!42VBd3B-zocf{1xD|W
b-xkQr{j~DP`m;*qnc%ET*S>#$8}14K;I=DL

literal 0
HcmV?d00001

diff --git a/modules/git/tests/repos/repo3_notes/logs/HEAD b/modules/git/tests/repos/repo3_notes/logs/HEAD
new file mode 100644
index 0000000000..4bd0a61ea6
--- /dev/null
+++ b/modules/git/tests/repos/repo3_notes/logs/HEAD
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 ba0a96fa63532d6c5087ecef070b0250ed72fa47 Filip Navara <filip.navara@gmail.com> 1567767895 +0200	commit (initial): 1
+ba0a96fa63532d6c5087ecef070b0250ed72fa47 3e668dbfac39cbc80a9ff9c61eb565d944453ba4 Filip Navara <filip.navara@gmail.com> 1567767909 +0200	commit: 2
diff --git a/modules/git/tests/repos/repo3_notes/logs/refs/heads/master b/modules/git/tests/repos/repo3_notes/logs/refs/heads/master
new file mode 100644
index 0000000000..4bd0a61ea6
--- /dev/null
+++ b/modules/git/tests/repos/repo3_notes/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 ba0a96fa63532d6c5087ecef070b0250ed72fa47 Filip Navara <filip.navara@gmail.com> 1567767895 +0200	commit (initial): 1
+ba0a96fa63532d6c5087ecef070b0250ed72fa47 3e668dbfac39cbc80a9ff9c61eb565d944453ba4 Filip Navara <filip.navara@gmail.com> 1567767909 +0200	commit: 2
diff --git a/modules/git/tests/repos/repo3_notes/objects/29/7128d6553180486c780e2f747cb6d0014bf1f6 b/modules/git/tests/repos/repo3_notes/objects/29/7128d6553180486c780e2f747cb6d0014bf1f6
new file mode 100644
index 0000000000000000000000000000000000000000..96fb7495211b6c63b505da9f1c3299ee32427136
GIT binary patch
literal 55
zcmV-70LcG%0V^p=O;s?qU@$Z=Ff%bxC`wIC$xYQOsVHH%p=S58jdjYUhHT!y(tpK+
Ny15rE0RUbR5br~X7V`iA

literal 0
HcmV?d00001

diff --git a/modules/git/tests/repos/repo3_notes/objects/2f/7e2ea1e905c14c8a98e7ce47b395592834b9ef b/modules/git/tests/repos/repo3_notes/objects/2f/7e2ea1e905c14c8a98e7ce47b395592834b9ef
new file mode 100644
index 0000000000000000000000000000000000000000..71cff177b2438d739269e7dfe455811b8819aeae
GIT binary patch
literal 81
zcmb<m)YkO!4K*-MG%ztRFg6U_;Nj`z?PIph$Iok<fyWjf@6A4D#y-AArpCS-O$-eU
lP5rz~5<Sj%R>{@tJ!HE*{j0vZcGqUT2SKHb3`~LIYybte8W;co

literal 0
HcmV?d00001

diff --git a/modules/git/tests/repos/repo3_notes/objects/3e/668dbfac39cbc80a9ff9c61eb565d944453ba4 b/modules/git/tests/repos/repo3_notes/objects/3e/668dbfac39cbc80a9ff9c61eb565d944453ba4
new file mode 100644
index 0000000000..8f13b31d71
--- /dev/null
+++ b/modules/git/tests/repos/repo3_notes/objects/3e/668dbfac39cbc80a9ff9c61eb565d944453ba4
@@ -0,0 +1,3 @@
+x��;�0@�s
+�H��&v*!����4�J��(p~
+G`|oxzi���;��3� �����6��$`�"NR�ѺXla�i�K������4r�$�\P0"ỵPQ'F_��V�N�i����*�ʗ��G���ӳK�|�Y�e��H�f��f����Em
\ No newline at end of file
diff --git a/modules/git/tests/repos/repo3_notes/objects/42/716fdb6f261867472899d785123e6ecaa5ca02 b/modules/git/tests/repos/repo3_notes/objects/42/716fdb6f261867472899d785123e6ecaa5ca02
new file mode 100644
index 0000000000000000000000000000000000000000..3d522eb2981ee0de169122a081ecfb920dd3b4c4
GIT binary patch
literal 44
zcmb<m)YkO!4K>)5VqnO?#A<BJ_Sb4}t9~T^uadJBb?0h7U-96bJoi20O%>j6082L!
AoB#j-

literal 0
HcmV?d00001

diff --git a/modules/git/tests/repos/repo3_notes/objects/56/a6051ca2b02b04ef92d5150c9ef600403cb1de b/modules/git/tests/repos/repo3_notes/objects/56/a6051ca2b02b04ef92d5150c9ef600403cb1de
new file mode 100644
index 0000000000000000000000000000000000000000..b17dfe30e64f245f6aa2284091ae1af5cba214ff
GIT binary patch
literal 16
Xcmb<m^geacKgb}(fQ5nk3X>85F9rm}

literal 0
HcmV?d00001

diff --git a/modules/git/tests/repos/repo3_notes/objects/61/6c62e75fce60d806f4afe993211705a00a2544 b/modules/git/tests/repos/repo3_notes/objects/61/6c62e75fce60d806f4afe993211705a00a2544
new file mode 100644
index 0000000000000000000000000000000000000000..b09d3a2d2cbdc83f887d52a5857db992c6509c83
GIT binary patch
literal 21
ccmb<m^geacKgb~Y!)blbPy;3gnGekO09_0SGXMYp

literal 0
HcmV?d00001

diff --git a/modules/git/tests/repos/repo3_notes/objects/65/4c8b6b63c08bf37f638d3f521626b7fbbd4d37 b/modules/git/tests/repos/repo3_notes/objects/65/4c8b6b63c08bf37f638d3f521626b7fbbd4d37
new file mode 100644
index 0000000000..0d317cb80e
--- /dev/null
+++ b/modules/git/tests/repos/repo3_notes/objects/65/4c8b6b63c08bf37f638d3f521626b7fbbd4d37
@@ -0,0 +1 @@
+x��;�0�}��"ǿu$�RQr���K1F���1�nf��R-%w��zc�{�%7��h#�x���Q���fXѻ?j�K����S#8�צ���{��M��3�> �����6Z�Q�m���8�
\ No newline at end of file
diff --git a/modules/git/tests/repos/repo3_notes/objects/ba/0a96fa63532d6c5087ecef070b0250ed72fa47 b/modules/git/tests/repos/repo3_notes/objects/ba/0a96fa63532d6c5087ecef070b0250ed72fa47
new file mode 100644
index 0000000000000000000000000000000000000000..c21f2b2a222583e74825e8314db14b29d072aa04
GIT binary patch
literal 125
zcmV-@0D}K`0iBI84gw(%0Ihk&_9hn=TsRVAtStS*!YxX2h#~0vJ$-`ZOfi#Mtz`gu
zw;NpqQm}`GFo{@)c@Z(lHrp7H%}kkuLrlhy;@7yh4wtf)CtUf>o#B|jG(CQFzEv*k
flv+IjlJnmAIS}l%(OPYG+Yb3Vpiq4Pq0%``SU);1

literal 0
HcmV?d00001

diff --git a/modules/git/tests/repos/repo3_notes/objects/c9/34d51cee361fdee21a3f3bb1a285f5ea9bc225 b/modules/git/tests/repos/repo3_notes/objects/c9/34d51cee361fdee21a3f3bb1a285f5ea9bc225
new file mode 100644
index 0000000000000000000000000000000000000000..f5a8caa759529c3670c7b897f352c83a4e711357
GIT binary patch
literal 55
zcmb<m)YkO!4K>)7U|?ckU~Cwu;pOXd()X(NN{vL0=wn)&Gn&m#|Mm=bt$bFST(IG0
L1wVtIk$^P-3+@ys

literal 0
HcmV?d00001

diff --git a/modules/git/tests/repos/repo3_notes/objects/d8/263ee9860594d2806b0dfd1bfd17528b0ba2a4 b/modules/git/tests/repos/repo3_notes/objects/d8/263ee9860594d2806b0dfd1bfd17528b0ba2a4
new file mode 100644
index 0000000000000000000000000000000000000000..4b1baefffb3b60b8e9181765583c22017a52f5b2
GIT binary patch
literal 16
Xcmb<m^geacKgb}(fQf<oDw8q*F8&0>

literal 0
HcmV?d00001

diff --git a/modules/git/tests/repos/repo3_notes/objects/f3/6ad903e408cb8f4ed90bda02e3a1fd2fab7907 b/modules/git/tests/repos/repo3_notes/objects/f3/6ad903e408cb8f4ed90bda02e3a1fd2fab7907
new file mode 100644
index 0000000000000000000000000000000000000000..dc2af772edef002837fbd9d1e23b88d138437ea3
GIT binary patch
literal 21
ccmb<m^geacKgb~Y!)blbPy-eQnfJ_h0bLXbGXMYp

literal 0
HcmV?d00001

diff --git a/modules/git/tests/repos/repo3_notes/objects/fe/c9fe57e9864fe537f02f825e377c4a8a65ad2e b/modules/git/tests/repos/repo3_notes/objects/fe/c9fe57e9864fe537f02f825e377c4a8a65ad2e
new file mode 100644
index 0000000000000000000000000000000000000000..6372ff10abcef2d2640ac58fc4be3aca993638d2
GIT binary patch
literal 113
zcmV-%0FM870V^p=O;xZoXD~4U0tMq#2B*UO+xcn|>FydcueS=><(*o3ipkKxz|6!%
zAt})y(b6m}(ahM?*eJy;+0?+oJT*Br&A{9s$-v0eAT`C@C@s;%oZ)lUP39*Yr~CbG
Ta^GTlyzsC7>PmJ1tjr@Pbo)Eu

literal 0
HcmV?d00001

diff --git a/modules/git/tests/repos/repo3_notes/refs/heads/master b/modules/git/tests/repos/repo3_notes/refs/heads/master
new file mode 100644
index 0000000000..e96af8d801
--- /dev/null
+++ b/modules/git/tests/repos/repo3_notes/refs/heads/master
@@ -0,0 +1 @@
+3e668dbfac39cbc80a9ff9c61eb565d944453ba4
\ No newline at end of file
diff --git a/modules/git/tests/repos/repo3_notes/refs/notes/commits b/modules/git/tests/repos/repo3_notes/refs/notes/commits
new file mode 100644
index 0000000000..74e3d3ad8d
--- /dev/null
+++ b/modules/git/tests/repos/repo3_notes/refs/notes/commits
@@ -0,0 +1 @@
+654c8b6b63c08bf37f638d3f521626b7fbbd4d37
\ No newline at end of file