Fossil SCM
Add the "fossil tree" command and the --tree option to "fossil ls" and "fossil extras".
Commit
dd72a96111edd296ea1fb2c09a637f471a3fa48ec1a7c2d11842bcc668cae8e4
Parent
e012f50a4b4c4df…
1 file changed
+146
-5
+146
-5
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -592,26 +592,119 @@ | ||
| 592 | 592 | /* The status command ends with warnings about ambiguous leaves (forks). */ |
| 593 | 593 | if( command==STATUS ){ |
| 594 | 594 | leaf_ambiguity_warning(vid, vid); |
| 595 | 595 | } |
| 596 | 596 | } |
| 597 | + | |
| 598 | +/* | |
| 599 | +** Characters used for drawing a file hierarchy graph. | |
| 600 | +*/ | |
| 601 | +#if _WIN32 | |
| 602 | +/* ASCII only available on Windows */ | |
| 603 | +#define ENTRY "|-- " | |
| 604 | +#define LASTE "`-- " | |
| 605 | +#define CONTU "| " | |
| 606 | +#define BLANK " " | |
| 607 | +#else | |
| 608 | +/* All other platforms support Unicode box-drawing chracters */ | |
| 609 | +#define ENTRY "\342\224\234\342\224\200\342\224\200 " | |
| 610 | +#define LASTE "\342\224\224\342\224\200\342\224\200 " | |
| 611 | +#define CONTU "\342\224\202 " | |
| 612 | +#define BLANK " " | |
| 613 | +#endif | |
| 614 | + | |
| 615 | + | |
| 616 | +/* zIn is a string that is guaranteed to be followed by \n. Return | |
| 617 | +** a pointer to the next line after the \n. The returned value might | |
| 618 | +** point to the \000 string terminator. | |
| 619 | +*/ | |
| 620 | +static const char *next_line(const char *zIn){ | |
| 621 | + const char *z = strchr(zIn, '\n'); | |
| 622 | + assert( z!=0 ); | |
| 623 | + return z+1; | |
| 624 | +} | |
| 625 | + | |
| 626 | +/* zIn is a non-empty list of filenames in sorted order and separated | |
| 627 | +** by \n. There might be a cluster of lines that have the same n-character | |
| 628 | +** prefix. Return a pointer to the start of the last line of that | |
| 629 | +** cluster. The return value might be zIn if the first line of zIn is | |
| 630 | +** unique in its first n character. | |
| 631 | +*/ | |
| 632 | +static const char *last_line(const char *zIn, int n){ | |
| 633 | + const char *zLast = zIn; | |
| 634 | + const char *z; | |
| 635 | + while( 1 ){ | |
| 636 | + z = next_line(zLast); | |
| 637 | + if( z[0]==0 || (n>0 && strncmp(zIn, z, n)!=0) ) break; | |
| 638 | + zLast = z; | |
| 639 | + } | |
| 640 | + return zLast; | |
| 641 | +} | |
| 642 | + | |
| 643 | +/* | |
| 644 | +** Print a section of a filelist hierarchy graph. This is a helper | |
| 645 | +** routine for print_filelist_as_tree() below. | |
| 646 | +*/ | |
| 647 | +static const char *print_filelist_section( | |
| 648 | + const char *zIn, /* List of filenames, separated by \n */ | |
| 649 | + const char *zLast, /* Last filename in the list to print */ | |
| 650 | + const char *zPrefix, /* Prefix so put before each output line */ | |
| 651 | + int nDir /* Ignore this many characters of directory name */ | |
| 652 | +){ | |
| 653 | + while( zIn<=zLast ){ | |
| 654 | + int i; | |
| 655 | + for(i=nDir; zIn[i]!='\n' && zIn[i]!='/'; i++){} | |
| 656 | + if( zIn[i]=='/' ){ | |
| 657 | + char *zSubPrefix; | |
| 658 | + const char *zSubLast = last_line(zIn, i+1); | |
| 659 | + zSubPrefix = mprintf("%s%s", zPrefix, zSubLast==zLast ? BLANK : CONTU); | |
| 660 | + fossil_print("%s%s%.*s\n", zPrefix, zSubLast==zLast ? LASTE : ENTRY, | |
| 661 | + i-nDir, &zIn[nDir]); | |
| 662 | + zIn = print_filelist_section(zIn, zSubLast, zSubPrefix, i+1); | |
| 663 | + fossil_free(zSubPrefix); | |
| 664 | + }else{ | |
| 665 | + fossil_print("%s%s%.*s\n", zPrefix, zIn==zLast ? LASTE : ENTRY, | |
| 666 | + i-nDir, &zIn[nDir]); | |
| 667 | + zIn = next_line(zIn); | |
| 668 | + } | |
| 669 | + } | |
| 670 | + return zIn; | |
| 671 | +} | |
| 672 | + | |
| 673 | +/* | |
| 674 | +** Input blob pList is a list of filenames, one filename per line, | |
| 675 | +** in sorted order and with / directory separators. Output this list | |
| 676 | +** as a tree in a manner similar to the "tree" command on Linux. | |
| 677 | +*/ | |
| 678 | +static void print_filelist_as_tree(Blob *pList){ | |
| 679 | + char *zAll; | |
| 680 | + const char *zLast; | |
| 681 | + fossil_print("%s\n", g.zLocalRoot); | |
| 682 | + zAll = blob_str(pList); | |
| 683 | + if( zAll[0] ){ | |
| 684 | + zLast = last_line(zAll, 0); | |
| 685 | + print_filelist_section(zAll, zLast, "", 0); | |
| 686 | + } | |
| 687 | +} | |
| 597 | 688 | |
| 598 | 689 | /* |
| 599 | 690 | ** Take care of -r version of ls command |
| 600 | 691 | */ |
| 601 | 692 | static void ls_cmd_rev( |
| 602 | 693 | const char *zRev, /* Revision string given */ |
| 603 | 694 | int verboseFlag, /* Verbose flag given */ |
| 604 | 695 | int showAge, /* Age flag given */ |
| 605 | - int timeOrder /* Order by time flag given */ | |
| 696 | + int timeOrder, /* Order by time flag given */ | |
| 697 | + int treeFmt /* Show output in the tree format */ | |
| 606 | 698 | ){ |
| 607 | 699 | Stmt q; |
| 608 | 700 | char *zOrderBy = "pathname COLLATE nocase"; |
| 609 | 701 | char *zName; |
| 610 | 702 | Blob where; |
| 611 | 703 | int rid; |
| 612 | 704 | int i; |
| 705 | + Blob out; | |
| 613 | 706 | |
| 614 | 707 | /* Handle given file names */ |
| 615 | 708 | blob_zero(&where); |
| 616 | 709 | for(i=2; i<g.argc; i++){ |
| 617 | 710 | Blob fname; |
| @@ -649,24 +742,31 @@ | ||
| 649 | 742 | " FROM fileage, blob\n" |
| 650 | 743 | " WHERE blob.rid=fileage.fid %s\n" |
| 651 | 744 | " ORDER BY %s;", blob_sql_text(&where), zOrderBy /*safe-for-%s*/ |
| 652 | 745 | ); |
| 653 | 746 | blob_reset(&where); |
| 747 | + if( treeFmt ) blob_init(&out, 0, 0); | |
| 654 | 748 | |
| 655 | 749 | while( db_step(&q)==SQLITE_ROW ){ |
| 656 | 750 | const char *zTime = db_column_text(&q,0); |
| 657 | 751 | const char *zFile = db_column_text(&q,1); |
| 658 | 752 | int size = db_column_int(&q,2); |
| 659 | - if( verboseFlag ){ | |
| 753 | + if( treeFmt ){ | |
| 754 | + blob_appendf(&out, "%s\n", zFile); | |
| 755 | + }else if( verboseFlag ){ | |
| 660 | 756 | fossil_print("%s %7d %s\n", zTime, size, zFile); |
| 661 | 757 | }else if( showAge ){ |
| 662 | 758 | fossil_print("%s %s\n", zTime, zFile); |
| 663 | 759 | }else{ |
| 664 | 760 | fossil_print("%s\n", zFile); |
| 665 | 761 | } |
| 666 | 762 | } |
| 667 | 763 | db_finalize(&q); |
| 764 | + if( treeFmt ){ | |
| 765 | + print_filelist_as_tree(&out); | |
| 766 | + blob_reset(&out); | |
| 767 | + } | |
| 668 | 768 | } |
| 669 | 769 | |
| 670 | 770 | /* |
| 671 | 771 | ** COMMAND: ls |
| 672 | 772 | ** |
| @@ -694,22 +794,24 @@ | ||
| 694 | 794 | ** |
| 695 | 795 | ** Options: |
| 696 | 796 | ** --age Show when each file was committed |
| 697 | 797 | ** -v|--verbose Provide extra information about each file |
| 698 | 798 | ** -t Sort output in time order |
| 799 | +** --tree Tree format | |
| 699 | 800 | ** -r VERSION The specific check-in to list |
| 700 | 801 | ** -R|--repository REPO Extract info from repository REPO |
| 701 | 802 | ** --hash With -v, verify file status using hashing |
| 702 | 803 | ** rather than relying on file sizes and mtimes |
| 703 | 804 | ** |
| 704 | -** See also: [[changes]], [[extras]], [[status]] | |
| 805 | +** See also: [[changes]], [[extras]], [[status]], [[tree]] | |
| 705 | 806 | */ |
| 706 | 807 | void ls_cmd(void){ |
| 707 | 808 | int vid; |
| 708 | 809 | Stmt q; |
| 709 | 810 | int verboseFlag; |
| 710 | 811 | int showAge; |
| 812 | + int treeFmt; | |
| 711 | 813 | int timeOrder; |
| 712 | 814 | char *zOrderBy = "pathname"; |
| 713 | 815 | Blob where; |
| 714 | 816 | int i; |
| 715 | 817 | int useHash = 0; |
| @@ -724,15 +826,19 @@ | ||
| 724 | 826 | zRev = find_option("r","r",1); |
| 725 | 827 | timeOrder = find_option("t","t",0)!=0; |
| 726 | 828 | if( verboseFlag ){ |
| 727 | 829 | useHash = find_option("hash",0,0)!=0; |
| 728 | 830 | } |
| 831 | + treeFmt = find_option("tree",0,0)!=0; | |
| 832 | + if( treeFmt ){ | |
| 833 | + if( zRev==0 ) zRev = "current"; | |
| 834 | + } | |
| 729 | 835 | |
| 730 | 836 | if( zRev!=0 ){ |
| 731 | 837 | db_find_and_open_repository(0, 0); |
| 732 | 838 | verify_all_options(); |
| 733 | - ls_cmd_rev(zRev,verboseFlag,showAge,timeOrder); | |
| 839 | + ls_cmd_rev(zRev,verboseFlag,showAge,timeOrder,treeFmt); | |
| 734 | 840 | return; |
| 735 | 841 | }else if( find_option("R",0,1)!=0 ){ |
| 736 | 842 | fossil_fatal("the -r is required in addition to -R"); |
| 737 | 843 | } |
| 738 | 844 | |
| @@ -828,10 +934,35 @@ | ||
| 828 | 934 | } |
| 829 | 935 | free(zFullName); |
| 830 | 936 | } |
| 831 | 937 | db_finalize(&q); |
| 832 | 938 | } |
| 939 | + | |
| 940 | +/* | |
| 941 | +** COMMAND: tree | |
| 942 | +** | |
| 943 | +** Usage: %fossil tree ?OPTIONS? ?PATHS ...? | |
| 944 | +** | |
| 945 | +** List all files in the current check-out in after the fashion of the | |
| 946 | +** "tree" command. If PATHS is included, only the named files | |
| 947 | +** (or their children if directories) are shown. | |
| 948 | +** | |
| 949 | +** Options: | |
| 950 | +** -r VERSION The specific check-in to list | |
| 951 | +** -R|--repository REPO Extract info from repository REPO | |
| 952 | +** | |
| 953 | +** See also: [[ls]] | |
| 954 | +*/ | |
| 955 | +void tree_cmd(void){ | |
| 956 | + const char *zRev; | |
| 957 | + | |
| 958 | + zRev = find_option("r","r",1); | |
| 959 | + if( zRev==0 ) zRev = "current"; | |
| 960 | + db_find_and_open_repository(0, 0); | |
| 961 | + verify_all_options(); | |
| 962 | + ls_cmd_rev(zRev,0,0,0,1); | |
| 963 | +} | |
| 833 | 964 | |
| 834 | 965 | /* |
| 835 | 966 | ** COMMAND: extras |
| 836 | 967 | ** |
| 837 | 968 | ** Usage: %fossil extras ?OPTIONS? ?PATH1 ...? |
| @@ -856,19 +987,21 @@ | ||
| 856 | 987 | ** --dotfiles Include files beginning with a dot (".") |
| 857 | 988 | ** --header Identify the repository if there are extras |
| 858 | 989 | ** --ignore CSG Ignore files matching patterns from the argument |
| 859 | 990 | ** --rel-paths Display pathnames relative to the current working |
| 860 | 991 | ** directory |
| 992 | +** --tree Show output in the tree format | |
| 861 | 993 | ** |
| 862 | 994 | ** See also: [[changes]], [[clean]], [[status]] |
| 863 | 995 | */ |
| 864 | 996 | void extras_cmd(void){ |
| 865 | 997 | Blob report = BLOB_INITIALIZER; |
| 866 | 998 | const char *zIgnoreFlag = find_option("ignore",0,1); |
| 867 | 999 | unsigned scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0; |
| 868 | 1000 | unsigned flags = C_EXTRA; |
| 869 | 1001 | int showHdr = find_option("header",0,0)!=0; |
| 1002 | + int treeFmt = find_option("tree",0,0)!=0; | |
| 870 | 1003 | Glob *pIgnore; |
| 871 | 1004 | |
| 872 | 1005 | if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; |
| 873 | 1006 | db_must_be_within_tree(); |
| 874 | 1007 | |
| @@ -875,10 +1008,14 @@ | ||
| 875 | 1008 | if( determine_cwd_relative_option() ){ |
| 876 | 1009 | flags |= C_RELPATH; |
| 877 | 1010 | } |
| 878 | 1011 | |
| 879 | 1012 | if( db_get_boolean("dotfiles", 0) ) scanFlags |= SCAN_ALL; |
| 1013 | + | |
| 1014 | + if( treeFmt ){ | |
| 1015 | + flags &= ~C_RELPATH; | |
| 1016 | + } | |
| 880 | 1017 | |
| 881 | 1018 | /* We should be done with options.. */ |
| 882 | 1019 | verify_all_options(); |
| 883 | 1020 | |
| 884 | 1021 | if( zIgnoreFlag==0 ){ |
| @@ -893,11 +1030,15 @@ | ||
| 893 | 1030 | if( blob_size(&report) ){ |
| 894 | 1031 | if( showHdr ){ |
| 895 | 1032 | fossil_print("Extras for %s at %s:\n", db_get("project-name","<unnamed>"), |
| 896 | 1033 | g.zLocalRoot); |
| 897 | 1034 | } |
| 898 | - blob_write_to_file(&report, "-"); | |
| 1035 | + if( treeFmt ){ | |
| 1036 | + print_filelist_as_tree(&report); | |
| 1037 | + }else{ | |
| 1038 | + blob_write_to_file(&report, "-"); | |
| 1039 | + } | |
| 899 | 1040 | } |
| 900 | 1041 | blob_reset(&report); |
| 901 | 1042 | } |
| 902 | 1043 | |
| 903 | 1044 | /* |
| 904 | 1045 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -592,26 +592,119 @@ | |
| 592 | /* The status command ends with warnings about ambiguous leaves (forks). */ |
| 593 | if( command==STATUS ){ |
| 594 | leaf_ambiguity_warning(vid, vid); |
| 595 | } |
| 596 | } |
| 597 | |
| 598 | /* |
| 599 | ** Take care of -r version of ls command |
| 600 | */ |
| 601 | static void ls_cmd_rev( |
| 602 | const char *zRev, /* Revision string given */ |
| 603 | int verboseFlag, /* Verbose flag given */ |
| 604 | int showAge, /* Age flag given */ |
| 605 | int timeOrder /* Order by time flag given */ |
| 606 | ){ |
| 607 | Stmt q; |
| 608 | char *zOrderBy = "pathname COLLATE nocase"; |
| 609 | char *zName; |
| 610 | Blob where; |
| 611 | int rid; |
| 612 | int i; |
| 613 | |
| 614 | /* Handle given file names */ |
| 615 | blob_zero(&where); |
| 616 | for(i=2; i<g.argc; i++){ |
| 617 | Blob fname; |
| @@ -649,24 +742,31 @@ | |
| 649 | " FROM fileage, blob\n" |
| 650 | " WHERE blob.rid=fileage.fid %s\n" |
| 651 | " ORDER BY %s;", blob_sql_text(&where), zOrderBy /*safe-for-%s*/ |
| 652 | ); |
| 653 | blob_reset(&where); |
| 654 | |
| 655 | while( db_step(&q)==SQLITE_ROW ){ |
| 656 | const char *zTime = db_column_text(&q,0); |
| 657 | const char *zFile = db_column_text(&q,1); |
| 658 | int size = db_column_int(&q,2); |
| 659 | if( verboseFlag ){ |
| 660 | fossil_print("%s %7d %s\n", zTime, size, zFile); |
| 661 | }else if( showAge ){ |
| 662 | fossil_print("%s %s\n", zTime, zFile); |
| 663 | }else{ |
| 664 | fossil_print("%s\n", zFile); |
| 665 | } |
| 666 | } |
| 667 | db_finalize(&q); |
| 668 | } |
| 669 | |
| 670 | /* |
| 671 | ** COMMAND: ls |
| 672 | ** |
| @@ -694,22 +794,24 @@ | |
| 694 | ** |
| 695 | ** Options: |
| 696 | ** --age Show when each file was committed |
| 697 | ** -v|--verbose Provide extra information about each file |
| 698 | ** -t Sort output in time order |
| 699 | ** -r VERSION The specific check-in to list |
| 700 | ** -R|--repository REPO Extract info from repository REPO |
| 701 | ** --hash With -v, verify file status using hashing |
| 702 | ** rather than relying on file sizes and mtimes |
| 703 | ** |
| 704 | ** See also: [[changes]], [[extras]], [[status]] |
| 705 | */ |
| 706 | void ls_cmd(void){ |
| 707 | int vid; |
| 708 | Stmt q; |
| 709 | int verboseFlag; |
| 710 | int showAge; |
| 711 | int timeOrder; |
| 712 | char *zOrderBy = "pathname"; |
| 713 | Blob where; |
| 714 | int i; |
| 715 | int useHash = 0; |
| @@ -724,15 +826,19 @@ | |
| 724 | zRev = find_option("r","r",1); |
| 725 | timeOrder = find_option("t","t",0)!=0; |
| 726 | if( verboseFlag ){ |
| 727 | useHash = find_option("hash",0,0)!=0; |
| 728 | } |
| 729 | |
| 730 | if( zRev!=0 ){ |
| 731 | db_find_and_open_repository(0, 0); |
| 732 | verify_all_options(); |
| 733 | ls_cmd_rev(zRev,verboseFlag,showAge,timeOrder); |
| 734 | return; |
| 735 | }else if( find_option("R",0,1)!=0 ){ |
| 736 | fossil_fatal("the -r is required in addition to -R"); |
| 737 | } |
| 738 | |
| @@ -828,10 +934,35 @@ | |
| 828 | } |
| 829 | free(zFullName); |
| 830 | } |
| 831 | db_finalize(&q); |
| 832 | } |
| 833 | |
| 834 | /* |
| 835 | ** COMMAND: extras |
| 836 | ** |
| 837 | ** Usage: %fossil extras ?OPTIONS? ?PATH1 ...? |
| @@ -856,19 +987,21 @@ | |
| 856 | ** --dotfiles Include files beginning with a dot (".") |
| 857 | ** --header Identify the repository if there are extras |
| 858 | ** --ignore CSG Ignore files matching patterns from the argument |
| 859 | ** --rel-paths Display pathnames relative to the current working |
| 860 | ** directory |
| 861 | ** |
| 862 | ** See also: [[changes]], [[clean]], [[status]] |
| 863 | */ |
| 864 | void extras_cmd(void){ |
| 865 | Blob report = BLOB_INITIALIZER; |
| 866 | const char *zIgnoreFlag = find_option("ignore",0,1); |
| 867 | unsigned scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0; |
| 868 | unsigned flags = C_EXTRA; |
| 869 | int showHdr = find_option("header",0,0)!=0; |
| 870 | Glob *pIgnore; |
| 871 | |
| 872 | if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; |
| 873 | db_must_be_within_tree(); |
| 874 | |
| @@ -875,10 +1008,14 @@ | |
| 875 | if( determine_cwd_relative_option() ){ |
| 876 | flags |= C_RELPATH; |
| 877 | } |
| 878 | |
| 879 | if( db_get_boolean("dotfiles", 0) ) scanFlags |= SCAN_ALL; |
| 880 | |
| 881 | /* We should be done with options.. */ |
| 882 | verify_all_options(); |
| 883 | |
| 884 | if( zIgnoreFlag==0 ){ |
| @@ -893,11 +1030,15 @@ | |
| 893 | if( blob_size(&report) ){ |
| 894 | if( showHdr ){ |
| 895 | fossil_print("Extras for %s at %s:\n", db_get("project-name","<unnamed>"), |
| 896 | g.zLocalRoot); |
| 897 | } |
| 898 | blob_write_to_file(&report, "-"); |
| 899 | } |
| 900 | blob_reset(&report); |
| 901 | } |
| 902 | |
| 903 | /* |
| 904 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -592,26 +592,119 @@ | |
| 592 | /* The status command ends with warnings about ambiguous leaves (forks). */ |
| 593 | if( command==STATUS ){ |
| 594 | leaf_ambiguity_warning(vid, vid); |
| 595 | } |
| 596 | } |
| 597 | |
| 598 | /* |
| 599 | ** Characters used for drawing a file hierarchy graph. |
| 600 | */ |
| 601 | #if _WIN32 |
| 602 | /* ASCII only available on Windows */ |
| 603 | #define ENTRY "|-- " |
| 604 | #define LASTE "`-- " |
| 605 | #define CONTU "| " |
| 606 | #define BLANK " " |
| 607 | #else |
| 608 | /* All other platforms support Unicode box-drawing chracters */ |
| 609 | #define ENTRY "\342\224\234\342\224\200\342\224\200 " |
| 610 | #define LASTE "\342\224\224\342\224\200\342\224\200 " |
| 611 | #define CONTU "\342\224\202 " |
| 612 | #define BLANK " " |
| 613 | #endif |
| 614 | |
| 615 | |
| 616 | /* zIn is a string that is guaranteed to be followed by \n. Return |
| 617 | ** a pointer to the next line after the \n. The returned value might |
| 618 | ** point to the \000 string terminator. |
| 619 | */ |
| 620 | static const char *next_line(const char *zIn){ |
| 621 | const char *z = strchr(zIn, '\n'); |
| 622 | assert( z!=0 ); |
| 623 | return z+1; |
| 624 | } |
| 625 | |
| 626 | /* zIn is a non-empty list of filenames in sorted order and separated |
| 627 | ** by \n. There might be a cluster of lines that have the same n-character |
| 628 | ** prefix. Return a pointer to the start of the last line of that |
| 629 | ** cluster. The return value might be zIn if the first line of zIn is |
| 630 | ** unique in its first n character. |
| 631 | */ |
| 632 | static const char *last_line(const char *zIn, int n){ |
| 633 | const char *zLast = zIn; |
| 634 | const char *z; |
| 635 | while( 1 ){ |
| 636 | z = next_line(zLast); |
| 637 | if( z[0]==0 || (n>0 && strncmp(zIn, z, n)!=0) ) break; |
| 638 | zLast = z; |
| 639 | } |
| 640 | return zLast; |
| 641 | } |
| 642 | |
| 643 | /* |
| 644 | ** Print a section of a filelist hierarchy graph. This is a helper |
| 645 | ** routine for print_filelist_as_tree() below. |
| 646 | */ |
| 647 | static const char *print_filelist_section( |
| 648 | const char *zIn, /* List of filenames, separated by \n */ |
| 649 | const char *zLast, /* Last filename in the list to print */ |
| 650 | const char *zPrefix, /* Prefix so put before each output line */ |
| 651 | int nDir /* Ignore this many characters of directory name */ |
| 652 | ){ |
| 653 | while( zIn<=zLast ){ |
| 654 | int i; |
| 655 | for(i=nDir; zIn[i]!='\n' && zIn[i]!='/'; i++){} |
| 656 | if( zIn[i]=='/' ){ |
| 657 | char *zSubPrefix; |
| 658 | const char *zSubLast = last_line(zIn, i+1); |
| 659 | zSubPrefix = mprintf("%s%s", zPrefix, zSubLast==zLast ? BLANK : CONTU); |
| 660 | fossil_print("%s%s%.*s\n", zPrefix, zSubLast==zLast ? LASTE : ENTRY, |
| 661 | i-nDir, &zIn[nDir]); |
| 662 | zIn = print_filelist_section(zIn, zSubLast, zSubPrefix, i+1); |
| 663 | fossil_free(zSubPrefix); |
| 664 | }else{ |
| 665 | fossil_print("%s%s%.*s\n", zPrefix, zIn==zLast ? LASTE : ENTRY, |
| 666 | i-nDir, &zIn[nDir]); |
| 667 | zIn = next_line(zIn); |
| 668 | } |
| 669 | } |
| 670 | return zIn; |
| 671 | } |
| 672 | |
| 673 | /* |
| 674 | ** Input blob pList is a list of filenames, one filename per line, |
| 675 | ** in sorted order and with / directory separators. Output this list |
| 676 | ** as a tree in a manner similar to the "tree" command on Linux. |
| 677 | */ |
| 678 | static void print_filelist_as_tree(Blob *pList){ |
| 679 | char *zAll; |
| 680 | const char *zLast; |
| 681 | fossil_print("%s\n", g.zLocalRoot); |
| 682 | zAll = blob_str(pList); |
| 683 | if( zAll[0] ){ |
| 684 | zLast = last_line(zAll, 0); |
| 685 | print_filelist_section(zAll, zLast, "", 0); |
| 686 | } |
| 687 | } |
| 688 | |
| 689 | /* |
| 690 | ** Take care of -r version of ls command |
| 691 | */ |
| 692 | static void ls_cmd_rev( |
| 693 | const char *zRev, /* Revision string given */ |
| 694 | int verboseFlag, /* Verbose flag given */ |
| 695 | int showAge, /* Age flag given */ |
| 696 | int timeOrder, /* Order by time flag given */ |
| 697 | int treeFmt /* Show output in the tree format */ |
| 698 | ){ |
| 699 | Stmt q; |
| 700 | char *zOrderBy = "pathname COLLATE nocase"; |
| 701 | char *zName; |
| 702 | Blob where; |
| 703 | int rid; |
| 704 | int i; |
| 705 | Blob out; |
| 706 | |
| 707 | /* Handle given file names */ |
| 708 | blob_zero(&where); |
| 709 | for(i=2; i<g.argc; i++){ |
| 710 | Blob fname; |
| @@ -649,24 +742,31 @@ | |
| 742 | " FROM fileage, blob\n" |
| 743 | " WHERE blob.rid=fileage.fid %s\n" |
| 744 | " ORDER BY %s;", blob_sql_text(&where), zOrderBy /*safe-for-%s*/ |
| 745 | ); |
| 746 | blob_reset(&where); |
| 747 | if( treeFmt ) blob_init(&out, 0, 0); |
| 748 | |
| 749 | while( db_step(&q)==SQLITE_ROW ){ |
| 750 | const char *zTime = db_column_text(&q,0); |
| 751 | const char *zFile = db_column_text(&q,1); |
| 752 | int size = db_column_int(&q,2); |
| 753 | if( treeFmt ){ |
| 754 | blob_appendf(&out, "%s\n", zFile); |
| 755 | }else if( verboseFlag ){ |
| 756 | fossil_print("%s %7d %s\n", zTime, size, zFile); |
| 757 | }else if( showAge ){ |
| 758 | fossil_print("%s %s\n", zTime, zFile); |
| 759 | }else{ |
| 760 | fossil_print("%s\n", zFile); |
| 761 | } |
| 762 | } |
| 763 | db_finalize(&q); |
| 764 | if( treeFmt ){ |
| 765 | print_filelist_as_tree(&out); |
| 766 | blob_reset(&out); |
| 767 | } |
| 768 | } |
| 769 | |
| 770 | /* |
| 771 | ** COMMAND: ls |
| 772 | ** |
| @@ -694,22 +794,24 @@ | |
| 794 | ** |
| 795 | ** Options: |
| 796 | ** --age Show when each file was committed |
| 797 | ** -v|--verbose Provide extra information about each file |
| 798 | ** -t Sort output in time order |
| 799 | ** --tree Tree format |
| 800 | ** -r VERSION The specific check-in to list |
| 801 | ** -R|--repository REPO Extract info from repository REPO |
| 802 | ** --hash With -v, verify file status using hashing |
| 803 | ** rather than relying on file sizes and mtimes |
| 804 | ** |
| 805 | ** See also: [[changes]], [[extras]], [[status]], [[tree]] |
| 806 | */ |
| 807 | void ls_cmd(void){ |
| 808 | int vid; |
| 809 | Stmt q; |
| 810 | int verboseFlag; |
| 811 | int showAge; |
| 812 | int treeFmt; |
| 813 | int timeOrder; |
| 814 | char *zOrderBy = "pathname"; |
| 815 | Blob where; |
| 816 | int i; |
| 817 | int useHash = 0; |
| @@ -724,15 +826,19 @@ | |
| 826 | zRev = find_option("r","r",1); |
| 827 | timeOrder = find_option("t","t",0)!=0; |
| 828 | if( verboseFlag ){ |
| 829 | useHash = find_option("hash",0,0)!=0; |
| 830 | } |
| 831 | treeFmt = find_option("tree",0,0)!=0; |
| 832 | if( treeFmt ){ |
| 833 | if( zRev==0 ) zRev = "current"; |
| 834 | } |
| 835 | |
| 836 | if( zRev!=0 ){ |
| 837 | db_find_and_open_repository(0, 0); |
| 838 | verify_all_options(); |
| 839 | ls_cmd_rev(zRev,verboseFlag,showAge,timeOrder,treeFmt); |
| 840 | return; |
| 841 | }else if( find_option("R",0,1)!=0 ){ |
| 842 | fossil_fatal("the -r is required in addition to -R"); |
| 843 | } |
| 844 | |
| @@ -828,10 +934,35 @@ | |
| 934 | } |
| 935 | free(zFullName); |
| 936 | } |
| 937 | db_finalize(&q); |
| 938 | } |
| 939 | |
| 940 | /* |
| 941 | ** COMMAND: tree |
| 942 | ** |
| 943 | ** Usage: %fossil tree ?OPTIONS? ?PATHS ...? |
| 944 | ** |
| 945 | ** List all files in the current check-out in after the fashion of the |
| 946 | ** "tree" command. If PATHS is included, only the named files |
| 947 | ** (or their children if directories) are shown. |
| 948 | ** |
| 949 | ** Options: |
| 950 | ** -r VERSION The specific check-in to list |
| 951 | ** -R|--repository REPO Extract info from repository REPO |
| 952 | ** |
| 953 | ** See also: [[ls]] |
| 954 | */ |
| 955 | void tree_cmd(void){ |
| 956 | const char *zRev; |
| 957 | |
| 958 | zRev = find_option("r","r",1); |
| 959 | if( zRev==0 ) zRev = "current"; |
| 960 | db_find_and_open_repository(0, 0); |
| 961 | verify_all_options(); |
| 962 | ls_cmd_rev(zRev,0,0,0,1); |
| 963 | } |
| 964 | |
| 965 | /* |
| 966 | ** COMMAND: extras |
| 967 | ** |
| 968 | ** Usage: %fossil extras ?OPTIONS? ?PATH1 ...? |
| @@ -856,19 +987,21 @@ | |
| 987 | ** --dotfiles Include files beginning with a dot (".") |
| 988 | ** --header Identify the repository if there are extras |
| 989 | ** --ignore CSG Ignore files matching patterns from the argument |
| 990 | ** --rel-paths Display pathnames relative to the current working |
| 991 | ** directory |
| 992 | ** --tree Show output in the tree format |
| 993 | ** |
| 994 | ** See also: [[changes]], [[clean]], [[status]] |
| 995 | */ |
| 996 | void extras_cmd(void){ |
| 997 | Blob report = BLOB_INITIALIZER; |
| 998 | const char *zIgnoreFlag = find_option("ignore",0,1); |
| 999 | unsigned scanFlags = find_option("dotfiles",0,0)!=0 ? SCAN_ALL : 0; |
| 1000 | unsigned flags = C_EXTRA; |
| 1001 | int showHdr = find_option("header",0,0)!=0; |
| 1002 | int treeFmt = find_option("tree",0,0)!=0; |
| 1003 | Glob *pIgnore; |
| 1004 | |
| 1005 | if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP; |
| 1006 | db_must_be_within_tree(); |
| 1007 | |
| @@ -875,10 +1008,14 @@ | |
| 1008 | if( determine_cwd_relative_option() ){ |
| 1009 | flags |= C_RELPATH; |
| 1010 | } |
| 1011 | |
| 1012 | if( db_get_boolean("dotfiles", 0) ) scanFlags |= SCAN_ALL; |
| 1013 | |
| 1014 | if( treeFmt ){ |
| 1015 | flags &= ~C_RELPATH; |
| 1016 | } |
| 1017 | |
| 1018 | /* We should be done with options.. */ |
| 1019 | verify_all_options(); |
| 1020 | |
| 1021 | if( zIgnoreFlag==0 ){ |
| @@ -893,11 +1030,15 @@ | |
| 1030 | if( blob_size(&report) ){ |
| 1031 | if( showHdr ){ |
| 1032 | fossil_print("Extras for %s at %s:\n", db_get("project-name","<unnamed>"), |
| 1033 | g.zLocalRoot); |
| 1034 | } |
| 1035 | if( treeFmt ){ |
| 1036 | print_filelist_as_tree(&report); |
| 1037 | }else{ |
| 1038 | blob_write_to_file(&report, "-"); |
| 1039 | } |
| 1040 | } |
| 1041 | blob_reset(&report); |
| 1042 | } |
| 1043 | |
| 1044 | /* |
| 1045 |