Fossil SCM
Reworked the "touch" command to be able to handle non-glob filenames.
Commit
1b49ab3c6c414a2cf76eab1acb0a72dcf9489390048f5148bfa0cb2601c251f8
Parent
7b7f5df8911f5d8…
1 file changed
+218
-58
+218
-58
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -1799,43 +1799,114 @@ | ||
| 1799 | 1799 | zDir = g.argv[2]; |
| 1800 | 1800 | zGlob = g.argc==4 ? g.argv[3] : 0; |
| 1801 | 1801 | fossil_print("%d\n", file_directory_size(zDir, zGlob, omitDotFiles)); |
| 1802 | 1802 | } |
| 1803 | 1803 | |
| 1804 | +/* | |
| 1805 | +** Internal helper for touch_cmd(). zAbsName must be resolvable as-is | |
| 1806 | +** to a file - this function does not expand/normalize it. i.e. it | |
| 1807 | +** "really should" be an absolute path. zTreeName is strictly | |
| 1808 | +** cosmetic: it is used when dryRunFlag or verboseFlag generate | |
| 1809 | +** output. It is assumed to be a repo-relative or or subdir-relative | |
| 1810 | +** filename. | |
| 1811 | +** | |
| 1812 | +** newMTime is the file's new timestamp (Unix epoch). | |
| 1813 | +** | |
| 1814 | +** Returns 1 if it sets zAbsName's mtime, 0 if it does not (indicating | |
| 1815 | +** that the file already has that timestamp). Dies fatally if given an | |
| 1816 | +** unresolvable filename. If dryRunFlag is true then it outputs the | |
| 1817 | +** name of the file it would have timestamped but does not stamp the | |
| 1818 | +** file. If verboseFlag is true, it outputs a message if the files | |
| 1819 | +** timestamp is actually modified. | |
| 1820 | +*/ | |
| 1821 | +static int touch_cmd_stamp_one_file(char const *zAbsName, | |
| 1822 | + char const *zTreeName, | |
| 1823 | + i64 newMtime, int dryRunFlag, | |
| 1824 | + int verboseFlag){ | |
| 1825 | + i64 const currentMtime = file_mtime(zAbsName, 0); | |
| 1826 | + if(currentMtime<0){ | |
| 1827 | + fossil_fatal("Cannot stat file: %s\n", zAbsName); | |
| 1828 | + }else if(currentMtime==newMtime){ | |
| 1829 | + return 0; | |
| 1830 | + }else if( dryRunFlag!=0 ){ | |
| 1831 | + fossil_print( "dry-run: %s\n", zTreeName ); | |
| 1832 | + }else{ | |
| 1833 | + file_set_mtime(zAbsName, newMtime); | |
| 1834 | + if( verboseFlag!=0 ){ | |
| 1835 | + fossil_print( "touched %s\n", zTreeName ); | |
| 1836 | + } | |
| 1837 | + } | |
| 1838 | + return 1; | |
| 1839 | +} | |
| 1840 | + | |
| 1841 | +/* | |
| 1842 | +** Internal helper for touch_cmd(). If the given name is found in the | |
| 1843 | +** given checkout version, which MUST be the checkout version | |
| 1844 | +** currently populating the vfile table, the vfile.mrid value for the | |
| 1845 | +** file is returned, else 0 is returned. zName must be resolvable | |
| 1846 | +** as-is - this function performs neither expands nor normalizes it. | |
| 1847 | +*/ | |
| 1848 | +static int touch_cmd_vfile_mrid( int vid, char const *zName ){ | |
| 1849 | + int mrid = 0; | |
| 1850 | + static Stmt q = empty_Stmt_m; | |
| 1851 | + db_static_prepare(&q, "SELECT vfile.mrid " | |
| 1852 | + "FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid " | |
| 1853 | + "WHERE vid=:vid AND pathname=:pathname %s", | |
| 1854 | + filename_collation()); | |
| 1855 | + db_bind_int(&q, ":vid", vid); | |
| 1856 | + db_bind_text(&q, ":pathname", zName); | |
| 1857 | + if(SQLITE_ROW==db_step(&q)){ | |
| 1858 | + mrid = db_column_int(&q, 0); | |
| 1859 | + } | |
| 1860 | + db_reset(&q); | |
| 1861 | + return mrid; | |
| 1862 | +} | |
| 1804 | 1863 | |
| 1805 | 1864 | /* |
| 1806 | 1865 | ** COMMAND: touch* |
| 1807 | 1866 | ** |
| 1808 | -** Usage: %fossil touch ?OPTIONS? | |
| 1867 | +** Usage: %fossil touch ?OPTIONS? ?FILENAME...? | |
| 1809 | 1868 | ** |
| 1810 | 1869 | ** For each file in the current checkout matching one of the provided |
| 1811 | -** list of glob patterns, or all files if no globs are provided, sets | |
| 1812 | -** the file's mtime to the time of the last checkin which modified | |
| 1813 | -** that file. | |
| 1870 | +** list of glob patterns and/or file names, the file's mtime is | |
| 1871 | +** updated to a value specified by one of the flags --checkout, | |
| 1872 | +** --checkin, or --now. | |
| 1873 | +** | |
| 1874 | +** If neither glob patterns nor filenames are provided, it operates on | |
| 1875 | +** all files managed by the currently checked-out version. | |
| 1814 | 1876 | ** |
| 1815 | 1877 | ** This command gets its name from the conventional Unix "touch" |
| 1816 | 1878 | ** command. |
| 1817 | 1879 | ** |
| 1818 | 1880 | ** Options: |
| 1819 | 1881 | ** --now Stamp each affected file with the current time. |
| 1820 | 1882 | ** This is the default behavior. |
| 1821 | 1883 | ** -c|--checkin Stamp each affected file with the time of the |
| 1822 | -** most recent checkin which modified that file. | |
| 1884 | +** most recent check-in which modified that file. | |
| 1885 | +** -C|--checkout Stamp each affected file with the time of the | |
| 1886 | +** currently-checked-out version. | |
| 1823 | 1887 | ** -g GLOBLIST Comma-separated list of glob patterns. Default |
| 1824 | 1888 | ** is to touch all SCM-controlled files. |
| 1825 | 1889 | ** -G GLOBFILE Similar to -g but reads its globs from a |
| 1826 | 1890 | ** fossil-conventional glob list file. |
| 1827 | -** -v|-verbose Outputs information about its globs and each | |
| 1828 | -** file it touches. | |
| 1891 | +** -v|-verbose Outputs extra information about its globs | |
| 1892 | +** and each file it touches. | |
| 1829 | 1893 | ** -n|--dry-run Outputs which files would require touching, |
| 1830 | 1894 | ** but does not touch them. |
| 1895 | +** -q|--quiet Suppress warnings when skipping unmanaged | |
| 1896 | +** or out-of-tree files. | |
| 1831 | 1897 | ** |
| 1832 | -** Only one of -g or -G may be used. If neither is provided, | |
| 1833 | -** the effect is as if a glob of '*' were provided. | |
| 1898 | +** Only one of --now, --checkin, and --checkout may be used. The | |
| 1899 | +** default is --now. | |
| 1834 | 1900 | ** |
| 1835 | -** Only one of --now and --checkin may be used. The default | |
| 1836 | -** is --now. | |
| 1901 | +** Only one of -g or -G may be used. If neither is provided and no | |
| 1902 | +** additional filenames are provided, the effect is as if a glob of | |
| 1903 | +** '*' were provided. Note that all glob patterns provided via these | |
| 1904 | +** flags are always evaluated as if they are relative to the top of | |
| 1905 | +** the source tree, not the current working (sub)directory. Filenames | |
| 1906 | +** provided without these flags, on the other hand, are treated as | |
| 1907 | +** relative to the current directory. | |
| 1837 | 1908 | ** |
| 1838 | 1909 | */ |
| 1839 | 1910 | void touch_cmd(){ |
| 1840 | 1911 | const char * zGlobList; /* -g List of glob patterns */ |
| 1841 | 1912 | const char * zGlobFile; /* -G File of glob patterns */ |
| @@ -1842,87 +1913,176 @@ | ||
| 1842 | 1913 | Glob * pGlob = 0; /* List of glob patterns */ |
| 1843 | 1914 | int verboseFlag; |
| 1844 | 1915 | int dryRunFlag; |
| 1845 | 1916 | int vid; /* Checkout version */ |
| 1846 | 1917 | int changeCount = 0; /* Number of files touched */ |
| 1847 | - int checkinFlag; /* -c|--checkin */ | |
| 1848 | - i64 const nowTime = time(0); | |
| 1918 | + int quietFlag = 0; /* -q|--quiet */ | |
| 1919 | + int timeFlag; /* -1==--checkin, 1==--checkout, 0==--now */ | |
| 1920 | + i64 nowTime = 0; /* Timestamp of --now or --checkout */ | |
| 1849 | 1921 | Stmt q; |
| 1922 | + Blob absBuffer = empty_blob; | |
| 1850 | 1923 | |
| 1851 | 1924 | verboseFlag = find_option("verbose","v",0)!=0; |
| 1852 | - dryRunFlag = find_option("dry-run","n",0)!=0; | |
| 1925 | + quietFlag = find_option("quiet","q",0)!=0; | |
| 1926 | + dryRunFlag = find_option("dry-run","n",0)!=0 | |
| 1927 | + || find_option("dryrun",0,0)!=0; | |
| 1853 | 1928 | zGlobList = find_option("glob", "g",1); |
| 1854 | 1929 | zGlobFile = find_option("globfile", "G",1); |
| 1855 | - checkinFlag = find_option("checkin","c",0)!=0; | |
| 1856 | 1930 | |
| 1857 | - if(find_option("now",0,0)!=0 && checkinFlag!=0){ | |
| 1858 | - fossil_fatal("Options --checkin and --now may not be used together."); | |
| 1859 | - } | |
| 1860 | 1931 | if(zGlobList && zGlobFile){ |
| 1861 | 1932 | fossil_fatal("Options -g and -G may not be used together."); |
| 1862 | 1933 | } |
| 1934 | + | |
| 1935 | + { | |
| 1936 | + int const ci = | |
| 1937 | + (find_option("checkin","c",0) || find_option("check-in",0,0)) | |
| 1938 | + ? 1 : 0; | |
| 1939 | + int const co = find_option("checkout","C",0) ? 1 : 0; | |
| 1940 | + int const now = find_option("now",0,0) ? 1 : 0; | |
| 1941 | + if(ci + co + now > 1){ | |
| 1942 | + fossil_fatal("Options --checkin, --checkout, and --now may " | |
| 1943 | + "not be used together."); | |
| 1944 | + }else if(co){ | |
| 1945 | + timeFlag = 1; | |
| 1946 | + if(verboseFlag){ | |
| 1947 | + fossil_print("Timestamp = current checkout version.\n"); | |
| 1948 | + } | |
| 1949 | + }else if(ci){ | |
| 1950 | + timeFlag = -1; | |
| 1951 | + if(verboseFlag){ | |
| 1952 | + fossil_print("Timestamp = checkin in which each file was " | |
| 1953 | + "most recently modified.\n"); | |
| 1954 | + } | |
| 1955 | + }else{ | |
| 1956 | + timeFlag = 0; | |
| 1957 | + if(verboseFlag){ | |
| 1958 | + fossil_print("Timestamp = current system time.\n"); | |
| 1959 | + } | |
| 1960 | + } | |
| 1961 | + } | |
| 1863 | 1962 | |
| 1864 | 1963 | verify_all_options(); |
| 1865 | 1964 | |
| 1866 | 1965 | db_must_be_within_tree(); |
| 1867 | 1966 | vid = db_lget_int("checkout", 0); |
| 1868 | 1967 | if(vid==0){ |
| 1869 | 1968 | fossil_fatal("Cannot determine checkout version."); |
| 1870 | 1969 | } |
| 1970 | + | |
| 1871 | 1971 | if(zGlobList){ |
| 1872 | 1972 | pGlob = *zGlobList ? glob_create(zGlobList) : 0; |
| 1873 | 1973 | }else if(zGlobFile){ |
| 1874 | - Blob globs; | |
| 1974 | + Blob globs = empty_blob; | |
| 1875 | 1975 | blob_read_from_file(&globs, zGlobFile, ExtFILE); |
| 1876 | 1976 | pGlob = glob_create( globs.aData ); |
| 1877 | 1977 | blob_reset(&globs); |
| 1878 | 1978 | } |
| 1879 | - db_begin_transaction(); | |
| 1880 | - db_prepare(&q, "SELECT vfile.mrid, pathname " | |
| 1881 | - "FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid " | |
| 1882 | - "WHERE vid=%d", vid); | |
| 1883 | 1979 | if( pGlob && verboseFlag!=0 ){ |
| 1884 | 1980 | int i; |
| 1885 | 1981 | for(i=0; i<pGlob->nPattern; ++i){ |
| 1886 | 1982 | fossil_print("glob: %s\n", pGlob->azPattern[i]); |
| 1887 | 1983 | } |
| 1888 | 1984 | } |
| 1889 | - if( verboseFlag ){ | |
| 1890 | - if(checkinFlag){ | |
| 1891 | - fossil_print("Using mtime from most recent commit(s).\n"); | |
| 1892 | - }else{ | |
| 1893 | - fossil_print("Using current time.\n"); | |
| 1894 | - } | |
| 1895 | - } | |
| 1896 | - while(SQLITE_ROW==db_step(&q)){ | |
| 1897 | - const char * zName = db_column_text(&q, 1); | |
| 1898 | - int const fid = db_column_int(&q, 0); | |
| 1899 | - i64 newMtime = checkinFlag ? 0 : nowTime; | |
| 1900 | - i64 currentMtime; | |
| 1901 | - if(pGlob){ | |
| 1902 | - if(glob_match(pGlob, zName)==0) continue; | |
| 1903 | - } | |
| 1904 | - currentMtime = file_mtime(zName, 0); | |
| 1905 | - if( newMtime || mtime_of_manifest_file(vid, fid, &newMtime)==0 ){ | |
| 1906 | - if( currentMtime!=newMtime ){ | |
| 1907 | - ++changeCount; | |
| 1908 | - if( dryRunFlag!=0 ){ | |
| 1909 | - fossil_print( "dry-run: %s\n", zName ); | |
| 1910 | - }else{ | |
| 1911 | - file_set_mtime(zName, newMtime); | |
| 1912 | - if( verboseFlag!=0 ){ | |
| 1913 | - fossil_print( "touched %s\n", zName ); | |
| 1914 | - } | |
| 1915 | - } | |
| 1916 | - } | |
| 1917 | - } | |
| 1918 | - } | |
| 1919 | - db_finalize(&q); | |
| 1920 | - db_end_transaction(0); | |
| 1921 | - glob_free(pGlob); | |
| 1985 | + | |
| 1986 | + db_begin_transaction(); | |
| 1987 | + if(timeFlag==0){/*--now*/ | |
| 1988 | + nowTime = time(0); | |
| 1989 | + }else if(timeFlag>0){/*--checkout: get the checkout | |
| 1990 | + manifest's timestamp*/ | |
| 1991 | + assert(vid>0); | |
| 1992 | + nowTime = db_int64(-1, | |
| 1993 | + "SELECT CAST(strftime('%%s'," | |
| 1994 | + "(SELECT mtime FROM event WHERE objid=%d)" | |
| 1995 | + ") AS INTEGER)", vid); | |
| 1996 | + if(nowTime<0){ | |
| 1997 | + fossil_fatal("Could not determine out checkout version's time!"); | |
| 1998 | + } | |
| 1999 | + }else{ /* --checkin */ | |
| 2000 | + assert(0 == nowTime); | |
| 2001 | + } | |
| 2002 | + if((pGlob && pGlob->nPattern>0) | |
| 2003 | + || g.argc<3 /* no non-flag arguments */ ){ | |
| 2004 | + /* | |
| 2005 | + ** We have either globs or no trailing filenames (in which case an | |
| 2006 | + ** effective glob pattern of '*' is assumed). If there are neither | |
| 2007 | + ** globs nor filenames then we operate on all managed files. | |
| 2008 | + */ | |
| 2009 | + db_prepare(&q, | |
| 2010 | + "SELECT vfile.mrid, pathname " | |
| 2011 | + "FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid " | |
| 2012 | + "WHERE vid=%d", vid); | |
| 2013 | + while(SQLITE_ROW==db_step(&q)){ | |
| 2014 | + int const fid = db_column_int(&q, 0); | |
| 2015 | + const char * zName = db_column_text(&q, 1); | |
| 2016 | + i64 newMtime = nowTime; | |
| 2017 | + char const * zAbs = 0; /* absolute path */ | |
| 2018 | + absBuffer.nUsed = 0; | |
| 2019 | + assert(timeFlag<0 ? newMtime==0 : newMtime>0); | |
| 2020 | + if(pGlob){ | |
| 2021 | + if(glob_match(pGlob, zName)==0) continue; | |
| 2022 | + } | |
| 2023 | + blob_appendf( &absBuffer, "%s%s", g.zLocalRoot, zName ); | |
| 2024 | + zAbs = blob_str(&absBuffer); | |
| 2025 | + if( newMtime || mtime_of_manifest_file(vid, fid, &newMtime)==0 ){ | |
| 2026 | + changeCount += | |
| 2027 | + touch_cmd_stamp_one_file( zAbs, zName, newMtime, | |
| 2028 | + dryRunFlag, verboseFlag ); | |
| 2029 | + } | |
| 2030 | + } | |
| 2031 | + db_finalize(&q); | |
| 2032 | + } | |
| 2033 | + glob_free(pGlob); | |
| 2034 | + pGlob = 0; | |
| 2035 | + if(g.argc>2){ | |
| 2036 | + /* | |
| 2037 | + ** Trailing filenames on the command line. These require extra | |
| 2038 | + ** care to avoid modifying unmanaged or out-of-tree files and | |
| 2039 | + ** finding an associated --checkin timestamp. | |
| 2040 | + */ | |
| 2041 | + int i; | |
| 2042 | + Blob treeNameBuf = empty_blob; | |
| 2043 | + for( i = 2; i < g.argc; ++i, | |
| 2044 | + blob_reset(&treeNameBuf) ){ | |
| 2045 | + char const * zArg = g.argv[i]; | |
| 2046 | + char const * zTreeFile; /* repo-relative filename */ | |
| 2047 | + char const * zAbs; /* absolute filename */ | |
| 2048 | + i64 newMtime = nowTime; | |
| 2049 | + int nameCheck; | |
| 2050 | + int fid; /* vfile.mrid of file */ | |
| 2051 | + absBuffer.nUsed = 0; | |
| 2052 | + nameCheck = file_tree_name( zArg, &treeNameBuf, 0, 0 ); | |
| 2053 | + if(nameCheck==0){ | |
| 2054 | + if(quietFlag==0){ | |
| 2055 | + fossil_print("SKIPPING out-of-tree file: %s\n", zArg); | |
| 2056 | + } | |
| 2057 | + continue; | |
| 2058 | + } | |
| 2059 | + zTreeFile = blob_str(&treeNameBuf); | |
| 2060 | + fid = touch_cmd_vfile_mrid( vid, zTreeFile ); | |
| 2061 | + if(fid==0){ | |
| 2062 | + if(quietFlag==0){ | |
| 2063 | + fossil_print("SKIPPING unmanaged file: %s\n", zArg); | |
| 2064 | + } | |
| 2065 | + continue; | |
| 2066 | + } | |
| 2067 | + blob_appendf(&absBuffer, "%s%s", g.zLocalRoot, zTreeFile); | |
| 2068 | + zAbs = blob_str(&absBuffer); | |
| 2069 | + if(timeFlag<0){/*--checkin*/ | |
| 2070 | + if(mtime_of_manifest_file( vid, fid, &newMtime )!=0){ | |
| 2071 | + fossil_fatal("Could not resolve --checkin mtime of %s", zTreeFile); | |
| 2072 | + } | |
| 2073 | + }else{ | |
| 2074 | + assert(newMtime>0); | |
| 2075 | + } | |
| 2076 | + changeCount += | |
| 2077 | + touch_cmd_stamp_one_file( zAbs, zArg, newMtime, | |
| 2078 | + dryRunFlag, verboseFlag ); | |
| 2079 | + } | |
| 2080 | + } | |
| 2081 | + db_end_transaction(0); | |
| 2082 | + blob_reset(&absBuffer); | |
| 1922 | 2083 | if( dryRunFlag!=0 ){ |
| 1923 | 2084 | fossil_print("dry-run: would have touched %d file(s)\n", |
| 1924 | 2085 | changeCount); |
| 1925 | - }else if( verboseFlag!=0 ){ | |
| 1926 | - fossil_print("Touched %d file(s)\n", changeCount); | |
| 1927 | 2086 | } |
| 2087 | + fossil_print("Touched %d file(s)\n", changeCount); | |
| 1928 | 2088 | } |
| 1929 | 2089 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1799,43 +1799,114 @@ | |
| 1799 | zDir = g.argv[2]; |
| 1800 | zGlob = g.argc==4 ? g.argv[3] : 0; |
| 1801 | fossil_print("%d\n", file_directory_size(zDir, zGlob, omitDotFiles)); |
| 1802 | } |
| 1803 | |
| 1804 | |
| 1805 | /* |
| 1806 | ** COMMAND: touch* |
| 1807 | ** |
| 1808 | ** Usage: %fossil touch ?OPTIONS? |
| 1809 | ** |
| 1810 | ** For each file in the current checkout matching one of the provided |
| 1811 | ** list of glob patterns, or all files if no globs are provided, sets |
| 1812 | ** the file's mtime to the time of the last checkin which modified |
| 1813 | ** that file. |
| 1814 | ** |
| 1815 | ** This command gets its name from the conventional Unix "touch" |
| 1816 | ** command. |
| 1817 | ** |
| 1818 | ** Options: |
| 1819 | ** --now Stamp each affected file with the current time. |
| 1820 | ** This is the default behavior. |
| 1821 | ** -c|--checkin Stamp each affected file with the time of the |
| 1822 | ** most recent checkin which modified that file. |
| 1823 | ** -g GLOBLIST Comma-separated list of glob patterns. Default |
| 1824 | ** is to touch all SCM-controlled files. |
| 1825 | ** -G GLOBFILE Similar to -g but reads its globs from a |
| 1826 | ** fossil-conventional glob list file. |
| 1827 | ** -v|-verbose Outputs information about its globs and each |
| 1828 | ** file it touches. |
| 1829 | ** -n|--dry-run Outputs which files would require touching, |
| 1830 | ** but does not touch them. |
| 1831 | ** |
| 1832 | ** Only one of -g or -G may be used. If neither is provided, |
| 1833 | ** the effect is as if a glob of '*' were provided. |
| 1834 | ** |
| 1835 | ** Only one of --now and --checkin may be used. The default |
| 1836 | ** is --now. |
| 1837 | ** |
| 1838 | */ |
| 1839 | void touch_cmd(){ |
| 1840 | const char * zGlobList; /* -g List of glob patterns */ |
| 1841 | const char * zGlobFile; /* -G File of glob patterns */ |
| @@ -1842,87 +1913,176 @@ | |
| 1842 | Glob * pGlob = 0; /* List of glob patterns */ |
| 1843 | int verboseFlag; |
| 1844 | int dryRunFlag; |
| 1845 | int vid; /* Checkout version */ |
| 1846 | int changeCount = 0; /* Number of files touched */ |
| 1847 | int checkinFlag; /* -c|--checkin */ |
| 1848 | i64 const nowTime = time(0); |
| 1849 | Stmt q; |
| 1850 | |
| 1851 | verboseFlag = find_option("verbose","v",0)!=0; |
| 1852 | dryRunFlag = find_option("dry-run","n",0)!=0; |
| 1853 | zGlobList = find_option("glob", "g",1); |
| 1854 | zGlobFile = find_option("globfile", "G",1); |
| 1855 | checkinFlag = find_option("checkin","c",0)!=0; |
| 1856 | |
| 1857 | if(find_option("now",0,0)!=0 && checkinFlag!=0){ |
| 1858 | fossil_fatal("Options --checkin and --now may not be used together."); |
| 1859 | } |
| 1860 | if(zGlobList && zGlobFile){ |
| 1861 | fossil_fatal("Options -g and -G may not be used together."); |
| 1862 | } |
| 1863 | |
| 1864 | verify_all_options(); |
| 1865 | |
| 1866 | db_must_be_within_tree(); |
| 1867 | vid = db_lget_int("checkout", 0); |
| 1868 | if(vid==0){ |
| 1869 | fossil_fatal("Cannot determine checkout version."); |
| 1870 | } |
| 1871 | if(zGlobList){ |
| 1872 | pGlob = *zGlobList ? glob_create(zGlobList) : 0; |
| 1873 | }else if(zGlobFile){ |
| 1874 | Blob globs; |
| 1875 | blob_read_from_file(&globs, zGlobFile, ExtFILE); |
| 1876 | pGlob = glob_create( globs.aData ); |
| 1877 | blob_reset(&globs); |
| 1878 | } |
| 1879 | db_begin_transaction(); |
| 1880 | db_prepare(&q, "SELECT vfile.mrid, pathname " |
| 1881 | "FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid " |
| 1882 | "WHERE vid=%d", vid); |
| 1883 | if( pGlob && verboseFlag!=0 ){ |
| 1884 | int i; |
| 1885 | for(i=0; i<pGlob->nPattern; ++i){ |
| 1886 | fossil_print("glob: %s\n", pGlob->azPattern[i]); |
| 1887 | } |
| 1888 | } |
| 1889 | if( verboseFlag ){ |
| 1890 | if(checkinFlag){ |
| 1891 | fossil_print("Using mtime from most recent commit(s).\n"); |
| 1892 | }else{ |
| 1893 | fossil_print("Using current time.\n"); |
| 1894 | } |
| 1895 | } |
| 1896 | while(SQLITE_ROW==db_step(&q)){ |
| 1897 | const char * zName = db_column_text(&q, 1); |
| 1898 | int const fid = db_column_int(&q, 0); |
| 1899 | i64 newMtime = checkinFlag ? 0 : nowTime; |
| 1900 | i64 currentMtime; |
| 1901 | if(pGlob){ |
| 1902 | if(glob_match(pGlob, zName)==0) continue; |
| 1903 | } |
| 1904 | currentMtime = file_mtime(zName, 0); |
| 1905 | if( newMtime || mtime_of_manifest_file(vid, fid, &newMtime)==0 ){ |
| 1906 | if( currentMtime!=newMtime ){ |
| 1907 | ++changeCount; |
| 1908 | if( dryRunFlag!=0 ){ |
| 1909 | fossil_print( "dry-run: %s\n", zName ); |
| 1910 | }else{ |
| 1911 | file_set_mtime(zName, newMtime); |
| 1912 | if( verboseFlag!=0 ){ |
| 1913 | fossil_print( "touched %s\n", zName ); |
| 1914 | } |
| 1915 | } |
| 1916 | } |
| 1917 | } |
| 1918 | } |
| 1919 | db_finalize(&q); |
| 1920 | db_end_transaction(0); |
| 1921 | glob_free(pGlob); |
| 1922 | if( dryRunFlag!=0 ){ |
| 1923 | fossil_print("dry-run: would have touched %d file(s)\n", |
| 1924 | changeCount); |
| 1925 | }else if( verboseFlag!=0 ){ |
| 1926 | fossil_print("Touched %d file(s)\n", changeCount); |
| 1927 | } |
| 1928 | } |
| 1929 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1799,43 +1799,114 @@ | |
| 1799 | zDir = g.argv[2]; |
| 1800 | zGlob = g.argc==4 ? g.argv[3] : 0; |
| 1801 | fossil_print("%d\n", file_directory_size(zDir, zGlob, omitDotFiles)); |
| 1802 | } |
| 1803 | |
| 1804 | /* |
| 1805 | ** Internal helper for touch_cmd(). zAbsName must be resolvable as-is |
| 1806 | ** to a file - this function does not expand/normalize it. i.e. it |
| 1807 | ** "really should" be an absolute path. zTreeName is strictly |
| 1808 | ** cosmetic: it is used when dryRunFlag or verboseFlag generate |
| 1809 | ** output. It is assumed to be a repo-relative or or subdir-relative |
| 1810 | ** filename. |
| 1811 | ** |
| 1812 | ** newMTime is the file's new timestamp (Unix epoch). |
| 1813 | ** |
| 1814 | ** Returns 1 if it sets zAbsName's mtime, 0 if it does not (indicating |
| 1815 | ** that the file already has that timestamp). Dies fatally if given an |
| 1816 | ** unresolvable filename. If dryRunFlag is true then it outputs the |
| 1817 | ** name of the file it would have timestamped but does not stamp the |
| 1818 | ** file. If verboseFlag is true, it outputs a message if the files |
| 1819 | ** timestamp is actually modified. |
| 1820 | */ |
| 1821 | static int touch_cmd_stamp_one_file(char const *zAbsName, |
| 1822 | char const *zTreeName, |
| 1823 | i64 newMtime, int dryRunFlag, |
| 1824 | int verboseFlag){ |
| 1825 | i64 const currentMtime = file_mtime(zAbsName, 0); |
| 1826 | if(currentMtime<0){ |
| 1827 | fossil_fatal("Cannot stat file: %s\n", zAbsName); |
| 1828 | }else if(currentMtime==newMtime){ |
| 1829 | return 0; |
| 1830 | }else if( dryRunFlag!=0 ){ |
| 1831 | fossil_print( "dry-run: %s\n", zTreeName ); |
| 1832 | }else{ |
| 1833 | file_set_mtime(zAbsName, newMtime); |
| 1834 | if( verboseFlag!=0 ){ |
| 1835 | fossil_print( "touched %s\n", zTreeName ); |
| 1836 | } |
| 1837 | } |
| 1838 | return 1; |
| 1839 | } |
| 1840 | |
| 1841 | /* |
| 1842 | ** Internal helper for touch_cmd(). If the given name is found in the |
| 1843 | ** given checkout version, which MUST be the checkout version |
| 1844 | ** currently populating the vfile table, the vfile.mrid value for the |
| 1845 | ** file is returned, else 0 is returned. zName must be resolvable |
| 1846 | ** as-is - this function performs neither expands nor normalizes it. |
| 1847 | */ |
| 1848 | static int touch_cmd_vfile_mrid( int vid, char const *zName ){ |
| 1849 | int mrid = 0; |
| 1850 | static Stmt q = empty_Stmt_m; |
| 1851 | db_static_prepare(&q, "SELECT vfile.mrid " |
| 1852 | "FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid " |
| 1853 | "WHERE vid=:vid AND pathname=:pathname %s", |
| 1854 | filename_collation()); |
| 1855 | db_bind_int(&q, ":vid", vid); |
| 1856 | db_bind_text(&q, ":pathname", zName); |
| 1857 | if(SQLITE_ROW==db_step(&q)){ |
| 1858 | mrid = db_column_int(&q, 0); |
| 1859 | } |
| 1860 | db_reset(&q); |
| 1861 | return mrid; |
| 1862 | } |
| 1863 | |
| 1864 | /* |
| 1865 | ** COMMAND: touch* |
| 1866 | ** |
| 1867 | ** Usage: %fossil touch ?OPTIONS? ?FILENAME...? |
| 1868 | ** |
| 1869 | ** For each file in the current checkout matching one of the provided |
| 1870 | ** list of glob patterns and/or file names, the file's mtime is |
| 1871 | ** updated to a value specified by one of the flags --checkout, |
| 1872 | ** --checkin, or --now. |
| 1873 | ** |
| 1874 | ** If neither glob patterns nor filenames are provided, it operates on |
| 1875 | ** all files managed by the currently checked-out version. |
| 1876 | ** |
| 1877 | ** This command gets its name from the conventional Unix "touch" |
| 1878 | ** command. |
| 1879 | ** |
| 1880 | ** Options: |
| 1881 | ** --now Stamp each affected file with the current time. |
| 1882 | ** This is the default behavior. |
| 1883 | ** -c|--checkin Stamp each affected file with the time of the |
| 1884 | ** most recent check-in which modified that file. |
| 1885 | ** -C|--checkout Stamp each affected file with the time of the |
| 1886 | ** currently-checked-out version. |
| 1887 | ** -g GLOBLIST Comma-separated list of glob patterns. Default |
| 1888 | ** is to touch all SCM-controlled files. |
| 1889 | ** -G GLOBFILE Similar to -g but reads its globs from a |
| 1890 | ** fossil-conventional glob list file. |
| 1891 | ** -v|-verbose Outputs extra information about its globs |
| 1892 | ** and each file it touches. |
| 1893 | ** -n|--dry-run Outputs which files would require touching, |
| 1894 | ** but does not touch them. |
| 1895 | ** -q|--quiet Suppress warnings when skipping unmanaged |
| 1896 | ** or out-of-tree files. |
| 1897 | ** |
| 1898 | ** Only one of --now, --checkin, and --checkout may be used. The |
| 1899 | ** default is --now. |
| 1900 | ** |
| 1901 | ** Only one of -g or -G may be used. If neither is provided and no |
| 1902 | ** additional filenames are provided, the effect is as if a glob of |
| 1903 | ** '*' were provided. Note that all glob patterns provided via these |
| 1904 | ** flags are always evaluated as if they are relative to the top of |
| 1905 | ** the source tree, not the current working (sub)directory. Filenames |
| 1906 | ** provided without these flags, on the other hand, are treated as |
| 1907 | ** relative to the current directory. |
| 1908 | ** |
| 1909 | */ |
| 1910 | void touch_cmd(){ |
| 1911 | const char * zGlobList; /* -g List of glob patterns */ |
| 1912 | const char * zGlobFile; /* -G File of glob patterns */ |
| @@ -1842,87 +1913,176 @@ | |
| 1913 | Glob * pGlob = 0; /* List of glob patterns */ |
| 1914 | int verboseFlag; |
| 1915 | int dryRunFlag; |
| 1916 | int vid; /* Checkout version */ |
| 1917 | int changeCount = 0; /* Number of files touched */ |
| 1918 | int quietFlag = 0; /* -q|--quiet */ |
| 1919 | int timeFlag; /* -1==--checkin, 1==--checkout, 0==--now */ |
| 1920 | i64 nowTime = 0; /* Timestamp of --now or --checkout */ |
| 1921 | Stmt q; |
| 1922 | Blob absBuffer = empty_blob; |
| 1923 | |
| 1924 | verboseFlag = find_option("verbose","v",0)!=0; |
| 1925 | quietFlag = find_option("quiet","q",0)!=0; |
| 1926 | dryRunFlag = find_option("dry-run","n",0)!=0 |
| 1927 | || find_option("dryrun",0,0)!=0; |
| 1928 | zGlobList = find_option("glob", "g",1); |
| 1929 | zGlobFile = find_option("globfile", "G",1); |
| 1930 | |
| 1931 | if(zGlobList && zGlobFile){ |
| 1932 | fossil_fatal("Options -g and -G may not be used together."); |
| 1933 | } |
| 1934 | |
| 1935 | { |
| 1936 | int const ci = |
| 1937 | (find_option("checkin","c",0) || find_option("check-in",0,0)) |
| 1938 | ? 1 : 0; |
| 1939 | int const co = find_option("checkout","C",0) ? 1 : 0; |
| 1940 | int const now = find_option("now",0,0) ? 1 : 0; |
| 1941 | if(ci + co + now > 1){ |
| 1942 | fossil_fatal("Options --checkin, --checkout, and --now may " |
| 1943 | "not be used together."); |
| 1944 | }else if(co){ |
| 1945 | timeFlag = 1; |
| 1946 | if(verboseFlag){ |
| 1947 | fossil_print("Timestamp = current checkout version.\n"); |
| 1948 | } |
| 1949 | }else if(ci){ |
| 1950 | timeFlag = -1; |
| 1951 | if(verboseFlag){ |
| 1952 | fossil_print("Timestamp = checkin in which each file was " |
| 1953 | "most recently modified.\n"); |
| 1954 | } |
| 1955 | }else{ |
| 1956 | timeFlag = 0; |
| 1957 | if(verboseFlag){ |
| 1958 | fossil_print("Timestamp = current system time.\n"); |
| 1959 | } |
| 1960 | } |
| 1961 | } |
| 1962 | |
| 1963 | verify_all_options(); |
| 1964 | |
| 1965 | db_must_be_within_tree(); |
| 1966 | vid = db_lget_int("checkout", 0); |
| 1967 | if(vid==0){ |
| 1968 | fossil_fatal("Cannot determine checkout version."); |
| 1969 | } |
| 1970 | |
| 1971 | if(zGlobList){ |
| 1972 | pGlob = *zGlobList ? glob_create(zGlobList) : 0; |
| 1973 | }else if(zGlobFile){ |
| 1974 | Blob globs = empty_blob; |
| 1975 | blob_read_from_file(&globs, zGlobFile, ExtFILE); |
| 1976 | pGlob = glob_create( globs.aData ); |
| 1977 | blob_reset(&globs); |
| 1978 | } |
| 1979 | if( pGlob && verboseFlag!=0 ){ |
| 1980 | int i; |
| 1981 | for(i=0; i<pGlob->nPattern; ++i){ |
| 1982 | fossil_print("glob: %s\n", pGlob->azPattern[i]); |
| 1983 | } |
| 1984 | } |
| 1985 | |
| 1986 | db_begin_transaction(); |
| 1987 | if(timeFlag==0){/*--now*/ |
| 1988 | nowTime = time(0); |
| 1989 | }else if(timeFlag>0){/*--checkout: get the checkout |
| 1990 | manifest's timestamp*/ |
| 1991 | assert(vid>0); |
| 1992 | nowTime = db_int64(-1, |
| 1993 | "SELECT CAST(strftime('%%s'," |
| 1994 | "(SELECT mtime FROM event WHERE objid=%d)" |
| 1995 | ") AS INTEGER)", vid); |
| 1996 | if(nowTime<0){ |
| 1997 | fossil_fatal("Could not determine out checkout version's time!"); |
| 1998 | } |
| 1999 | }else{ /* --checkin */ |
| 2000 | assert(0 == nowTime); |
| 2001 | } |
| 2002 | if((pGlob && pGlob->nPattern>0) |
| 2003 | || g.argc<3 /* no non-flag arguments */ ){ |
| 2004 | /* |
| 2005 | ** We have either globs or no trailing filenames (in which case an |
| 2006 | ** effective glob pattern of '*' is assumed). If there are neither |
| 2007 | ** globs nor filenames then we operate on all managed files. |
| 2008 | */ |
| 2009 | db_prepare(&q, |
| 2010 | "SELECT vfile.mrid, pathname " |
| 2011 | "FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid " |
| 2012 | "WHERE vid=%d", vid); |
| 2013 | while(SQLITE_ROW==db_step(&q)){ |
| 2014 | int const fid = db_column_int(&q, 0); |
| 2015 | const char * zName = db_column_text(&q, 1); |
| 2016 | i64 newMtime = nowTime; |
| 2017 | char const * zAbs = 0; /* absolute path */ |
| 2018 | absBuffer.nUsed = 0; |
| 2019 | assert(timeFlag<0 ? newMtime==0 : newMtime>0); |
| 2020 | if(pGlob){ |
| 2021 | if(glob_match(pGlob, zName)==0) continue; |
| 2022 | } |
| 2023 | blob_appendf( &absBuffer, "%s%s", g.zLocalRoot, zName ); |
| 2024 | zAbs = blob_str(&absBuffer); |
| 2025 | if( newMtime || mtime_of_manifest_file(vid, fid, &newMtime)==0 ){ |
| 2026 | changeCount += |
| 2027 | touch_cmd_stamp_one_file( zAbs, zName, newMtime, |
| 2028 | dryRunFlag, verboseFlag ); |
| 2029 | } |
| 2030 | } |
| 2031 | db_finalize(&q); |
| 2032 | } |
| 2033 | glob_free(pGlob); |
| 2034 | pGlob = 0; |
| 2035 | if(g.argc>2){ |
| 2036 | /* |
| 2037 | ** Trailing filenames on the command line. These require extra |
| 2038 | ** care to avoid modifying unmanaged or out-of-tree files and |
| 2039 | ** finding an associated --checkin timestamp. |
| 2040 | */ |
| 2041 | int i; |
| 2042 | Blob treeNameBuf = empty_blob; |
| 2043 | for( i = 2; i < g.argc; ++i, |
| 2044 | blob_reset(&treeNameBuf) ){ |
| 2045 | char const * zArg = g.argv[i]; |
| 2046 | char const * zTreeFile; /* repo-relative filename */ |
| 2047 | char const * zAbs; /* absolute filename */ |
| 2048 | i64 newMtime = nowTime; |
| 2049 | int nameCheck; |
| 2050 | int fid; /* vfile.mrid of file */ |
| 2051 | absBuffer.nUsed = 0; |
| 2052 | nameCheck = file_tree_name( zArg, &treeNameBuf, 0, 0 ); |
| 2053 | if(nameCheck==0){ |
| 2054 | if(quietFlag==0){ |
| 2055 | fossil_print("SKIPPING out-of-tree file: %s\n", zArg); |
| 2056 | } |
| 2057 | continue; |
| 2058 | } |
| 2059 | zTreeFile = blob_str(&treeNameBuf); |
| 2060 | fid = touch_cmd_vfile_mrid( vid, zTreeFile ); |
| 2061 | if(fid==0){ |
| 2062 | if(quietFlag==0){ |
| 2063 | fossil_print("SKIPPING unmanaged file: %s\n", zArg); |
| 2064 | } |
| 2065 | continue; |
| 2066 | } |
| 2067 | blob_appendf(&absBuffer, "%s%s", g.zLocalRoot, zTreeFile); |
| 2068 | zAbs = blob_str(&absBuffer); |
| 2069 | if(timeFlag<0){/*--checkin*/ |
| 2070 | if(mtime_of_manifest_file( vid, fid, &newMtime )!=0){ |
| 2071 | fossil_fatal("Could not resolve --checkin mtime of %s", zTreeFile); |
| 2072 | } |
| 2073 | }else{ |
| 2074 | assert(newMtime>0); |
| 2075 | } |
| 2076 | changeCount += |
| 2077 | touch_cmd_stamp_one_file( zAbs, zArg, newMtime, |
| 2078 | dryRunFlag, verboseFlag ); |
| 2079 | } |
| 2080 | } |
| 2081 | db_end_transaction(0); |
| 2082 | blob_reset(&absBuffer); |
| 2083 | if( dryRunFlag!=0 ){ |
| 2084 | fossil_print("dry-run: would have touched %d file(s)\n", |
| 2085 | changeCount); |
| 2086 | } |
| 2087 | fossil_print("Touched %d file(s)\n", changeCount); |
| 2088 | } |
| 2089 |