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(process)). 56:- use_module(library(readutil)). 57:- use_module(library(option)). 58:- use_module(library(dcg/basics)). 59:- use_module(library(record)). 60:- use_module(library(lists)). 61:- use_module(library(error)). 62 63:- meta_predicate 64 git_process_output( , , ).
77:- predicate_options(git/2, 2, 78 [ directory(atom), 79 error(-codes), 80 output(-codes), 81 status(-any), 82 askpass(any) 83 ]). 84:- predicate_options(git_default_branch/2, 2, 85 [ pass_to(git_process_output/3, 3) 86 ] ). 87:- predicate_options(git_describe/2, 2, 88 [ commit(atom), 89 directory(atom), 90 match(atom) 91 ]). 92:- predicate_options(git_hash/2, 2, 93 [ commit(atom), 94 directory(atom) 95 ]). 96:- predicate_options(git_ls_tree/2, 2, 97 [ commit(atom), 98 directory(atom) 99 ]). 100:- predicate_options(git_process_output/3, 3, 101 [ directory(atom), 102 askpass(any), 103 error(-codes) 104 ]). 105:- predicate_options(git_remote_url/3, 3, 106 [ pass_to(git_process_output/3, 3) 107 ]). 108:- predicate_options(git_shortlog/3, 3, 109 [ revisions(atom), 110 limit(nonneg), 111 path(atom) 112 ]). 113:- predicate_options(git_show/4, 4, 114 [ diff(oneof([patch,stat])) 115 ]).
informational
.output(Out)
, but messages are printed at level error
.133git(Argv, Options) :- 134 git_cwd_options(Argv, Argv1, Options), 135 env_options(Extra, Options), 136 setup_call_cleanup( 137 process_create(path(git), Argv1, 138 [ stdout(pipe(Out)), 139 stderr(pipe(Error)), 140 process(PID) 141 | Extra 142 ]), 143 call_cleanup( 144 ( read_stream_to_codes(Out, OutCodes, []), 145 read_stream_to_codes(Error, ErrorCodes, []) 146 ), 147 process_wait(PID, Status)), 148 close_streams([Out,Error])), 149 print_error(ErrorCodes, Options), 150 print_output(OutCodes, Options), 151 ( option(status(Status0), Options) 152 -> Status = Status0 153 ; Status == exit(0) 154 -> true 155 ; throw(error(process_error(git(Argv), Status), _)) 156 ). 157 158git_cwd_options(Argv0, Argv, Options) :- 159 option(directory(Dir), Options), 160 !, 161 Argv = ['-C', file(Dir) | Argv0 ]. 162git_cwd_options(Argv, Argv, _). 163 164env_options([env(['GIT_ASKPASS'=Program])], Options) :- 165 option(askpass(Exe), Options), 166 !, 167 exe_options(ExeOptions), 168 absolute_file_name(Exe, PlProg, ExeOptions), 169 prolog_to_os_filename(PlProg, Program). 170env_options([], _). 171 172exe_options(Options) :- 173 current_prolog_flag(windows, true), 174 !, 175 Options = [ extensions(['',exe,com]), access(read) ]. 176exe_options(Options) :- 177 Options = [ access(execute) ]. 178 179print_output(OutCodes, Options) :- 180 option(output(Codes), Options), 181 !, 182 Codes = OutCodes. 183print_output([], _) :- !. 184print_output(OutCodes, _) :- 185 print_message(informational, git(output(OutCodes))). 186 187print_error(OutCodes, Options) :- 188 option(error(Codes), Options), 189 !, 190 Codes = OutCodes. 191print_error([], _) :- !. 192print_error(OutCodes, _) :- 193 phrase(classify_message(Level), OutCodes, _), 194 print_message(Level, git(output(OutCodes))). 195 196classify_message(error) --> 197 string(_), "fatal:", 198 !. 199classify_message(error) --> 200 string(_), "error:", 201 !. 202classify_message(warning) --> 203 string(_), "warning:", 204 !. 205classify_message(informational) --> 206 [].
213close_streams(List) :- 214 phrase(close_streams(List), Errors), 215 ( Errors = [Error|_] 216 -> throw(Error) 217 ; true 218 ). 219 220close_streams([H|T]) --> 221 { catch(close(H), E, true) }, 222 ( { var(E) } 223 -> [] 224 ; [E] 225 ), 226 close_streams(T).
call(OnOutput, Stream)
.234git_process_output(Argv, OnOutput, Options) :- 235 git_cwd_options(Argv, Argv1, Options), 236 env_options(Extra, Options), 237 setup_call_cleanup( 238 process_create(path(git), Argv1, 239 [ stdout(pipe(Out)), 240 stderr(pipe(Error)), 241 process(PID) 242 | Extra 243 ]), 244 call_cleanup( 245 ( call(OnOutput, Out), 246 read_stream_to_codes(Error, ErrorCodes, []) 247 ), 248 git_wait(PID, Out, Status)), 249 close_streams([Out,Error])), 250 print_error(ErrorCodes, Options), 251 ( Status = exit(0) 252 -> true 253 ; throw(error(process_error(git, Status))) 254 ). 255 256git_wait(PID, Out, Status) :- 257 at_end_of_stream(Out), 258 !, 259 process_wait(PID, Status). 260git_wait(PID, Out, Status) :- 261 setup_call_cleanup( 262 open_null_stream(Null), 263 copy_stream_data(Out, Null), 264 close(Null)), 265 process_wait(PID, Status).
275git_open_file(Dir, File, Branch, In) :-
276 atomic_list_concat([Branch, :, File], Ref),
277 process_create(path(git),
278 [ '-C', file(Dir), show, Ref ],
279 [ stdout(pipe(In))
280 ]),
281 set_stream(In, file_name(File)).
289is_git_directory(Directory) :- 290 directory_file_path(Directory, '.git', GitDir), 291 exists_directory(GitDir), 292 !. 293is_git_directory(Directory) :- 294 exists_directory(Directory), 295 git(['rev-parse', '--git-dir'], 296 [ output(Codes), 297 error(_), 298 status(Status), 299 directory(Directory) 300 ]), 301 Status == exit(0), 302 string_codes(".\n", Codes).
V*
)HEAD
320git_describe(Version, Options) :- 321 ( option(match(Pattern), Options) 322 -> true 323 ; git_version_pattern(Pattern) 324 ), 325 ( option(commit(Commit), Options) 326 -> Extra = [Commit] 327 ; Extra = [] 328 ), 329 option(directory(Dir), Options, .), 330 setup_call_cleanup( 331 process_create(path(git), 332 [ 'describe', 333 '--match', Pattern 334 | Extra 335 ], 336 [ stdout(pipe(Out)), 337 stderr(null), 338 process(PID), 339 cwd(Dir) 340 ]), 341 call_cleanup( 342 read_stream_to_codes(Out, V0, []), 343 git_wait(PID, Out, Status)), 344 close(Out)), 345 Status = exit(0), 346 !, 347 atom_codes(V1, V0), 348 normalize_space(atom(Plain), V1), 349 ( git_is_clean(Dir) 350 -> Version = Plain 351 ; atom_concat(Plain, '-DIRTY', Version) 352 ). 353git_describe(Version, Options) :- 354 option(directory(Dir), Options, .), 355 option(commit(Commit), Options, 'HEAD'), 356 setup_call_cleanup( 357 process_create(path(git), 358 [ 'rev-parse', '--short', 359 Commit 360 ], 361 [ stdout(pipe(Out)), 362 stderr(null), 363 process(PID), 364 cwd(Dir) 365 ]), 366 call_cleanup( 367 read_stream_to_codes(Out, V0, []), 368 git_wait(PID, Out, Status)), 369 close(Out)), 370 Status = exit(0), 371 atom_codes(V1, V0), 372 normalize_space(atom(Plain), V1), 373 ( git_is_clean(Dir) 374 -> Version = Plain 375 ; atom_concat(Plain, '-DIRTY', Version) 376 ). 377 378 379:- multifile 380 git_version_pattern/1. 381 382git_version_pattern('V*'). 383git_version_pattern('*').
git diff
produces
no output.392git_is_clean(Dir) :- 393 setup_call_cleanup(process_create(path(git), ['diff', '--stat'], 394 [ stdout(pipe(Out)), 395 stderr(null), 396 cwd(Dir) 397 ]), 398 stream_char_count(Out, Count), 399 close(Out)), 400 Count == 0. 401 402stream_char_count(Out, Count) :- 403 setup_call_cleanup(open_null_stream(Null), 404 ( copy_stream_data(Out, Null), 405 character_count(Null, Count) 406 ), 407 close(Null)).
414git_hash(Hash, Options) :- 415 option(commit(Commit), Options, 'HEAD'), 416 git_process_output(['rev-parse', '--verify', Commit], 417 read_hash(Hash), 418 Options). 419 420read_hash(Hash, Stream) :- 421 read_line_to_codes(Stream, Line), 422 atom_codes(Hash, Line).
object(Mode, Type, Hash, Size, Name)
434git_ls_tree(Entries, Options) :- 435 option(commit(Commit), Options, 'HEAD'), 436 git_process_output(['ls-tree', '-z', '-r', '-l', Commit], 437 read_tree(Entries), 438 Options). 439 440read_tree(Entries, Stream) :- 441 read_stream_to_codes(Stream, Codes), 442 phrase(ls_tree(Entries), Codes). 443 444ls_tree([H|T]) --> 445 ls_entry(H), 446 !, 447 ls_tree(T). 448ls_tree([]) --> []. 449 450ls_entry(object(Mode, Type, Hash, Size, Name)) --> 451 string(MS), " ", 452 string(TS), " ", 453 string(HS), " ", 454 string(SS), "\t", 455 string(NS), [0], 456 !, 457 { number_codes(Mode, [0'0,0'o|MS]), 458 atom_codes(Type, TS), 459 atom_codes(Hash, HS), 460 ( Type == blob 461 -> number_codes(Size, SS) 462 ; Size = 0 % actually '-', but 0 sums easier 463 ), 464 atom_codes(Name, NS) 465 }.
472git_remote_url(Remote, URL, Options) :- 473 git_process_output([remote, show, Remote], 474 read_url("Fetch URL:", URL), 475 Options). 476 477read_url(Tag, URL, In) :- 478 repeat, 479 read_line_to_codes(In, Line), 480 ( Line == end_of_file 481 -> !, fail 482 ; phrase(url_codes(Tag, Codes), Line) 483 -> !, atom_codes(URL, Codes) 484 ). 485 486url_codes(Tag, Rest) --> 487 { string_codes(Tag, TagCodes) }, 488 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'].
510git_ls_remote(GitURL, Refs, Options) :- 511 findall(O, ls_remote_option(Options, O), RemoteOptions), 512 option(refs(LimitRefs), Options, []), 513 must_be(list(atom), LimitRefs), 514 append([ 'ls-remote' | RemoteOptions], [GitURL|LimitRefs], Argv), 515 git_process_output(Argv, remote_refs(Refs), []). 516 517ls_remote_option(Options, '--heads') :- 518 option(heads(true), Options). 519ls_remote_option(Options, '--tags') :- 520 option(tags(true), Options). 521 522remote_refs(Refs, Out) :- 523 read_line_to_codes(Out, Line0), 524 remote_refs(Line0, Out, Refs). 525 526remote_refs(end_of_file, _, []) :- !. 527remote_refs(Line, Out, [Hash-Ref|Tail]) :- 528 phrase(remote_ref(Hash,Ref), Line), 529 read_line_to_codes(Out, Line1), 530 remote_refs(Line1, Out, Tail). 531 532remote_ref(Hash, Ref) --> 533 string_without("\t ", HashCodes), 534 whites, 535 string_without("\t ", RefCodes), 536 { atom_codes(Hash, HashCodes), 537 atom_codes(Ref, RefCodes) 538 }.
546git_remote_branches(GitURL, Branches) :-
547 git_ls_remote(GitURL, Refs, [heads(true)]),
548 findall(B, (member(_-Head, Refs),
549 atom_concat('refs/heads/', B, Head)),
550 Branches).
557git_default_branch(BranchName, Options) :- 558 git_process_output([branch], 559 read_default_branch(BranchName), 560 Options). 561 562read_default_branch(BranchName, In) :- 563 repeat, 564 read_line_to_codes(In, Line), 565 ( Line == end_of_file 566 -> !, fail 567 ; phrase(default_branch(Codes), Line) 568 -> !, atom_codes(BranchName, Codes) 569 ). 570 571default_branch(Rest) --> 572 "*", whites, string(Rest).
582git_branches(Branches, Options) :- 583 ( select_option(commit(Commit), Options, GitOptions) 584 -> Extra = ['--contains', Commit] 585 ; Extra = [], 586 GitOptions = Options 587 ), 588 git_process_output([branch|Extra], 589 read_branches(Branches), 590 GitOptions). 591 592read_branches(Branches, In) :- 593 read_line_to_codes(In, Line), 594 ( Line == end_of_file 595 -> Branches = [] 596 ; Line = [_,_|Codes], 597 atom_codes(H, Codes), 598 Branches = [H|T], 599 read_branches(T, In) 600 ).
610git_tags_on_branch(Dir, Branch, Tags) :- 611 git_process_output([ log, '--oneline', '--decorate', Branch ], 612 log_to_tags(Tags), 613 [ directory(Dir) ]). 614 Tags, Out) (:- 616 read_line_to_codes(Out, Line0), 617 log_to_tags(Line0, Out, Tags, []). 618 _, Tags, Tags) (end_of_file, :- !. 620log_to_tags(Line, Out, Tags, Tail) :- 621 phrase(tags_on_line(Tags, Tail1), Line), 622 read_line_to_codes(Out, Line1), 623 log_to_tags(Line1, Out, Tail1, Tail). 624 Tags, Tail) (--> 626 string_without(" ", _Hash), 627 tags(Tags, Tail), 628 skip_rest. 629 Tags, Tail) (--> 631 whites, 632 "(", 633 tag_list(Tags, Rest), 634 !, 635 tags(Rest, Tail). 636tags(Tags, Tags) --> 637 skip_rest. 638 639tag_list([H|T], Rest) --> 640 "tag:", !, whites, 641 string(Codes), 642 ( ")" 643 -> { atom_codes(H, Codes), 644 T = Rest 645 } 646 ; "," 647 -> { atom_codes(H, Codes) 648 }, 649 whites, 650 tag_list(T, Rest) 651 ). 652tag_list(List, Rest) --> 653 string(_), 654 ( ")" 655 -> { List = Rest } 656 ; "," 657 -> whites, 658 tag_list(List, Rest) 659 ). 660 661skip_rest(_,_). 662 663 664 /******************************* 665 * READ GIT HISTORY * 666 *******************************/
path
, but Path is relative to the repository.685:- record 686 git_log(commit_hash:atom, 687 author_name:atom, 688 author_date_relative:atom, 689 committer_name:atom, 690 committer_date_relative:atom, 691 committer_date_unix:integer, 692 subject:atom, 693 ref_names:list). 694 695git_shortlog(Dir, ShortLog, Options) :- 696 ( option(revisions(Range), Options) 697 -> RangeSpec = [Range] 698 ; option(limit(Limit), Options, 10), 699 RangeSpec = ['-n', Limit] 700 ), 701 ( option(git_path(Path), Options) 702 -> Extra = ['--', Path] 703 ; option(path(Path), Options) 704 -> relative_file_name(Path, Dir, RelPath), 705 Extra = ['--', RelPath] 706 ; Extra = [] 707 ), 708 git_format_string(git_log, Fields, Format), 709 append([[log, Format], RangeSpec, Extra], GitArgv), 710 git_process_output(GitArgv, 711 read_git_formatted(git_log, Fields, ShortLog), 712 [directory(Dir)]). 713 714 715read_git_formatted(Record, Fields, ShortLog, In) :- 716 read_line_to_codes(In, Line0), 717 read_git_formatted(Line0, In, Record, Fields, ShortLog). 718 719read_git_formatted(end_of_file, _, _, _, []) :- !. 720read_git_formatted(Line, In, Record, Fields, [H|T]) :- 721 record_from_line(Record, Fields, Line, H), 722 read_line_to_codes(In, Line1), 723 read_git_formatted(Line1, In, Record, Fields, T). 724 725record_from_line(RecordName, Fields, Line, Record) :- 726 phrase(fields_from_line(Fields, Values), Line), 727 Record =.. [RecordName|Values]. 728 729fields_from_line([], []) --> []. 730fields_from_line([F|FT], [V|VT]) --> 731 to_nul_s(Codes), 732 { field_to_prolog(F, Codes, V) }, 733 fields_from_line(FT, VT). 734 735to_nul_s([]) --> [0], !. 736to_nul_s([H|T]) --> [H], to_nul_s(T). 737 738field_to_prolog(ref_names, Line, List) :- 739 phrase(ref_names(List), Line), 740 !. 741field_to_prolog(committer_date_unix, Line, Stamp) :- 742 !, 743 number_codes(Stamp, Line). 744field_to_prolog(_, Line, Atom) :- 745 atom_codes(Atom, Line). 746 747ref_names([]) --> []. 748ref_names(List) --> 749 blanks, "(", ref_name_list(List), ")". 750 751ref_name_list([H|T]) --> 752 string_without(",)", Codes), 753 { atom_codes(H, Codes) }, 754 ( ",", blanks 755 -> ref_name_list(T) 756 ; {T=[]} 757 ).
stat
773:- record 774 git_commit(tree_hash:atom, 775 parent_hashes:list, 776 author_name:atom, 777 author_date:atom, 778 committer_name:atom, 779 committer_date:atom, 780 subject:atom). 781 782git_show(Dir, Hash, Commit, Options) :- 783 git_format_string(git_commit, Fields, Format), 784 option(diff(Diff), Options, patch), 785 diff_arg(Diff, DiffArg), 786 git_process_output([ show, DiffArg, Hash, Format ], 787 read_commit(Fields, Commit, Options), 788 [directory(Dir)]). 789 790diff_arg(patch, '-p'). 791diff_arg(stat, '--stat'). 792 793read_commit(Fields, Data-Body, Options, In) :- 794 read_line_to_codes(In, Line1), 795 record_from_line(git_commit, Fields, Line1, Data), 796 read_line_to_codes(In, Line2), 797 ( Line2 == [] 798 -> option(max_lines(Max), Options, -1), 799 read_n_lines(In, Max, Body) 800 ; Line2 == end_of_file 801 -> Body = [] 802 ). 803 804read_n_lines(In, Max, Lines) :- 805 read_line_to_codes(In, Line1), 806 read_n_lines(Line1, Max, In, Lines). 807 808read_n_lines(end_of_file, _, _, []) :- !. 809read_n_lines(_, 0, In, []) :- 810 !, 811 setup_call_cleanup(open_null_stream(Out), 812 copy_stream_data(In, Out), 813 close(Out)). 814read_n_lines(Line, Max0, In, [Line|More]) :- 815 read_line_to_codes(In, Line2), 816 Max is Max0-1, 817 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.827:- meta_predicate 828 git_format_string( , , ). 829 830git_format_string(M:RecordName, Fields, Format) :- 831 current_record(RecordName, M:Term), 832 findall(F, record_field(Term, F), Fields), 833 maplist(git_field_format, Fields, Formats), 834 atomic_list_concat(['--format='|Formats], Format). 835 836record_field(Term, Name) :- 837 arg(_, Term, Field), 838 field_name(Field, Name). 839 840field_name(Name:_Type=_Default, Name) :- !. 841field_name(Name:_Type, Name) :- !. 842field_name(Name=_Default, Name) :- !. 843field_name(Name, Name). 844 845git_field_format(Field, Fmt) :- 846 ( git_format(NoPercent, Field) 847 -> atomic_list_concat(['%', NoPercent, '%x00'], Fmt) 848 ; existence_error(git_format, Field) 849 ). 850 851git_format('H', commit_hash). 852git_format('h', abbreviated_commit_hash). 853git_format('T', tree_hash). 854git_format('t', abbreviated_tree_hash). 855git_format('P', parent_hashes). 856git_format('p', abbreviated_parent_hashes). 857 858git_format('an', author_name). 859git_format('aN', author_name_mailcap). 860git_format('ae', author_email). 861git_format('aE', author_email_mailcap). 862git_format('ad', author_date). 863git_format('aD', author_date_rfc2822). 864git_format('ar', author_date_relative). 865git_format('at', author_date_unix). 866git_format('ai', author_date_iso8601). 867 868git_format('cn', committer_name). 869git_format('cN', committer_name_mailcap). 870git_format('ce', committer_email). 871git_format('cE', committer_email_mailcap). 872git_format('cd', committer_date). 873git_format('cD', committer_date_rfc2822). 874git_format('cr', committer_date_relative). 875git_format('ct', committer_date_unix). 876git_format('ci', committer_date_iso8601). 877 878git_format('d', ref_names). % git log? 879git_format('e', encoding). % git log? 880 881git_format('s', subject). 882git_format('f', subject_sanitized). 883git_format('b', body). 884git_format('N', notes). 885 886git_format('gD', reflog_selector). 887git_format('gd', shortened_reflog_selector). 888git_format('gs', reflog_subject). 889 890 891 /******************************* 892 * MESSAGES * 893 *******************************/ 894 895:- multifile 896 prolog:message//1. 897 898prologmessage(git(output(Codes))) --> 899 { split_lines(Codes, Lines) }, 900 git_lines(Lines). 901 902git_lines([]) --> []. 903git_lines([H|T]) --> 904 [ '~s'-[H] ], 905 ( {T==[]} 906 -> [] 907 ; [nl], git_lines(T) 908 ). 909 910split_lines([], []) :- !. 911split_lines(All, [Line1|More]) :- 912 append(Line1, [0'\n|Rest], All), 913 !, 914 split_lines(Rest, More). 915split_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. */