42
42
$ changedFiles = getChangedFiles ($ changes , $ fileExtensions );
43
43
generateChangedFilesList ($ options ['output-file ' ], $ changedFiles );
44
44
saveChangedFileContent ($ repo );
45
+
46
+ $ additions = retrieveNewFilesAcrossForks ($ mainline , $ repo , $ options ['branch ' ]);
47
+ $ addedFiles = getChangedFiles ($ additions , $ fileExtensions );
48
+ $ additionsFile = pathinfo ($ options ['output-file ' ]);
49
+ $ additionsFile = $ additionsFile ['dirname ' ]
50
+ . DIRECTORY_SEPARATOR
51
+ . $ additionsFile ['filename ' ]
52
+ . '.added. '
53
+ . $ additionsFile ['extension ' ];
54
+ generateChangedFilesList ($ additionsFile , $ addedFiles );
55
+
45
56
cleanup ($ repo , $ mainline );
46
57
47
58
/**
@@ -143,7 +154,18 @@ function getRepo($options, $mainline)
143
154
*/
144
155
function retrieveChangesAcrossForks ($ mainline , GitRepo $ repo , $ branchName )
145
156
{
146
- return $ repo ->compareChanges ($ mainline , $ branchName );
157
+ return $ repo ->compareChanges ($ mainline , $ branchName , GitRepo::CHANGE_TYPE_ALL );
158
+ }
159
+
160
+ /**
161
+ * @param string $mainline
162
+ * @param GitRepo $repo
163
+ * @param string $branchName
164
+ * @return array
165
+ */
166
+ function retrieveNewFilesAcrossForks ($ mainline , GitRepo $ repo , $ branchName )
167
+ {
168
+ return $ repo ->compareChanges ($ mainline , $ branchName , GitRepo::CHANGE_TYPE_ADDED );
147
169
}
148
170
149
171
/**
@@ -178,6 +200,10 @@ function validateInput(array $options, array $requiredOptions)
178
200
class GitRepo
179
201
// @codingStandardsIgnoreEnd
180
202
{
203
+ const CHANGE_TYPE_ADDED = 1 ;
204
+ const CHANGE_TYPE_MODIFIED = 2 ;
205
+ const CHANGE_TYPE_ALL = 3 ;
206
+
181
207
/**
182
208
* Absolute path to git project
183
209
*
@@ -274,9 +300,10 @@ public function getBranches($source = '--all')
274
300
*
275
301
* @param string $remoteAlias
276
302
* @param string $remoteBranch
303
+ * @param int $changesType
277
304
* @return array
278
305
*/
279
- public function compareChanges ($ remoteAlias , $ remoteBranch )
306
+ public function compareChanges ($ remoteAlias , $ remoteBranch, $ changesType = self :: CHANGE_TYPE_ALL )
280
307
{
281
308
if (!isset ($ this ->remoteList [$ remoteAlias ])) {
282
309
throw new LogicException ('Alias " ' . $ remoteAlias . '" is not defined ' );
@@ -288,7 +315,8 @@ public function compareChanges($remoteAlias, $remoteBranch)
288
315
? $ this ->filterChangedFiles (
289
316
$ result ,
290
317
$ remoteAlias ,
291
- $ remoteBranch
318
+ $ remoteBranch ,
319
+ $ changesType
292
320
)
293
321
: [];
294
322
}
@@ -299,50 +327,113 @@ public function compareChanges($remoteAlias, $remoteBranch)
299
327
* @param array $changes
300
328
* @param string $remoteAlias
301
329
* @param string $remoteBranch
330
+ * @param int $changesType
302
331
* @return array
303
332
*/
304
- protected function filterChangedFiles (array $ changes , $ remoteAlias , $ remoteBranch )
305
- {
333
+ protected function filterChangedFiles (
334
+ array $ changes ,
335
+ $ remoteAlias ,
336
+ $ remoteBranch ,
337
+ $ changesType = self ::CHANGE_TYPE_ALL
338
+ ) {
306
339
$ countScannedFiles = 0 ;
307
- $ changedFilesMasks = [
308
- 'M ' => "M \t" ,
309
- 'A ' => "A \t"
310
- ];
340
+ $ changedFilesMasks = $ this ->buildChangedFilesMask ($ changesType );
311
341
$ filteredChanges = [];
312
342
foreach ($ changes as $ fileName ) {
313
343
$ countScannedFiles ++;
314
344
if (($ countScannedFiles % 5000 ) == 0 ) {
315
345
echo $ countScannedFiles . " files scanned so far \n" ;
316
346
}
317
- foreach ($ changedFilesMasks as $ mask ) {
318
- if (strpos ($ fileName , $ mask ) === 0 ) {
319
- $ fileName = str_replace ($ mask , '' , $ fileName );
320
- $ fileName = trim ($ fileName );
321
- if (!in_array ($ fileName , $ filteredChanges ) && is_file ($ this ->workTree . '/ ' . $ fileName )) {
322
- $ result = $ this ->call (
323
- sprintf (
324
- 'diff HEAD %s/%s -- %s ' ,
325
- $ remoteAlias ,
326
- $ remoteBranch ,
327
- $ this ->workTree . '/ ' . $ fileName
328
- )
329
- );
330
- if ($ result ) {
331
- if (!(isset ($ this ->changedContentFiles [$ fileName ]))) {
332
- $ this ->setChangedContentFile ($ result , $ fileName );
333
- }
334
- $ filteredChanges [] = $ fileName ;
335
- }
336
- }
337
- break ;
338
- }
347
+
348
+ $ changeTypeMask = $ this ->detectChangeTypeMask ($ fileName , $ changedFilesMasks );
349
+ if (null === $ changeTypeMask ) {
350
+ continue ;
339
351
}
352
+
353
+ $ fileName = trim (substr ($ fileName , strlen ($ changeTypeMask )));
354
+ if (in_array ($ fileName , $ filteredChanges )) {
355
+ continue ;
356
+ }
357
+
358
+ $ fileChanges = $ this ->getFileChangeDetails ($ fileName , $ remoteAlias , $ remoteBranch );
359
+ if (empty ($ fileChanges )) {
360
+ continue ;
361
+ }
362
+
363
+ if (!(isset ($ this ->changedContentFiles [$ fileName ]))) {
364
+ $ this ->setChangedContentFile ($ fileChanges , $ fileName );
365
+ }
366
+ $ filteredChanges [] = $ fileName ;
340
367
}
341
368
echo $ countScannedFiles . " files scanned \n" ;
342
369
343
370
return $ filteredChanges ;
344
371
}
345
372
373
+ /**
374
+ * Build mask of git diff report
375
+ *
376
+ * @param int $changesType
377
+ * @return array
378
+ */
379
+ private function buildChangedFilesMask (int $ changesType ): array
380
+ {
381
+ $ changedFilesMasks = [];
382
+ foreach ([
383
+ self ::CHANGE_TYPE_ADDED => "A \t" ,
384
+ self ::CHANGE_TYPE_MODIFIED => "M \t" ,
385
+ ] as $ changeType => $ changedFilesMask ) {
386
+ if ($ changeType & $ changesType ) {
387
+ $ changedFilesMasks [] = $ changedFilesMask ;
388
+ }
389
+ }
390
+ return $ changedFilesMasks ;
391
+ }
392
+
393
+ /**
394
+ * Find one of the allowed modification mask returned by git diff.
395
+ * Example of change record: "A path/to/added_file"
396
+ *
397
+ * @param string $changeRecord
398
+ * @param array $allowedMasks
399
+ * @return string|null
400
+ */
401
+ private function detectChangeTypeMask (string $ changeRecord , array $ allowedMasks )
402
+ {
403
+ foreach ($ allowedMasks as $ mask ) {
404
+ if (strpos ($ changeRecord , $ mask ) === 0 ) {
405
+ return $ mask ;
406
+ }
407
+ }
408
+ return null ;
409
+ }
410
+
411
+ /**
412
+ * Read detailed information about changes in a file
413
+ *
414
+ * @param string $fileName
415
+ * @param string $remoteAlias
416
+ * @param string $remoteBranch
417
+ * @return array
418
+ */
419
+ private function getFileChangeDetails (string $ fileName , string $ remoteAlias , string $ remoteBranch ): array
420
+ {
421
+ if (!is_file ($ this ->workTree . '/ ' . $ fileName )) {
422
+ return [];
423
+ }
424
+
425
+ $ result = $ this ->call (
426
+ sprintf (
427
+ 'diff HEAD %s/%s -- %s ' ,
428
+ $ remoteAlias ,
429
+ $ remoteBranch ,
430
+ $ this ->workTree . '/ ' . $ fileName
431
+ )
432
+ );
433
+
434
+ return $ result ;
435
+ }
436
+
346
437
/**
347
438
* Set changed content for file.
348
439
*
0 commit comments