@@ -34,7 +34,11 @@ public function diffWorkingVsIndex(): array
3434 $ fullPath = $ this ->repository ->workDir . '/ ' . $ path ;
3535
3636 if (! file_exists ($ fullPath )) {
37- $ diffs [] = new FileDiff ($ path , FileStatus::Deleted, []);
37+ $ diff = $ this ->diffDeletedFile ($ path , $ entry ->objectId );
38+ if ($ diff instanceof FileDiff) {
39+ $ diffs [] = $ diff ;
40+ }
41+
3842 continue ;
3943 }
4044
@@ -52,8 +56,9 @@ public function diffWorkingVsIndex(): array
5256 $ oldLines = $ this ->splitLines ($ indexBlob ->content );
5357 $ newLines = $ this ->splitLines ($ workingContent );
5458 $ hunks = $ this ->diffAlgorithm ->diff ($ oldLines , $ newLines );
59+ $ newBlob = new Blob ($ workingContent );
5560
56- $ diffs [] = new FileDiff ($ path , FileStatus::Modified, $ hunks );
61+ $ diffs [] = new FileDiff ($ path , FileStatus::Modified, $ hunks, $ entry -> objectId , $ newBlob -> getId () );
5762 }
5863
5964 return $ diffs ;
@@ -69,33 +74,27 @@ public function diffIndexVsHead(): array
6974 $ diffs = [];
7075
7176 foreach ($ index ->getEntries () as $ path => $ entry ) {
72- if (! isset ($ headEntries [$ path ])) {
73- $ diffs [] = new FileDiff ($ path , FileStatus::Added, []);
74- continue ;
75- }
76-
77- if ($ entry ->objectId ->equals ($ headEntries [$ path ])) {
78- continue ;
79- }
80-
81- $ headBlob = $ this ->repository ->objects ->read ($ headEntries [$ path ]);
82- $ indexBlob = $ this ->repository ->objects ->read ($ entry ->objectId );
83-
84- if (! $ headBlob instanceof Blob || ! $ indexBlob instanceof Blob) {
85- continue ;
77+ $ diff = $ this ->diffIndexEntryVsHead ($ path , $ entry ->objectId , $ headEntries [$ path ] ?? null );
78+ if ($ diff instanceof FileDiff) {
79+ $ diffs [] = $ diff ;
8680 }
81+ }
8782
88- $ oldLines = $ this ->splitLines ($ headBlob ->content );
89- $ newLines = $ this ->splitLines ($ indexBlob ->content );
90- $ hunks = $ this ->diffAlgorithm ->diff ($ oldLines , $ newLines );
83+ return array_merge ($ diffs , $ this ->findDeletedFromHead ($ index , $ headEntries ));
84+ }
9185
92- $ diffs [] = new FileDiff ($ path , FileStatus::Modified, $ hunks );
93- }
86+ /**
87+ * @return list<FileDiff>
88+ */
89+ public function diffRootCommit (ObjectId $ commitId ): array
90+ {
91+ $ entries = $ this ->collectCommitEntries ($ commitId );
92+ $ diffs = [];
9493
95- // Deleted from HEAD
96- foreach ( array_keys ( $ headEntries ) as $ path) {
97- if (! $ index -> hasEntry ( $ path ) ) {
98- $ diffs [] = new FileDiff ( $ path , FileStatus::Deleted, []) ;
94+ foreach ( $ entries as $ path => $ objectId ) {
95+ $ diff = $ this -> diffAddedFile ( $ path, $ objectId );
96+ if ($ diff instanceof FileDiff ) {
97+ $ diffs [] = $ diff ;
9998 }
10099 }
101100
@@ -124,6 +123,39 @@ public function diffCommits(ObjectId $oldCommitId, ObjectId $newCommitId): array
124123 return $ diffs ;
125124 }
126125
126+ /**
127+ * @param array<string, ObjectId> $headEntries
128+ * @return list<FileDiff>
129+ */
130+ private function findDeletedFromHead (\Lukasojd \PureGit \Domain \Index \Index $ index , array $ headEntries ): array
131+ {
132+ $ diffs = [];
133+
134+ foreach ($ headEntries as $ path => $ objectId ) {
135+ if (! $ index ->hasEntry ($ path )) {
136+ $ diff = $ this ->diffDeletedFile ($ path , $ objectId );
137+ if ($ diff instanceof FileDiff) {
138+ $ diffs [] = $ diff ;
139+ }
140+ }
141+ }
142+
143+ return $ diffs ;
144+ }
145+
146+ private function diffIndexEntryVsHead (string $ path , ObjectId $ indexObjectId , ?ObjectId $ headObjectId ): ?FileDiff
147+ {
148+ if (! $ headObjectId instanceof ObjectId) {
149+ return $ this ->diffAddedFile ($ path , $ indexObjectId );
150+ }
151+
152+ if ($ indexObjectId ->equals ($ headObjectId )) {
153+ return null ;
154+ }
155+
156+ return $ this ->diffModifiedFile ($ path , $ headObjectId , $ indexObjectId );
157+ }
158+
127159 private function diffPath (string $ path , ?ObjectId $ oldId , ?ObjectId $ newId ): ?FileDiff
128160 {
129161 if (! $ oldId instanceof \Lukasojd \PureGit \Domain \Object \ObjectId && $ newId instanceof ObjectId) {
@@ -150,7 +182,7 @@ private function diffAddedFile(string $path, ObjectId $newId): ?FileDiff
150182
151183 $ hunks = $ this ->diffAlgorithm ->diff ([], $ this ->splitLines ($ blob ->content ));
152184
153- return new FileDiff ($ path , FileStatus::Added, $ hunks );
185+ return new FileDiff ($ path , FileStatus::Added, $ hunks, newId: $ newId );
154186 }
155187
156188 private function diffDeletedFile (string $ path , ObjectId $ oldId ): ?FileDiff
@@ -162,7 +194,7 @@ private function diffDeletedFile(string $path, ObjectId $oldId): ?FileDiff
162194
163195 $ hunks = $ this ->diffAlgorithm ->diff ($ this ->splitLines ($ blob ->content ), []);
164196
165- return new FileDiff ($ path , FileStatus::Deleted, $ hunks );
197+ return new FileDiff ($ path , FileStatus::Deleted, $ hunks, oldId: $ oldId );
166198 }
167199
168200 private function diffModifiedFile (string $ path , ObjectId $ oldId , ObjectId $ newId ): ?FileDiff
@@ -179,7 +211,7 @@ private function diffModifiedFile(string $path, ObjectId $oldId, ObjectId $newId
179211 $ this ->splitLines ($ newBlob ->content ),
180212 );
181213
182- return new FileDiff ($ path , FileStatus::Modified, $ hunks );
214+ return new FileDiff ($ path , FileStatus::Modified, $ hunks, $ oldId , $ newId );
183215 }
184216
185217 /**
@@ -247,6 +279,11 @@ private function splitLines(string $content): array
247279 return [];
248280 }
249281
282+ // Remove trailing newline to avoid phantom empty line
283+ if (str_ends_with ($ content , "\n" )) {
284+ $ content = substr ($ content , 0 , -1 );
285+ }
286+
250287 return explode ("\n" , $ content );
251288 }
252289}
0 commit comments