@@ -271,12 +271,12 @@ check_file(File, LargestLicense, Templates, VendorPaths, Opts) ->
271271 Data = read (File , LargestLicense * 3 ),
272272 case re :run (Data , " (.* )?%CopyrightBegin%(?:\r\n |\n )" ,[]) of
273273 {match , [StartPos | PrefixPos ]} ->
274- check_file_header (File , File , Data , StartPos , PrefixPos , Templates , Opts );
274+ check_file_header (File , File , Data , StartPos , PrefixPos , Templates , VendorPaths , Opts );
275275 nomatch ->
276276 maybe
277277 {ok , LicData } ?= file :read_file (File ++ " .license" ),
278278 {match , [StartPos | PrefixPos ]} ?= re :run (LicData , " (.* )?%CopyrightBegin%(?:\r\n |\n )" ,[]),
279- check_file_header (File , File ++ " .license" , LicData , StartPos , PrefixPos , Templates , Opts )
279+ check_file_header (File , File ++ " .license" , LicData , StartPos , PrefixPos , Templates , VendorPaths , Opts )
280280 else
281281 _ ->
282282 ReportMissing = not is_ignored (File ) andalso
@@ -300,11 +300,11 @@ check_file(File, LargestLicense, Templates, VendorPaths, Opts) ->
300300 erlang :raise (E ,R ,ST )
301301 end .
302302
303- check_file_header (File , LicenseFile , Data , StartPos , [], Templates , Opts ) ->
304- check_file_header (File , LicenseFile , Data , StartPos , <<>>, Templates , Opts );
305- check_file_header (File , LicenseFile , Data , StartPos , [PrefixPos ], Templates , Opts ) ->
306- check_file_header (File , LicenseFile , Data , StartPos , binary :part (Data , PrefixPos ), Templates , Opts );
307- check_file_header (File , LicenseFile , Data , {Start , StartEnd }, Prefix , Templates , Opts ) ->
303+ check_file_header (File , LicenseFile , Data , StartPos , [], Templates , VendorPaths , Opts ) ->
304+ check_file_header (File , LicenseFile , Data , StartPos , <<>>, Templates , VendorPaths , Opts );
305+ check_file_header (File , LicenseFile , Data , StartPos , [PrefixPos ], Templates , VendorPaths , Opts ) ->
306+ check_file_header (File , LicenseFile , Data , StartPos , binary :part (Data , PrefixPos ), Templates , VendorPaths , Opts );
307+ check_file_header (File , LicenseFile , Data , {Start , StartEnd }, Prefix , Templates , VendorPaths , Opts ) ->
308308 case re :run (Data , [" \\ Q" , Prefix , " \\ E%CopyrightEnd%(\r\n |\n )" ],[]) of
309309 {match , [{End , EndPos },{_ ,NlSize }]} ->
310310 DataAfterHeader = binary :part (Data , End + EndPos , byte_size (Data ) - (End + EndPos )),
@@ -315,8 +315,8 @@ check_file_header(File, LicenseFile, Data, {Start, StartEnd}, Prefix, Templates,
315315 check_license (License , Spdx , Templates ,
316316 length (string :split (DataAfterHeader ," \n " ,all )),
317317 File , not string :equal (File , LicenseFile ), Opts ),
318- case maps :get (update , Opts , false ) of
319- true -> update_copyright (LicenseFile , Start + StartEnd , End , Prefix , LineEnding , Spdx , Copyrights , License );
318+ case maps :get (update , Opts , false ) andalso not is_vendored ( File , VendorPaths ) of
319+ true -> update_copyright (File , LicenseFile , Start + StartEnd , End , Prefix , LineEnding , Spdx , Copyrights , License );
320320 false -> ok
321321 end ;
322322 nomatch when map_get (verbose , Opts ) ->
@@ -325,15 +325,15 @@ check_file_header(File, LicenseFile, Data, {Start, StartEnd}, Prefix, Templates,
325325 throw ({warn , " Could not find '~ts %CopyrightEnd%'" , [Prefix ]})
326326 end .
327327
328- update_copyright (File , Begin , End , Prefix , LineEnding , Spdx , Copyrights , License ) ->
328+ update_copyright (File , LicenseFile , Begin , End , Prefix , LineEnding , Spdx , Copyrights , License ) ->
329329 case update_copyright (File , Copyrights ) of
330330 Copyrights -> ok ;
331331 NewCopyrights ->
332- {ok , Data } = file :read_file (File ),
332+ {ok , Data } = file :read_file (LicenseFile ),
333333 Before = binary :part (Data , 0 , Begin ),
334334 After = binary :part (Data , End , byte_size (Data ) - End ),
335335 ok = file :write_file (
336- File ,
336+ LicenseFile ,
337337 [Before ,
338338 string :trim (Prefix , trailing ), LineEnding ,
339339 Prefix , " SPDX-License-Identifier: " , Spdx , LineEnding ,
@@ -354,7 +354,10 @@ update_copyright(File, [C | T]) ->
354354 [C | T ];
355355 false ->
356356 LastUpdatedYear = last_updated_year (File ,
357- fun () -> throw ({warn ," Could not get copyright year using git log. You need to update it manually." , []}) end ),
357+ fun () ->
358+ [throw (skip ) || is_ignored (File )],
359+ throw ({warn ," Could not get copyright year using git log. You need to update it manually." , []})
360+ end ),
358361 case string :equal (LastUpdatedYear , EndYear ) of
359362 true ->
360363 [C | T ];
@@ -390,11 +393,23 @@ first_updated_year(File, Missing) ->
390393 commit_year (File , Missing , first ).
391394
392395commit_year (File , Missing , When ) when When =:= first ; When =:= last , is_function (Missing )->
393- RFC3339Date =
394- cmd ([" git log --format=format:%aI" ,
396+
397+ Files = follow_renames (File ),
398+
399+ % % We use a as in author when looking for the first copyright
400+ % % and we use c as in committer when looking for the last.
401+ % % This is because with an --amend workflow the author date
402+ % % can be a long time in the past, but the committer is when
403+ % % the last change was done.
404+ Modifier = if When =:= first -> " a" ; When =:= last -> " c" end ,
405+
406+ Cmd = [" git log --format=format:%" ,Modifier ," I" ,
395407 [" --reverse" || When =:= first ],
396408 " --author='@erlang.org' --author='@ericsson.com'" ,
397- " --no-merges HEAD -- " , File , " | head -1" ]),
409+ " --no-merges HEAD -- " , [[" '" ,F ," '" ] || F <- Files ], " | head -1" ],
410+
411+ RFC3339Date = cmd (Cmd ),
412+
398413 try calendar :rfc3339_to_system_time (RFC3339Date ) of
399414 SystemTime ->
400415 {{YY , _ , _ }, _ } = calendar :system_time_to_local_time (SystemTime ,second ),
@@ -403,6 +418,35 @@ commit_year(File, Missing, When) when When =:= first; When =:= last, is_function
403418 Missing ()
404419 end .
405420
421+
422+ % % Because of problems with git log --follow we implement out own. An example
423+ % % where --follow has issues is for erts/emulator/test/erl_debugger_SUITE_data/gc_test.erl
424+ % % The reason why --follow fails is because gc_test.erl is identified as a copy of
425+ % % another file which is not true.
426+ % %
427+ % % This is all heuristic based so it might make mistakes...
428+ follow_renames (File ) ->
429+ follow_renames (File , " HEAD" ).
430+ follow_renames (File , Sha ) ->
431+
432+ maybe
433+
434+ LastCommit = cmd ([" git log '--format=format:%H' --no-merges " , Sha , " -- '" , File , " ' | tail -1" ]),
435+
436+ true ?= LastCommit =/= " " ,
437+
438+ RenameCmd = [" git diff-tree -r -M --name-status \" " ,LastCommit ," \" | grep '^R[0-9]\\ +.*" ,File ," $' | awk '{ print $2 }'" ],
439+
440+ Rename = cmd (RenameCmd ),
441+
442+ true ?= Rename =/= " " ,
443+
444+ [File | follow_renames (Rename , LastCommit )]
445+ else
446+ _ ->
447+ [File ]
448+ end .
449+
406450check_prefix (Prefix , Bin ) when is_binary (Bin ) ->
407451 case string :split (Bin , " \r\n " ) of
408452 [Bin ] ->
@@ -577,6 +621,7 @@ is_ignored(Filename) ->
577621 " ^.mailmap$" ,
578622 " ^OTP_VERSION$" ,
579623 " ^make/otp_patch_solve_forward_merge_version$" ,
624+ " ^make/otp_version_tickets_in_merge$" ,
580625 " ^make/otp_version_tickets$" ,
581626 " /configure$" ,
582627 " /config\\ .h\\ .in" ,
0 commit comments