1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2010-2013, University of Amsterdam, 7 VU University 8 Amsterdam All rights reserved. 9 10 Redistribution and use in source and binary forms, with or without 11 modification, are permitted provided that the following conditions 12 are met: 13 14 1. Redistributions of source code must retain the above copyright 15 notice, this list of conditions and the following disclaimer. 16 17 2. Redistributions in binary form must reproduce the above copyright 18 notice, this list of conditions and the following disclaimer in 19 the documentation and/or other materials provided with the 20 distribution. 21 22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 POSSIBILITY OF SUCH DAMAGE. 34*/ 35 36:- module(git, 37 [ git/2, % +Argv, +Options 38 git_process_output/3, % +Argv, :OnOutput, +Options 39 git_open_file/4, % +Dir, +File, +Branch, -Stream 40 is_git_directory/1, % +Dir 41 git_describe/2, % -Version, +Options 42 git_hash/2, % -Hash, +Options 43 git_ls_tree/2, % -Content, +Options 44 git_remote_url/3, % +Remote, -URL, +Options 45 git_ls_remote/3, % +GitURL, -Refs, +Options 46 git_branches/2, % -Branches, +Options 47 git_remote_branches/2, % +GitURL, -Branches 48 git_default_branch/2, % -DefaultBranch, +Options 49 git_tags_on_branch/3, % +Dir, +Branch, -Tags 50 git_shortlog/3, % +Dir, -Shortlog, +Options 51 git_log_data/3, % +Field, +Record, -Value 52 git_show/4, % +Dir, +Hash, -Commit, +Options 53 git_commit_data/3 % +Field, +Record, -Value 54 ]). 55:- use_module(library(record),[(record)/1,current_record/2, op(_,_,record)]). 56 57:- autoload(library(apply),[maplist/3]). 58:- autoload(library(error),[must_be/2,existence_error/2]). 59:- autoload(library(filesex), 60 [directory_file_path/3,relative_file_name/3]). 61:- autoload(library(lists),[append/3,member/2,append/2]). 62:- autoload(library(option),[option/2,option/3,select_option/3]). 63:- autoload(library(process),[process_create/3,process_wait/2]). 64:- autoload(library(readutil), 65 [ read_stream_to_codes/3, 66 read_line_to_codes/2, 67 read_stream_to_codes/2 68 ]). 69:- autoload(library(dcg/basics), 70 [string//1,whites//0,string_without//2,blanks//0]). 71 72 73:- meta_predicate 74 git_process_output( , , ).
87:- predicate_options(git/2, 2, 88 [ directory(atom), 89 error(-codes), 90 output(-codes), 91 status(-any), 92 askpass(any) 93 ]). 94:- predicate_options(git_default_branch/2, 2, 95 [ pass_to(git_process_output/3, 3) 96 ] ). 97:- predicate_options(git_describe/2, 2, 98 [ commit(atom), 99 directory(atom), 100 match(atom) 101 ]). 102:- predicate_options(git_hash/2, 2, 103 [ commit(atom), 104 directory(atom), 105 pass_to(git_process_output/3, 3) 106 ]). 107:- predicate_options(git_ls_tree/2, 2, 108 [ commit(atom), 109 directory(atom) 110 ]). 111:- predicate_options(git_process_output/3, 3, 112 [ directory(atom), 113 askpass(any), 114 error(-codes) 115 ]). 116:- predicate_options(git_remote_url/3, 3, 117 [ pass_to(git_process_output/3, 3) 118 ]). 119:- predicate_options(git_shortlog/3, 3, 120 [ revisions(atom), 121 limit(nonneg), 122 path(atom) 123 ]). 124:- predicate_options(git_show/4, 4, 125 [ diff(oneof([patch,stat])) 126 ]).
informational
.output(Out)
, but messages are printed at level error
.144git(Argv, Options) :- 145 git_cwd_options(Argv, Argv1, Options), 146 env_options(Extra, Options), 147 setup_call_cleanup( 148 process_create(path(git), Argv1, 149 [ stdout(pipe(Out)), 150 stderr(pipe(Error)), 151 process(PID) 152 | Extra 153 ]), 154 call_cleanup( 155 ( read_stream_to_codes(Out, OutCodes, []), 156 read_stream_to_codes(Error, ErrorCodes, []) 157 ), 158 process_wait(PID, Status)), 159 close_streams([Out,Error])), 160 print_error(ErrorCodes, Options), 161 print_output(OutCodes, Options), 162 ( option(status(Status0), Options) 163 -> Status = Status0 164 ; Status == exit(0) 165 -> true 166 ; throw(error(process_error(git(Argv), Status), _)) 167 ). 168 169git_cwd_options(Argv0, Argv, Options) :- 170 option(directory(Dir), Options), 171 !, 172 Argv = ['-C', file(Dir) | Argv0 ]. 173git_cwd_options(Argv, Argv, _). 174 175env_options([env(['GIT_ASKPASS'=Program])], Options) :- 176 option(askpass(Exe), Options), 177 !, 178 exe_options(ExeOptions), 179 absolute_file_name(Exe, PlProg, ExeOptions), 180 prolog_to_os_filename(PlProg, Program). 181env_options([], _). 182 183exe_options(Options) :- 184 current_prolog_flag(windows, true), 185 !, 186 Options = [ extensions(['',exe,com]), access(read) ]. 187exe_options(Options) :- 188 Options = [ access(execute) ]. 189 190print_output(OutCodes, Options) :- 191 option(output(Codes), Options), 192 !, 193 Codes = OutCodes. 194print_output([], _) :- !. 195print_output(OutCodes, _) :- 196 print_message(informational, git(output(OutCodes))). 197 198print_error(OutCodes, Options) :- 199 option(error(Codes), Options), 200 !, 201 Codes = OutCodes. 202print_error([], _) :- !. 203print_error(OutCodes, _) :- 204 phrase(classify_message(Level), OutCodes, _), 205 print_message(Level, git(output(OutCodes))). 206 207classify_message(error) --> 208 string(_), "fatal:", 209 !. 210classify_message(error) --> 211 string(_), "error:", 212 !. 213classify_message(warning) --> 214 string(_), "warning:", 215 !. 216classify_message(informational) --> 217 [].
224close_streams(List) :- 225 phrase(close_streams(List), Errors), 226 ( Errors = [Error|_] 227 -> throw(Error) 228 ; true 229 ). 230 231close_streams([H|T]) --> 232 { catch(close(H), E, true) }, 233 ( { var(E) } 234 -> [] 235 ; [E] 236 ), 237 close_streams(T).
call(OnOutput, Stream)
.245git_process_output(Argv, OnOutput, Options) :- 246 git_cwd_options(Argv, Argv1, Options), 247 env_options(Extra, Options), 248 setup_call_cleanup( 249 process_create(path(git), Argv1, 250 [ stdout(pipe(Out)), 251 stderr(pipe(Error)), 252 process(PID) 253 | Extra 254 ]), 255 call_cleanup( 256 ( call(OnOutput, Out), 257 read_stream_to_codes(Error, ErrorCodes, []) 258 ), 259 git_wait(PID, Out, Status)), 260 close_streams([Out,Error])), 261 print_error(ErrorCodes, Options), 262 ( Status = exit(0) 263 -> true 264 ; throw(error(process_error(git, Status))) 265 ). 266 267git_wait(PID, Out, Status) :- 268 at_end_of_stream(Out), 269 !, 270 process_wait(PID, Status). 271git_wait(PID, Out, Status) :- 272 setup_call_cleanup( 273 open_null_stream(Null), 274 copy_stream_data(Out, Null), 275 close(Null)), 276 process_wait(PID, Status).
286git_open_file(Dir, File, Branch, In) :-
287 atomic_list_concat([Branch, :, File], Ref),
288 process_create(path(git),
289 [ '-C', file(Dir), show, Ref ],
290 [ stdout(pipe(In))
291 ]),
292 set_stream(In, file_name(File)).
300is_git_directory(Directory) :- 301 directory_file_path(Directory, '.git', GitDir), 302 exists_directory(GitDir), 303 !. 304is_git_directory(Directory) :- 305 exists_directory(Directory), 306 git(['rev-parse', '--git-dir'], 307 [ output(Codes), 308 error(_), 309 status(Status), 310 directory(Directory) 311 ]), 312 Status == exit(0), 313 string_codes(GitDir0, Codes), 314 split_string(GitDir0, "", " \n", [GitDir]), 315 sub_string(GitDir, B, _, A, "/.git/modules/"), 316 !, 317 sub_string(GitDir, 0, B, _, Main), 318 sub_string(GitDir, _, A, 0, Below), 319 directory_file_path(Main, Below, Dir), 320 same_file(Dir, Directory).
V*
)HEAD
338git_describe(Version, Options) :- 339 ( option(match(Pattern), Options) 340 -> true 341 ; git_version_pattern(Pattern) 342 ), 343 ( option(commit(Commit), Options) 344 -> Extra = [Commit] 345 ; Extra = [] 346 ), 347 option(directory(Dir), Options, .), 348 setup_call_cleanup( 349 process_create(path(git), 350 [ 'describe', 351 '--match', Pattern 352 | Extra 353 ], 354 [ stdout(pipe(Out)), 355 stderr(null), 356 process(PID), 357 cwd(Dir) 358 ]), 359 call_cleanup( 360 read_stream_to_codes(Out, V0, []), 361 git_wait(PID, Out, Status)), 362 close(Out)), 363 Status = exit(0), 364 !, 365 atom_codes(V1, V0), 366 normalize_space(atom(Plain), V1), 367 ( git_is_clean(Dir) 368 -> Version = Plain 369 ; atom_concat(Plain, '-DIRTY', Version) 370 ). 371git_describe(Version, Options) :- 372 option(directory(Dir), Options, .), 373 option(commit(Commit), Options, 'HEAD'), 374 setup_call_cleanup( 375 process_create(path(git), 376 [ 'rev-parse', '--short', 377 Commit 378 ], 379 [ stdout(pipe(Out)), 380 stderr(null), 381 process(PID), 382 cwd(Dir) 383 ]), 384 call_cleanup( 385 read_stream_to_codes(Out, V0, []), 386 git_wait(PID, Out, Status)), 387 close(Out)), 388 Status = exit(0), 389 atom_codes(V1, V0), 390 normalize_space(atom(Plain), V1), 391 ( git_is_clean(Dir) 392 -> Version = Plain 393 ; atom_concat(Plain, '-DIRTY', Version) 394 ). 395 396 397:- multifile 398 git_version_pattern/1. 399 400git_version_pattern('V*'). 401git_version_pattern('*').
git diff
produces
no output.410git_is_clean(Dir) :- 411 setup_call_cleanup(process_create(path(git), ['diff', '--stat'], 412 [ stdout(pipe(Out)), 413 stderr(null), 414 cwd(Dir) 415 ]), 416 stream_char_count(Out, Count), 417 close(Out)), 418 Count == 0. 419 420stream_char_count(Out, Count) :- 421 setup_call_cleanup(open_null_stream(Null), 422 ( copy_stream_data(Out, Null), 423 character_count(Null, Count) 424 ), 425 close(Null)).
432git_hash(Hash, Options) :- 433 option(commit(Commit), Options, 'HEAD'), 434 git_process_output(['rev-parse', '--verify', Commit], 435 read_hash(Hash), 436 Options). 437 438read_hash(Hash, Stream) :- 439 read_line_to_codes(Stream, Line), 440 atom_codes(Hash, Line).
object(Mode, Type, Hash, Size, Name)
452git_ls_tree(Entries, Options) :- 453 option(commit(Commit), Options, 'HEAD'), 454 git_process_output(['ls-tree', '-z', '-r', '-l', Commit], 455 read_tree(Entries), 456 Options). 457 458read_tree(Entries, Stream) :- 459 read_stream_to_codes(Stream, Codes), 460 phrase(ls_tree(Entries), Codes). 461 462ls_tree([H|T]) --> 463 ls_entry(H), 464 !, 465 ls_tree(T). 466ls_tree([]) --> []. 467 468ls_entry(object(Mode, Type, Hash, Size, Name)) --> 469 string(MS), " ", 470 string(TS), " ", 471 string(HS), " ", 472 string(SS), "\t", 473 string(NS), [0], 474 !, 475 { number_codes(Mode, [0'0,0'o|MS]), 476 atom_codes(Type, TS), 477 atom_codes(Hash, HS), 478 ( Type == blob 479 -> number_codes(Size, SS) 480 ; Size = 0 % actually '-', but 0 sums easier 481 ), 482 atom_codes(Name, NS) 483 }.
490git_remote_url(Remote, URL, Options) :- 491 git_process_output([remote, show, Remote], 492 read_url("Fetch URL:", URL), 493 Options). 494 495read_url(Tag, URL, In) :- 496 repeat, 497 read_line_to_codes(In, Line), 498 ( Line == end_of_file 499 -> !, fail 500 ; phrase(url_codes(Tag, Codes), Line) 501 -> !, atom_codes(URL, Codes) 502 ). 503 504url_codes(Tag, Rest) --> 505 { string_codes(Tag, TagCodes) }, 506 whites, string(TagCodes), whites, string(Rest).
git ls-remote
against the remote repository to fetch
references from the remote. Options processed:
heads(Boolean)
tags(Boolean)
refs(List)
For example, to find the hash of the remote HEAD
, one can use
?- git_ls_remote('git://www.swi-prolog.org/home/pl/git/pl-devel.git', Refs, [refs(['HEAD'])]). Refs = ['5d596c52aa969d88e7959f86327f5c7ff23695f3'-'HEAD'].
528git_ls_remote(GitURL, Refs, Options) :- 529 findall(O, ls_remote_option(Options, O), RemoteOptions), 530 option(refs(LimitRefs), Options, []), 531 must_be(list(atom), LimitRefs), 532 append([ 'ls-remote' | RemoteOptions], [GitURL|LimitRefs], Argv), 533 git_process_output(Argv, remote_refs(Refs), Options). 534 535ls_remote_option(Options, '--heads') :- 536 option(heads(true), Options). 537ls_remote_option(Options, '--tags') :- 538 option(tags(true), Options). 539 540remote_refs(Refs, Out) :- 541 read_line_to_codes(Out, Line0), 542 remote_refs(Line0, Out, Refs). 543 544remote_refs(end_of_file, _, []) :- !. 545remote_refs(Line, Out, [Hash-Ref|Tail]) :- 546 phrase(remote_ref(Hash,Ref), Line), 547 read_line_to_codes(Out, Line1), 548 remote_refs(Line1, Out, Tail). 549 550remote_ref(Hash, Ref) --> 551 string_without("\t ", HashCodes), 552 whites, 553 string_without("\t ", RefCodes), 554 { atom_codes(Hash, HashCodes), 555 atom_codes(Ref, RefCodes) 556 }.
564git_remote_branches(GitURL, Branches) :-
565 git_ls_remote(GitURL, Refs, [heads(true)]),
566 findall(B, (member(_-Head, Refs),
567 atom_concat('refs/heads/', B, Head)),
568 Branches).
575git_default_branch(BranchName, Options) :- 576 git_process_output([branch], 577 read_default_branch(BranchName), 578 Options). 579 580read_default_branch(BranchName, In) :- 581 repeat, 582 read_line_to_codes(In, Line), 583 ( Line == end_of_file 584 -> !, fail 585 ; phrase(default_branch(Codes), Line) 586 -> !, atom_codes(BranchName, Codes) 587 ). 588 589default_branch(Rest) --> 590 "*", whites, string(Rest).
600git_branches(Branches, Options) :- 601 ( select_option(commit(Commit), Options, GitOptions) 602 -> Extra = ['--contains', Commit] 603 ; Extra = [], 604 GitOptions = Options 605 ), 606 git_process_output([branch|Extra], 607 read_branches(Branches), 608 GitOptions). 609 610read_branches(Branches, In) :- 611 read_line_to_codes(In, Line), 612 ( Line == end_of_file 613 -> Branches = [] 614 ; Line = [_,_|Codes], 615 atom_codes(H, Codes), 616 Branches = [H|T], 617 read_branches(T, In) 618 ).
628git_tags_on_branch(Dir, Branch, Tags) :- 629 git_process_output([ log, '--oneline', '--decorate', Branch ], 630 log_to_tags(Tags), 631 [ directory(Dir) ]). 632 Tags, Out) (:- 634 read_line_to_codes(Out, Line0), 635 log_to_tags(Line0, Out, Tags, []). 636 _, Tags, Tags) (end_of_file, :- !. 638log_to_tags(Line, Out, Tags, Tail) :- 639 phrase(tags_on_line(Tags, Tail1), Line), 640 read_line_to_codes(Out, Line1), 641 log_to_tags(Line1, Out, Tail1, Tail). 642 Tags, Tail) (--> 644 string_without(" ", _Hash), 645 tags(Tags, Tail), 646 skip_rest. 647 Tags, Tail) (--> 649 whites, 650 "(", 651 tag_list(Tags, Rest), 652 !, 653 tags(Rest, Tail). 654tags(Tags, Tags) --> 655 skip_rest. 656 657tag_list([H|T], Rest) --> 658 "tag:", !, whites, 659 string(Codes), 660 ( ")" 661 -> { atom_codes(H, Codes), 662 T = Rest 663 } 664 ; "," 665 -> { atom_codes(H, Codes) 666 }, 667 whites, 668 tag_list(T, Rest) 669 ). 670tag_list(List, Rest) --> 671 string(_), 672 ( ")" 673 -> { List = Rest } 674 ; "," 675 -> whites, 676 tag_list(List, Rest) 677 ). 678 679skip_rest(_,_). 680 681 682 /******************************* 683 * READ GIT HISTORY * 684 *******************************/
path
, but Path is relative to the repository.703:- record 704 git_log(commit_hash:atom, 705 author_name:atom, 706 author_date_relative:atom, 707 committer_name:atom, 708 committer_date_relative:atom, 709 committer_date_unix:integer, 710 subject:atom, 711 ref_names:list). 712 713git_shortlog(Dir, ShortLog, Options) :- 714 ( option(revisions(Range), Options) 715 -> RangeSpec = [Range] 716 ; option(limit(Limit), Options, 10), 717 RangeSpec = ['-n', Limit] 718 ), 719 ( option(git_path(Path), Options) 720 -> Extra = ['--', Path] 721 ; option(path(Path), Options) 722 -> relative_file_name(Path, Dir, RelPath), 723 Extra = ['--', RelPath] 724 ; Extra = [] 725 ), 726 git_format_string(git_log, Fields, Format), 727 append([[log, Format], RangeSpec, Extra], GitArgv), 728 git_process_output(GitArgv, 729 read_git_formatted(git_log, Fields, ShortLog), 730 [directory(Dir)]). 731 732 733read_git_formatted(Record, Fields, ShortLog, In) :- 734 read_line_to_codes(In, Line0), 735 read_git_formatted(Line0, In, Record, Fields, ShortLog). 736 737read_git_formatted(end_of_file, _, _, _, []) :- !. 738read_git_formatted(Line, In, Record, Fields, [H|T]) :- 739 record_from_line(Record, Fields, Line, H), 740 read_line_to_codes(In, Line1), 741 read_git_formatted(Line1, In, Record, Fields, T). 742 743record_from_line(RecordName, Fields, Line, Record) :- 744 phrase(fields_from_line(Fields, Values), Line), 745 Record =.. [RecordName|Values]. 746 747fields_from_line([], []) --> []. 748fields_from_line([F|FT], [V|VT]) --> 749 to_nul_s(Codes), 750 { field_to_prolog(F, Codes, V) }, 751 fields_from_line(FT, VT). 752 753to_nul_s([]) --> [0], !. 754to_nul_s([H|T]) --> [H], to_nul_s(T). 755 756field_to_prolog(ref_names, Line, List) :- 757 phrase(ref_names(List), Line), 758 !. 759field_to_prolog(committer_date_unix, Line, Stamp) :- 760 !, 761 number_codes(Stamp, Line). 762field_to_prolog(_, Line, Atom) :- 763 atom_codes(Atom, Line). 764 765ref_names([]) --> []. 766ref_names(List) --> 767 blanks, "(", ref_name_list(List), ")". 768 769ref_name_list([H|T]) --> 770 string_without(",)", Codes), 771 { atom_codes(H, Codes) }, 772 ( ",", blanks 773 -> ref_name_list(T) 774 ; {T=[]} 775 ).
stat
791:- record 792 git_commit(tree_hash:atom, 793 parent_hashes:list, 794 author_name:atom, 795 author_date:atom, 796 committer_name:atom, 797 committer_date:atom, 798 subject:atom). 799 800git_show(Dir, Hash, Commit, Options) :- 801 git_format_string(git_commit, Fields, Format), 802 option(diff(Diff), Options, patch), 803 diff_arg(Diff, DiffArg), 804 git_process_output([ show, DiffArg, Hash, Format ], 805 read_commit(Fields, Commit, Options), 806 [directory(Dir)]). 807 808diff_arg(patch, '-p'). 809diff_arg(stat, '--stat'). 810 811read_commit(Fields, Data-Body, Options, In) :- 812 read_line_to_codes(In, Line1), 813 record_from_line(git_commit, Fields, Line1, Data), 814 read_line_to_codes(In, Line2), 815 ( Line2 == [] 816 -> option(max_lines(Max), Options, -1), 817 read_n_lines(In, Max, Body) 818 ; Line2 == end_of_file 819 -> Body = [] 820 ). 821 822read_n_lines(In, Max, Lines) :- 823 read_line_to_codes(In, Line1), 824 read_n_lines(Line1, Max, In, Lines). 825 826read_n_lines(end_of_file, _, _, []) :- !. 827read_n_lines(_, 0, In, []) :- 828 !, 829 setup_call_cleanup(open_null_stream(Out), 830 copy_stream_data(In, Out), 831 close(Out)). 832read_n_lines(Line, Max0, In, [Line|More]) :- 833 read_line_to_codes(In, Line2), 834 Max is Max0-1, 835 read_n_lines(Line2, Max, In, More).
--format=
argument with
the appropriate format-specifiers, terminated by %x00, which
causes the actual field to be 0-terminated.845:- meta_predicate 846 git_format_string( , , ). 847 848git_format_string(M:RecordName, Fields, Format) :- 849 current_record(RecordName, M:Term), 850 findall(F, record_field(Term, F), Fields), 851 maplist(git_field_format, Fields, Formats), 852 atomic_list_concat(['--format='|Formats], Format). 853 854record_field(Term, Name) :- 855 arg(_, Term, Field), 856 field_name(Field, Name). 857 858field_name(Name:_Type=_Default, Name) :- !. 859field_name(Name:_Type, Name) :- !. 860field_name(Name=_Default, Name) :- !. 861field_name(Name, Name). 862 863git_field_format(Field, Fmt) :- 864 ( git_format(NoPercent, Field) 865 -> atomic_list_concat(['%', NoPercent, '%x00'], Fmt) 866 ; existence_error(git_format, Field) 867 ). 868 869git_format('H', commit_hash). 870git_format('h', abbreviated_commit_hash). 871git_format('T', tree_hash). 872git_format('t', abbreviated_tree_hash). 873git_format('P', parent_hashes). 874git_format('p', abbreviated_parent_hashes). 875 876git_format('an', author_name). 877git_format('aN', author_name_mailcap). 878git_format('ae', author_email). 879git_format('aE', author_email_mailcap). 880git_format('ad', author_date). 881git_format('aD', author_date_rfc2822). 882git_format('ar', author_date_relative). 883git_format('at', author_date_unix). 884git_format('ai', author_date_iso8601). 885 886git_format('cn', committer_name). 887git_format('cN', committer_name_mailcap). 888git_format('ce', committer_email). 889git_format('cE', committer_email_mailcap). 890git_format('cd', committer_date). 891git_format('cD', committer_date_rfc2822). 892git_format('cr', committer_date_relative). 893git_format('ct', committer_date_unix). 894git_format('ci', committer_date_iso8601). 895 896git_format('d', ref_names). % git log? 897git_format('e', encoding). % git log? 898 899git_format('s', subject). 900git_format('f', subject_sanitized). 901git_format('b', body). 902git_format('N', notes). 903 904git_format('gD', reflog_selector). 905git_format('gd', shortened_reflog_selector). 906git_format('gs', reflog_subject). 907 908 909 /******************************* 910 * MESSAGES * 911 *******************************/ 912 913:- multifile 914 prolog:message//1. 915 916prologmessage(git(output(Codes))) --> 917 { split_lines(Codes, Lines) }, 918 git_lines(Lines). 919 920git_lines([]) --> []. 921git_lines([H|T]) --> 922 [ '~s'-[H] ], 923 ( {T==[]} 924 -> [] 925 ; [nl], git_lines(T) 926 ). 927 928split_lines([], []) :- !. 929split_lines(All, [Line1|More]) :- 930 append(Line1, [0'\n|Rest], All), 931 !, 932 split_lines(Rest, More). 933split_lines(Line, [Line])
Run GIT commands
This module performs common GIT tasks by calling git as a remote process through process_create/3. It requires that the
git
executable is in the current PATH.This module started life in ClioPatria and has been used by the Prolog web-server to provide information on git repositories. It is now moved into the core Prolog library to support the Prolog package manager. */