##// END OF EJS Templates
cmdutil: expand filename format string by templater (BC)...
Yuya Nishihara -
r36528:aa329402 default
parent child Browse files
Show More
@@ -42,6 +42,7 b' from . import ('
42 42 scmutil,
43 43 smartset,
44 44 subrepoutil,
45 templatekw,
45 46 templater,
46 47 util,
47 48 vfs as vfsmod,
@@ -891,46 +892,98 b' def getcommiteditor(edit=False, finishde'
891 892 else:
892 893 return commiteditor
893 894
894 def makefilename(ctx, pat,
895 total=None, seqno=None, revwidth=None, pathname=None):
895 def rendertemplate(ctx, tmpl, props=None):
896 """Expand a literal template 'tmpl' byte-string against one changeset
897
898 Each props item must be a stringify-able value or a callable returning
899 such value, i.e. no bare list nor dict should be passed.
900 """
901 repo = ctx.repo()
902 tres = formatter.templateresources(repo.ui, repo)
903 t = formatter.maketemplater(repo.ui, tmpl, defaults=templatekw.keywords,
904 resources=tres)
905 mapping = {'ctx': ctx, 'revcache': {}}
906 if props:
907 mapping.update(props)
908 return t.render(mapping)
909
910 def _buildfntemplate(pat, total=None, seqno=None, revwidth=None, pathname=None):
911 r"""Convert old-style filename format string to template string
912
913 >>> _buildfntemplate(b'foo-%b-%n.patch', seqno=0)
914 'foo-{reporoot|basename}-{seqno}.patch'
915 >>> _buildfntemplate(b'%R{tags % "{tag}"}%H')
916 '{rev}{tags % "{tag}"}{node}'
917
918 '\' in outermost strings has to be escaped because it is a directory
919 separator on Windows:
920
921 >>> _buildfntemplate(b'c:\\tmp\\%R\\%n.patch', seqno=0)
922 'c:\\\\tmp\\\\{rev}\\\\{seqno}.patch'
923 >>> _buildfntemplate(b'\\\\foo\\bar.patch')
924 '\\\\\\\\foo\\\\bar.patch'
925 >>> _buildfntemplate(b'\\{tags % "{tag}"}')
926 '\\\\{tags % "{tag}"}'
927
928 but inner strings follow the template rules (i.e. '\' is taken as an
929 escape character):
930
931 >>> _buildfntemplate(br'{"c:\tmp"}', seqno=0)
932 '{"c:\\tmp"}'
933 """
896 934 expander = {
897 'H': lambda: ctx.hex(),
898 'R': lambda: '%d' % ctx.rev(),
899 'h': lambda: short(ctx.node()),
900 'm': lambda: re.sub('[^\w]', '_',
901 ctx.description().strip().splitlines()[0]),
902 'r': lambda: ('%d' % ctx.rev()).zfill(revwidth or 0),
903 '%': lambda: '%',
904 'b': lambda: os.path.basename(ctx.repo().root),
905 }
935 b'H': b'{node}',
936 b'R': b'{rev}',
937 b'h': b'{node|short}',
938 b'm': br'{sub(r"[^\w]", "_", desc|firstline)}',
939 b'r': b'{if(revwidth, pad(rev, revwidth, "0", left=True), rev)}',
940 b'%': b'%',
941 b'b': b'{reporoot|basename}',
942 }
906 943 if total is not None:
907 expander['N'] = lambda: '%d' % total
944 expander[b'N'] = b'{total}'
908 945 if seqno is not None:
909 expander['n'] = lambda: '%d' % seqno
946 expander[b'n'] = b'{seqno}'
910 947 if total is not None and seqno is not None:
911 expander['n'] = (lambda: ('%d' % seqno).zfill(len('%d' % total)))
948 expander[b'n'] = b'{pad(seqno, total|stringify|count, "0", left=True)}'
912 949 if pathname is not None:
913 expander['s'] = lambda: os.path.basename(pathname)
914 expander['d'] = lambda: os.path.dirname(pathname) or '.'
915 expander['p'] = lambda: pathname
950 expander[b's'] = b'{pathname|basename}'
951 expander[b'd'] = b'{if(pathname|dirname, pathname|dirname, ".")}'
952 expander[b'p'] = b'{pathname}'
916 953
917 954 newname = []
918 patlen = len(pat)
919 i = 0
920 while i < patlen:
921 c = pat[i:i + 1]
922 if c == '%':
923 i += 1
924 c = pat[i:i + 1]
955 for typ, start, end in templater.scantemplate(pat, raw=True):
956 if typ != b'string':
957 newname.append(pat[start:end])
958 continue
959 i = start
960 while i < end:
961 n = pat.find(b'%', i, end)
962 if n < 0:
963 newname.append(util.escapestr(pat[i:end]))
964 break
965 newname.append(util.escapestr(pat[i:n]))
966 if n + 2 > end:
967 raise error.Abort(_("incomplete format spec in output "
968 "filename"))
969 c = pat[n + 1:n + 2]
970 i = n + 2
925 971 try:
926 c = expander[c]()
972 newname.append(expander[c])
927 973 except KeyError:
928 974 raise error.Abort(_("invalid format spec '%%%s' in output "
929 975 "filename") % c)
930 newname.append(c)
931 i += 1
932 976 return ''.join(newname)
933 977
978 def makefilename(ctx, pat, **props):
979 if not pat:
980 return pat
981 tmpl = _buildfntemplate(pat, **props)
982 # BUG: alias expansion shouldn't be made against template fragments
983 # rewritten from %-format strings, but we have no easy way to partially
984 # disable the expansion.
985 return rendertemplate(ctx, tmpl, pycompat.byteskwargs(props))
986
934 987 def isstdiofilename(pat):
935 988 """True if the given pat looks like a filename denoting stdin/stdout"""
936 989 return not pat or pat == '-'
@@ -1285,7 +1285,9 b' def cat(ui, repo, file1, *pats, **opts):'
1285 1285 no revision is given, the parent of the working directory is used.
1286 1286
1287 1287 Output may be to a file, in which case the name of the file is
1288 given using a format string. The formatting rules as follows:
1288 given using a template string. See :hg:`help templates`. In addition
1289 to the common template keywords, the following formatting rules are
1290 supported:
1289 1291
1290 1292 :``%%``: literal "%" character
1291 1293 :``%s``: basename of file being printed
@@ -1296,6 +1298,7 b' def cat(ui, repo, file1, *pats, **opts):'
1296 1298 :``%h``: short-form changeset hash (12 hexadecimal digits)
1297 1299 :``%r``: zero-padded changeset revision number
1298 1300 :``%b``: basename of the exporting repository
1301 :``\\``: literal "\\" character
1299 1302
1300 1303 Returns 0 on success.
1301 1304 """
@@ -1901,7 +1904,9 b' def export(ui, repo, *changesets, **opts'
1901 1904 first parent only.
1902 1905
1903 1906 Output may be to a file, in which case the name of the file is
1904 given using a format string. The formatting rules are as follows:
1907 given using a template string. See :hg:`help templates`. In addition
1908 to the common template keywords, the following formatting rules are
1909 supported:
1905 1910
1906 1911 :``%%``: literal "%" character
1907 1912 :``%H``: changeset hash (40 hexadecimal digits)
@@ -1912,6 +1917,7 b' def export(ui, repo, *changesets, **opts'
1912 1917 :``%m``: first line of the commit message (only alphanumeric characters)
1913 1918 :``%n``: zero-padded sequence number, starting at 1
1914 1919 :``%r``: zero-padded changeset revision number
1920 :``\\``: literal "\\" character
1915 1921
1916 1922 Without the -a/--text option, export will avoid generating diffs
1917 1923 of files it detects as binary. With -a, export will generate a
@@ -42,6 +42,7 b' def testmod(name, optionflags=0, testtar'
42 42
43 43 testmod('mercurial.changegroup')
44 44 testmod('mercurial.changelog')
45 testmod('mercurial.cmdutil')
45 46 testmod('mercurial.color')
46 47 testmod('mercurial.config')
47 48 testmod('mercurial.context')
@@ -186,11 +186,45 b' Checking if only alphanumeric characters'
186 186 exporting patch:
187 187 ___________0123456789_______ABCDEFGHIJKLMNOPQRSTUVWXYZ______abcdefghijklmnopqrstuvwxyz____.patch
188 188
189 Template fragments in file name:
190
191 $ hg export -v -o '{node|shortest}.patch' tip
192 exporting patch:
193 197e.patch
194
195 Backslash should be preserved because it is a directory separator on Windows:
196
197 $ mkdir out
198 $ hg export -v -o 'out\{node|shortest}.patch' tip
199 exporting patch:
200 out\197e.patch
201
202 Still backslash is taken as an escape character in inner template strings:
203
204 $ hg export -v -o '{"out\{foo}.patch"}' tip
205 exporting patch:
206 out{foo}.patch
207
189 208 Invalid pattern in file name:
190 209
191 210 $ hg export -o '%x.patch' tip
192 211 abort: invalid format spec '%x' in output filename
193 212 [255]
213 $ hg export -o '%' tip
214 abort: incomplete format spec in output filename
215 [255]
216 $ hg export -o '%{"foo"}' tip
217 abort: incomplete format spec in output filename
218 [255]
219 $ hg export -o '%m{' tip
220 hg: parse error at 3: unterminated template expansion
221 [255]
222 $ hg export -o '%\' tip
223 abort: invalid format spec '%\' in output filename
224 [255]
225 $ hg export -o '\%' tip
226 abort: incomplete format spec in output filename
227 [255]
194 228
195 229 Catch exporting unknown revisions (especially empty revsets, see issue3353)
196 230
General Comments 0
You need to be logged in to leave comments. Login now