@@ -268,12 +268,12 @@ check_file(File, LargestLicense, Templates, VendorPaths, Opts) ->
268268 Data = read (File , LargestLicense * 3 ),
269269 case re :run (Data , " (.* )?%CopyrightBegin%(?:\r\n |\n )" ,[]) of
270270 {match , [StartPos | PrefixPos ]} ->
271- check_file_header (File , File , Data , StartPos , PrefixPos , Templates , Opts );
271+ check_file_header (File , File , Data , StartPos , PrefixPos , Templates , VendorPaths , Opts );
272272 nomatch ->
273273 maybe
274274 {ok , LicData } ?= file :read_file (File ++ " .license" ),
275275 {match , [StartPos | PrefixPos ]} ?= re :run (LicData , " (.* )?%CopyrightBegin%(?:\r\n |\n )" ,[]),
276- check_file_header (File , File ++ " .license" , LicData , StartPos , PrefixPos , Templates , Opts )
276+ check_file_header (File , File ++ " .license" , LicData , StartPos , PrefixPos , Templates , VendorPaths , Opts )
277277 else
278278 _ ->
279279 ReportMissing = not is_ignored (File ) andalso
@@ -297,11 +297,11 @@ check_file(File, LargestLicense, Templates, VendorPaths, Opts) ->
297297 erlang :raise (E ,R ,ST )
298298 end .
299299
300- check_file_header (File , LicenseFile , Data , StartPos , [], Templates , Opts ) ->
301- check_file_header (File , LicenseFile , Data , StartPos , <<>>, Templates , Opts );
302- check_file_header (File , LicenseFile , Data , StartPos , [PrefixPos ], Templates , Opts ) ->
303- check_file_header (File , LicenseFile , Data , StartPos , binary :part (Data , PrefixPos ), Templates , Opts );
304- check_file_header (File , LicenseFile , Data , {Start , StartEnd }, Prefix , Templates , Opts ) ->
300+ check_file_header (File , LicenseFile , Data , StartPos , [], Templates , VendorPaths , Opts ) ->
301+ check_file_header (File , LicenseFile , Data , StartPos , <<>>, Templates , VendorPaths , Opts );
302+ check_file_header (File , LicenseFile , Data , StartPos , [PrefixPos ], Templates , VendorPaths , Opts ) ->
303+ check_file_header (File , LicenseFile , Data , StartPos , binary :part (Data , PrefixPos ), Templates , VendorPaths , Opts );
304+ check_file_header (File , LicenseFile , Data , {Start , StartEnd }, Prefix , Templates , VendorPaths , Opts ) ->
305305 case re :run (Data , [" \\ Q" , Prefix , " \\ E%CopyrightEnd%(\r\n |\n )" ],[]) of
306306 {match , [{End , EndPos },{_ ,NlSize }]} ->
307307 DataAfterHeader = binary :part (Data , End + EndPos , byte_size (Data ) - (End + EndPos )),
@@ -312,8 +312,8 @@ check_file_header(File, LicenseFile, Data, {Start, StartEnd}, Prefix, Templates,
312312 check_license (License , Spdx , Templates ,
313313 length (string :split (DataAfterHeader ," \n " ,all )),
314314 File , not string :equal (File , LicenseFile ), Opts ),
315- case maps :get (update , Opts , false ) of
316- true -> update_copyright (LicenseFile , Start + StartEnd , End , Prefix , LineEnding , Spdx , Copyrights , License );
315+ case maps :get (update , Opts , false ) andalso not is_vendored ( File , VendorPaths ) of
316+ true -> update_copyright (File , LicenseFile , Start + StartEnd , End , Prefix , LineEnding , Spdx , Copyrights , License );
317317 false -> ok
318318 end ;
319319 nomatch when map_get (verbose , Opts ) ->
@@ -322,15 +322,15 @@ check_file_header(File, LicenseFile, Data, {Start, StartEnd}, Prefix, Templates,
322322 throw ({warn , " Could not find '~ts %CopyrightEnd%'" , [Prefix ]})
323323 end .
324324
325- update_copyright (File , Begin , End , Prefix , LineEnding , Spdx , Copyrights , License ) ->
325+ update_copyright (File , LicenseFile , Begin , End , Prefix , LineEnding , Spdx , Copyrights , License ) ->
326326 case update_copyright (File , Copyrights ) of
327327 Copyrights -> ok ;
328328 NewCopyrights ->
329- {ok , Data } = file :read_file (File ),
329+ {ok , Data } = file :read_file (LicenseFile ),
330330 Before = binary :part (Data , 0 , Begin ),
331331 After = binary :part (Data , End , byte_size (Data ) - End ),
332332 ok = file :write_file (
333- File ,
333+ LicenseFile ,
334334 [Before ,
335335 string :trim (Prefix , trailing ), LineEnding ,
336336 Prefix , " SPDX-License-Identifier: " , Spdx , LineEnding ,
@@ -351,7 +351,10 @@ update_copyright(File, [C | T]) ->
351351 [C | T ];
352352 false ->
353353 LastUpdatedYear = last_updated_year (File ,
354- fun () -> throw ({warn ," Could not get copyright year using git log. You need to update it manually." , []}) end ),
354+ fun () ->
355+ [throw (skip ) || is_ignored (File )],
356+ throw ({warn ," Could not get copyright year using git log. You need to update it manually." , []})
357+ end ),
355358 case string :equal (LastUpdatedYear , EndYear ) of
356359 true ->
357360 [C | T ];
@@ -387,11 +390,23 @@ first_updated_year(File, Missing) ->
387390 commit_year (File , Missing , first ).
388391
389392commit_year (File , Missing , When ) when When =:= first ; When =:= last , is_function (Missing )->
390- RFC3339Date =
391- cmd ([" git log --format=format:%aI" ,
393+
394+ Files = follow_renames (File ),
395+
396+ % % We use a as in author when looking for the first copyright
397+ % % and we use c as in committer when looking for the last.
398+ % % This is because with an --amend workflow the author date
399+ % % can be a long time in the past, but the committer is when
400+ % % the last change was done.
401+ Modifier = if When =:= first -> " a" ; When =:= last -> " c" end ,
402+
403+ Cmd = [" git log --format=format:%" ,Modifier ," I" ,
392404 [" --reverse" || When =:= first ],
393405 " --author='@erlang.org' --author='@ericsson.com'" ,
394- " --no-merges HEAD -- " , File , " | head -1" ]),
406+ " --no-merges HEAD -- " , [[" '" ,F ," '" ] || F <- Files ], " | head -1" ],
407+
408+ RFC3339Date = cmd (Cmd ),
409+
395410 try calendar :rfc3339_to_system_time (RFC3339Date ) of
396411 SystemTime ->
397412 {{YY , _ , _ }, _ } = calendar :system_time_to_local_time (SystemTime ,second ),
@@ -400,6 +415,35 @@ commit_year(File, Missing, When) when When =:= first; When =:= last, is_function
400415 Missing ()
401416 end .
402417
418+
419+ % % Because of problems with git log --follow we implement out own. An example
420+ % % where --follow has issues is for erts/emulator/test/erl_debugger_SUITE_data/gc_test.erl
421+ % % The reason why --follow fails is because gc_test.erl is identified as a copy of
422+ % % another file which is not true.
423+ % %
424+ % % This is all heuristic based so it might make mistakes...
425+ follow_renames (File ) ->
426+ follow_renames (File , " HEAD" ).
427+ follow_renames (File , Sha ) ->
428+
429+ maybe
430+
431+ LastCommit = cmd ([" git log '--format=format:%H' --no-merges " , Sha , " -- '" , File , " ' | tail -1" ]),
432+
433+ true ?= LastCommit =/= " " ,
434+
435+ RenameCmd = [" git diff-tree -r -M --name-status \" " ,LastCommit ," \" | grep '^R[0-9]\\ +.*" ,File ," $' | awk '{ print $2 }'" ],
436+
437+ Rename = cmd (RenameCmd ),
438+
439+ true ?= Rename =/= " " ,
440+
441+ [File | follow_renames (Rename , LastCommit )]
442+ else
443+ _ ->
444+ [File ]
445+ end .
446+
403447check_prefix (Prefix , Bin ) when is_binary (Bin ) ->
404448 case string :split (Bin , " \r\n " ) of
405449 [Bin ] ->
@@ -574,6 +618,7 @@ is_ignored(Filename) ->
574618 " ^.mailmap$" ,
575619 " ^OTP_VERSION$" ,
576620 " ^make/otp_patch_solve_forward_merge_version$" ,
621+ " ^make/otp_version_tickets_in_merge$" ,
577622 " ^make/otp_version_tickets$" ,
578623 " /configure$" ,
579624 " /config\\ .h\\ .in" ,
0 commit comments