##// END OF EJS Templates
check if a file is ignored before complaining if it is an unsupported type
Benoit Boissinot -
r1396:8c3e2a25 default
parent child Browse files
Show More
@@ -1,426 +1,425 b''
1 """
1 """
2 dirstate.py - working directory tracking for mercurial
2 dirstate.py - working directory tracking for mercurial
3
3
4 Copyright 2005 Matt Mackall <mpm@selenic.com>
4 Copyright 2005 Matt Mackall <mpm@selenic.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8 """
8 """
9
9
10 import struct, os
10 import struct, os
11 from node import *
11 from node import *
12 from demandload import *
12 from demandload import *
13 demandload(globals(), "time bisect stat util re")
13 demandload(globals(), "time bisect stat util re")
14
14
15 class dirstate:
15 class dirstate:
16 def __init__(self, opener, ui, root):
16 def __init__(self, opener, ui, root):
17 self.opener = opener
17 self.opener = opener
18 self.root = root
18 self.root = root
19 self.dirty = 0
19 self.dirty = 0
20 self.ui = ui
20 self.ui = ui
21 self.map = None
21 self.map = None
22 self.pl = None
22 self.pl = None
23 self.copies = {}
23 self.copies = {}
24 self.ignorefunc = None
24 self.ignorefunc = None
25 self.blockignore = False
25 self.blockignore = False
26
26
27 def wjoin(self, f):
27 def wjoin(self, f):
28 return os.path.join(self.root, f)
28 return os.path.join(self.root, f)
29
29
30 def getcwd(self):
30 def getcwd(self):
31 cwd = os.getcwd()
31 cwd = os.getcwd()
32 if cwd == self.root: return ''
32 if cwd == self.root: return ''
33 return cwd[len(self.root) + 1:]
33 return cwd[len(self.root) + 1:]
34
34
35 def hgignore(self):
35 def hgignore(self):
36 '''return the contents of .hgignore as a list of patterns.
36 '''return the contents of .hgignore as a list of patterns.
37
37
38 trailing white space is dropped.
38 trailing white space is dropped.
39 the escape character is backslash.
39 the escape character is backslash.
40 comments start with #.
40 comments start with #.
41 empty lines are skipped.
41 empty lines are skipped.
42
42
43 lines can be of the following formats:
43 lines can be of the following formats:
44
44
45 syntax: regexp # defaults following lines to non-rooted regexps
45 syntax: regexp # defaults following lines to non-rooted regexps
46 syntax: glob # defaults following lines to non-rooted globs
46 syntax: glob # defaults following lines to non-rooted globs
47 re:pattern # non-rooted regular expression
47 re:pattern # non-rooted regular expression
48 glob:pattern # non-rooted glob
48 glob:pattern # non-rooted glob
49 pattern # pattern of the current default type'''
49 pattern # pattern of the current default type'''
50 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
50 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
51 def parselines(fp):
51 def parselines(fp):
52 for line in fp:
52 for line in fp:
53 escape = False
53 escape = False
54 for i in xrange(len(line)):
54 for i in xrange(len(line)):
55 if escape: escape = False
55 if escape: escape = False
56 elif line[i] == '\\': escape = True
56 elif line[i] == '\\': escape = True
57 elif line[i] == '#': break
57 elif line[i] == '#': break
58 line = line[:i].rstrip()
58 line = line[:i].rstrip()
59 if line: yield line
59 if line: yield line
60 pats = []
60 pats = []
61 try:
61 try:
62 fp = open(self.wjoin('.hgignore'))
62 fp = open(self.wjoin('.hgignore'))
63 syntax = 'relre:'
63 syntax = 'relre:'
64 for line in parselines(fp):
64 for line in parselines(fp):
65 if line.startswith('syntax:'):
65 if line.startswith('syntax:'):
66 s = line[7:].strip()
66 s = line[7:].strip()
67 try:
67 try:
68 syntax = syntaxes[s]
68 syntax = syntaxes[s]
69 except KeyError:
69 except KeyError:
70 self.ui.warn("ignoring invalid syntax '%s'\n" % s)
70 self.ui.warn("ignoring invalid syntax '%s'\n" % s)
71 continue
71 continue
72 pat = syntax + line
72 pat = syntax + line
73 for s in syntaxes.values():
73 for s in syntaxes.values():
74 if line.startswith(s):
74 if line.startswith(s):
75 pat = line
75 pat = line
76 break
76 break
77 pats.append(pat)
77 pats.append(pat)
78 except IOError: pass
78 except IOError: pass
79 return pats
79 return pats
80
80
81 def ignore(self, fn):
81 def ignore(self, fn):
82 '''default match function used by dirstate and localrepository.
82 '''default match function used by dirstate and localrepository.
83 this honours the .hgignore file, and nothing more.'''
83 this honours the .hgignore file, and nothing more.'''
84 if self.blockignore:
84 if self.blockignore:
85 return False
85 return False
86 if not self.ignorefunc:
86 if not self.ignorefunc:
87 ignore = self.hgignore()
87 ignore = self.hgignore()
88 if ignore:
88 if ignore:
89 files, self.ignorefunc, anypats = util.matcher(self.root,
89 files, self.ignorefunc, anypats = util.matcher(self.root,
90 inc=ignore)
90 inc=ignore)
91 else:
91 else:
92 self.ignorefunc = util.never
92 self.ignorefunc = util.never
93 return self.ignorefunc(fn)
93 return self.ignorefunc(fn)
94
94
95 def __del__(self):
95 def __del__(self):
96 if self.dirty:
96 if self.dirty:
97 self.write()
97 self.write()
98
98
99 def __getitem__(self, key):
99 def __getitem__(self, key):
100 try:
100 try:
101 return self.map[key]
101 return self.map[key]
102 except TypeError:
102 except TypeError:
103 self.read()
103 self.read()
104 return self[key]
104 return self[key]
105
105
106 def __contains__(self, key):
106 def __contains__(self, key):
107 if not self.map: self.read()
107 if not self.map: self.read()
108 return key in self.map
108 return key in self.map
109
109
110 def parents(self):
110 def parents(self):
111 if not self.pl:
111 if not self.pl:
112 self.read()
112 self.read()
113 return self.pl
113 return self.pl
114
114
115 def markdirty(self):
115 def markdirty(self):
116 if not self.dirty:
116 if not self.dirty:
117 self.dirty = 1
117 self.dirty = 1
118
118
119 def setparents(self, p1, p2=nullid):
119 def setparents(self, p1, p2=nullid):
120 if not self.pl:
120 if not self.pl:
121 self.read()
121 self.read()
122 self.markdirty()
122 self.markdirty()
123 self.pl = p1, p2
123 self.pl = p1, p2
124
124
125 def state(self, key):
125 def state(self, key):
126 try:
126 try:
127 return self[key][0]
127 return self[key][0]
128 except KeyError:
128 except KeyError:
129 return "?"
129 return "?"
130
130
131 def read(self):
131 def read(self):
132 if self.map is not None: return self.map
132 if self.map is not None: return self.map
133
133
134 self.map = {}
134 self.map = {}
135 self.pl = [nullid, nullid]
135 self.pl = [nullid, nullid]
136 try:
136 try:
137 st = self.opener("dirstate").read()
137 st = self.opener("dirstate").read()
138 if not st: return
138 if not st: return
139 except: return
139 except: return
140
140
141 self.pl = [st[:20], st[20: 40]]
141 self.pl = [st[:20], st[20: 40]]
142
142
143 pos = 40
143 pos = 40
144 while pos < len(st):
144 while pos < len(st):
145 e = struct.unpack(">cllll", st[pos:pos+17])
145 e = struct.unpack(">cllll", st[pos:pos+17])
146 l = e[4]
146 l = e[4]
147 pos += 17
147 pos += 17
148 f = st[pos:pos + l]
148 f = st[pos:pos + l]
149 if '\0' in f:
149 if '\0' in f:
150 f, c = f.split('\0')
150 f, c = f.split('\0')
151 self.copies[f] = c
151 self.copies[f] = c
152 self.map[f] = e[:4]
152 self.map[f] = e[:4]
153 pos += l
153 pos += l
154
154
155 def copy(self, source, dest):
155 def copy(self, source, dest):
156 self.read()
156 self.read()
157 self.markdirty()
157 self.markdirty()
158 self.copies[dest] = source
158 self.copies[dest] = source
159
159
160 def copied(self, file):
160 def copied(self, file):
161 return self.copies.get(file, None)
161 return self.copies.get(file, None)
162
162
163 def update(self, files, state, **kw):
163 def update(self, files, state, **kw):
164 ''' current states:
164 ''' current states:
165 n normal
165 n normal
166 m needs merging
166 m needs merging
167 r marked for removal
167 r marked for removal
168 a marked for addition'''
168 a marked for addition'''
169
169
170 if not files: return
170 if not files: return
171 self.read()
171 self.read()
172 self.markdirty()
172 self.markdirty()
173 for f in files:
173 for f in files:
174 if state == "r":
174 if state == "r":
175 self.map[f] = ('r', 0, 0, 0)
175 self.map[f] = ('r', 0, 0, 0)
176 else:
176 else:
177 s = os.lstat(os.path.join(self.root, f))
177 s = os.lstat(os.path.join(self.root, f))
178 st_size = kw.get('st_size', s.st_size)
178 st_size = kw.get('st_size', s.st_size)
179 st_mtime = kw.get('st_mtime', s.st_mtime)
179 st_mtime = kw.get('st_mtime', s.st_mtime)
180 self.map[f] = (state, s.st_mode, st_size, st_mtime)
180 self.map[f] = (state, s.st_mode, st_size, st_mtime)
181 if self.copies.has_key(f):
181 if self.copies.has_key(f):
182 del self.copies[f]
182 del self.copies[f]
183
183
184 def forget(self, files):
184 def forget(self, files):
185 if not files: return
185 if not files: return
186 self.read()
186 self.read()
187 self.markdirty()
187 self.markdirty()
188 for f in files:
188 for f in files:
189 try:
189 try:
190 del self.map[f]
190 del self.map[f]
191 except KeyError:
191 except KeyError:
192 self.ui.warn("not in dirstate: %s!\n" % f)
192 self.ui.warn("not in dirstate: %s!\n" % f)
193 pass
193 pass
194
194
195 def clear(self):
195 def clear(self):
196 self.map = {}
196 self.map = {}
197 self.markdirty()
197 self.markdirty()
198
198
199 def write(self):
199 def write(self):
200 st = self.opener("dirstate", "w")
200 st = self.opener("dirstate", "w")
201 st.write("".join(self.pl))
201 st.write("".join(self.pl))
202 for f, e in self.map.items():
202 for f, e in self.map.items():
203 c = self.copied(f)
203 c = self.copied(f)
204 if c:
204 if c:
205 f = f + "\0" + c
205 f = f + "\0" + c
206 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
206 e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
207 st.write(e + f)
207 st.write(e + f)
208 self.dirty = 0
208 self.dirty = 0
209
209
210 def filterfiles(self, files):
210 def filterfiles(self, files):
211 ret = {}
211 ret = {}
212 unknown = []
212 unknown = []
213
213
214 for x in files:
214 for x in files:
215 if x is '.':
215 if x is '.':
216 return self.map.copy()
216 return self.map.copy()
217 if x not in self.map:
217 if x not in self.map:
218 unknown.append(x)
218 unknown.append(x)
219 else:
219 else:
220 ret[x] = self.map[x]
220 ret[x] = self.map[x]
221
221
222 if not unknown:
222 if not unknown:
223 return ret
223 return ret
224
224
225 b = self.map.keys()
225 b = self.map.keys()
226 b.sort()
226 b.sort()
227 blen = len(b)
227 blen = len(b)
228
228
229 for x in unknown:
229 for x in unknown:
230 bs = bisect.bisect(b, x)
230 bs = bisect.bisect(b, x)
231 if bs != 0 and b[bs-1] == x:
231 if bs != 0 and b[bs-1] == x:
232 ret[x] = self.map[x]
232 ret[x] = self.map[x]
233 continue
233 continue
234 while bs < blen:
234 while bs < blen:
235 s = b[bs]
235 s = b[bs]
236 if len(s) > len(x) and s.startswith(x) and s[len(x)] == '/':
236 if len(s) > len(x) and s.startswith(x) and s[len(x)] == '/':
237 ret[s] = self.map[s]
237 ret[s] = self.map[s]
238 else:
238 else:
239 break
239 break
240 bs += 1
240 bs += 1
241 return ret
241 return ret
242
242
243 def walk(self, files=None, match=util.always, dc=None):
243 def walk(self, files=None, match=util.always, dc=None):
244 self.read()
244 self.read()
245
245
246 # walk all files by default
246 # walk all files by default
247 if not files:
247 if not files:
248 files = [self.root]
248 files = [self.root]
249 if not dc:
249 if not dc:
250 dc = self.map.copy()
250 dc = self.map.copy()
251 elif not dc:
251 elif not dc:
252 dc = self.filterfiles(files)
252 dc = self.filterfiles(files)
253
253
254 def statmatch(file, stat):
254 def statmatch(file, stat):
255 file = util.pconvert(file)
255 file = util.pconvert(file)
256 if file not in dc and self.ignore(file):
256 if file not in dc and self.ignore(file):
257 return False
257 return False
258 return match(file)
258 return match(file)
259
259
260 return self.walkhelper(files=files, statmatch=statmatch, dc=dc)
260 return self.walkhelper(files=files, statmatch=statmatch, dc=dc)
261
261
262 # walk recursively through the directory tree, finding all files
262 # walk recursively through the directory tree, finding all files
263 # matched by the statmatch function
263 # matched by the statmatch function
264 #
264 #
265 # results are yielded in a tuple (src, filename), where src is one of:
265 # results are yielded in a tuple (src, filename), where src is one of:
266 # 'f' the file was found in the directory tree
266 # 'f' the file was found in the directory tree
267 # 'm' the file was only in the dirstate and not in the tree
267 # 'm' the file was only in the dirstate and not in the tree
268 #
268 #
269 # dc is an optional arg for the current dirstate. dc is not modified
269 # dc is an optional arg for the current dirstate. dc is not modified
270 # directly by this function, but might be modified by your statmatch call.
270 # directly by this function, but might be modified by your statmatch call.
271 #
271 #
272 def walkhelper(self, files, statmatch, dc):
272 def walkhelper(self, files, statmatch, dc):
273 def supported_type(f, st):
273 def supported_type(f, st):
274 if stat.S_ISREG(st.st_mode):
274 if stat.S_ISREG(st.st_mode):
275 return True
275 return True
276 else:
276 else:
277 kind = 'unknown'
277 kind = 'unknown'
278 if stat.S_ISCHR(st.st_mode): kind = 'character device'
278 if stat.S_ISCHR(st.st_mode): kind = 'character device'
279 elif stat.S_ISBLK(st.st_mode): kind = 'block device'
279 elif stat.S_ISBLK(st.st_mode): kind = 'block device'
280 elif stat.S_ISFIFO(st.st_mode): kind = 'fifo'
280 elif stat.S_ISFIFO(st.st_mode): kind = 'fifo'
281 elif stat.S_ISLNK(st.st_mode): kind = 'symbolic link'
281 elif stat.S_ISLNK(st.st_mode): kind = 'symbolic link'
282 elif stat.S_ISSOCK(st.st_mode): kind = 'socket'
282 elif stat.S_ISSOCK(st.st_mode): kind = 'socket'
283 elif stat.S_ISDIR(st.st_mode): kind = 'directory'
283 elif stat.S_ISDIR(st.st_mode): kind = 'directory'
284 self.ui.warn('%s: unsupported file type (type is %s)\n' % (
284 self.ui.warn('%s: unsupported file type (type is %s)\n' % (
285 util.pathto(self.getcwd(), f),
285 util.pathto(self.getcwd(), f),
286 kind))
286 kind))
287 return False
287 return False
288
288
289 # recursion free walker, faster than os.walk.
289 # recursion free walker, faster than os.walk.
290 def findfiles(s):
290 def findfiles(s):
291 retfiles = []
291 retfiles = []
292 work = [s]
292 work = [s]
293 while work:
293 while work:
294 top = work.pop()
294 top = work.pop()
295 names = os.listdir(top)
295 names = os.listdir(top)
296 names.sort()
296 names.sort()
297 # nd is the top of the repository dir tree
297 # nd is the top of the repository dir tree
298 nd = util.normpath(top[len(self.root) + 1:])
298 nd = util.normpath(top[len(self.root) + 1:])
299 if nd == '.': nd = ''
299 if nd == '.': nd = ''
300 for f in names:
300 for f in names:
301 np = os.path.join(nd, f)
301 np = os.path.join(nd, f)
302 if seen(np):
302 if seen(np):
303 continue
303 continue
304 p = os.path.join(top, f)
304 p = os.path.join(top, f)
305 # don't trip over symlinks
305 # don't trip over symlinks
306 st = os.lstat(p)
306 st = os.lstat(p)
307 if stat.S_ISDIR(st.st_mode):
307 if stat.S_ISDIR(st.st_mode):
308 ds = os.path.join(nd, f +'/')
308 ds = os.path.join(nd, f +'/')
309 if statmatch(ds, st):
309 if statmatch(ds, st):
310 work.append(p)
310 work.append(p)
311 elif supported_type(np, st):
311 elif statmatch(np, st) and supported_type(np, st):
312 if statmatch(np, st):
313 yield util.pconvert(np)
312 yield util.pconvert(np)
314
313
315
314
316 known = {'.hg': 1}
315 known = {'.hg': 1}
317 def seen(fn):
316 def seen(fn):
318 if fn in known: return True
317 if fn in known: return True
319 known[fn] = 1
318 known[fn] = 1
320
319
321 # step one, find all files that match our criteria
320 # step one, find all files that match our criteria
322 files.sort()
321 files.sort()
323 for ff in util.unique(files):
322 for ff in util.unique(files):
324 f = os.path.join(self.root, ff)
323 f = os.path.join(self.root, ff)
325 try:
324 try:
326 st = os.lstat(f)
325 st = os.lstat(f)
327 except OSError, inst:
326 except OSError, inst:
328 if ff not in dc: self.ui.warn('%s: %s\n' % (
327 if ff not in dc: self.ui.warn('%s: %s\n' % (
329 util.pathto(self.getcwd(), ff),
328 util.pathto(self.getcwd(), ff),
330 inst.strerror))
329 inst.strerror))
331 continue
330 continue
332 if stat.S_ISDIR(st.st_mode):
331 if stat.S_ISDIR(st.st_mode):
333 sorted = [ x for x in findfiles(f) ]
332 sorted = [ x for x in findfiles(f) ]
334 sorted.sort()
333 sorted.sort()
335 for fl in sorted:
334 for fl in sorted:
336 yield 'f', fl
335 yield 'f', fl
337 else:
336 else:
338 ff = util.normpath(ff)
337 ff = util.normpath(ff)
339 if seen(ff):
338 if seen(ff):
340 continue
339 continue
341 found = False
340 found = False
342 self.blockignore = True
341 self.blockignore = True
343 if supported_type(ff, st) and statmatch(ff, st):
342 if statmatch(ff, st) and supported_type(ff, st):
344 found = True
343 found = True
345 self.blockignore = False
344 self.blockignore = False
346 if found:
345 if found:
347 yield 'f', ff
346 yield 'f', ff
348
347
349 # step two run through anything left in the dc hash and yield
348 # step two run through anything left in the dc hash and yield
350 # if we haven't already seen it
349 # if we haven't already seen it
351 ks = dc.keys()
350 ks = dc.keys()
352 ks.sort()
351 ks.sort()
353 for k in ks:
352 for k in ks:
354 if not seen(k) and (statmatch(k, None)):
353 if not seen(k) and (statmatch(k, None)):
355 yield 'm', k
354 yield 'm', k
356
355
357 def changes(self, files=None, match=util.always):
356 def changes(self, files=None, match=util.always):
358 self.read()
357 self.read()
359 if not files:
358 if not files:
360 files = [self.root]
359 files = [self.root]
361 dc = self.map.copy()
360 dc = self.map.copy()
362 else:
361 else:
363 dc = self.filterfiles(files)
362 dc = self.filterfiles(files)
364 lookup, modified, added, unknown = [], [], [], []
363 lookup, modified, added, unknown = [], [], [], []
365 removed, deleted = [], []
364 removed, deleted = [], []
366
365
367 # statmatch function to eliminate entries from the dirstate copy
366 # statmatch function to eliminate entries from the dirstate copy
368 # and put files into the appropriate array. This gets passed
367 # and put files into the appropriate array. This gets passed
369 # to the walking code
368 # to the walking code
370 def statmatch(fn, s):
369 def statmatch(fn, s):
371 fn = util.pconvert(fn)
370 fn = util.pconvert(fn)
372 def checkappend(l, fn):
371 def checkappend(l, fn):
373 if match is util.always or match(fn):
372 if match is util.always or match(fn):
374 l.append(fn)
373 l.append(fn)
375
374
376 if not s or stat.S_ISDIR(s.st_mode):
375 if not s or stat.S_ISDIR(s.st_mode):
377 if self.ignore(fn): return False
376 if self.ignore(fn): return False
378 return match(fn)
377 return match(fn)
379
378
380 c = dc.pop(fn, None)
379 c = dc.pop(fn, None)
381 if c:
380 if c:
382 type, mode, size, time = c
381 type, mode, size, time = c
383 # check the common case first
382 # check the common case first
384 if type == 'n':
383 if type == 'n':
385 if size != s.st_size or (mode ^ s.st_mode) & 0100:
384 if size != s.st_size or (mode ^ s.st_mode) & 0100:
386 checkappend(modified, fn)
385 checkappend(modified, fn)
387 elif time != s.st_mtime:
386 elif time != s.st_mtime:
388 checkappend(lookup, fn)
387 checkappend(lookup, fn)
389 elif type == 'm':
388 elif type == 'm':
390 checkappend(modified, fn)
389 checkappend(modified, fn)
391 elif type == 'a':
390 elif type == 'a':
392 checkappend(added, fn)
391 checkappend(added, fn)
393 elif type == 'r':
392 elif type == 'r':
394 checkappend(unknown, fn)
393 checkappend(unknown, fn)
395 elif not self.ignore(fn) and match(fn):
394 elif not self.ignore(fn) and match(fn):
396 unknown.append(fn)
395 unknown.append(fn)
397 # return false because we've already handled all cases above.
396 # return false because we've already handled all cases above.
398 # there's no need for the walking code to process the file
397 # there's no need for the walking code to process the file
399 # any further.
398 # any further.
400 return False
399 return False
401
400
402 # because our statmatch always returns false, self.walk will only
401 # because our statmatch always returns false, self.walk will only
403 # return files in the dirstate map that are not present in the FS.
402 # return files in the dirstate map that are not present in the FS.
404 # But, we still need to iterate through the results to force the
403 # But, we still need to iterate through the results to force the
405 # walk to complete
404 # walk to complete
406 for src, fn in self.walkhelper(files, statmatch, dc):
405 for src, fn in self.walkhelper(files, statmatch, dc):
407 pass
406 pass
408
407
409 # there may be patterns in the .hgignore file that prevent us
408 # there may be patterns in the .hgignore file that prevent us
410 # from examining entire directories in the dirstate map, so we
409 # from examining entire directories in the dirstate map, so we
411 # go back and explicitly examine any matching files we've
410 # go back and explicitly examine any matching files we've
412 # ignored
411 # ignored
413 unexamined = [fn for fn in dc.iterkeys()
412 unexamined = [fn for fn in dc.iterkeys()
414 if self.ignore(fn) and match(fn)]
413 if self.ignore(fn) and match(fn)]
415
414
416 for src, fn in self.walkhelper(unexamined, statmatch, dc):
415 for src, fn in self.walkhelper(unexamined, statmatch, dc):
417 pass
416 pass
418
417
419 # anything left in dc didn't exist in the filesystem
418 # anything left in dc didn't exist in the filesystem
420 for fn, c in dc.iteritems():
419 for fn, c in dc.iteritems():
421 if not match(fn): continue
420 if not match(fn): continue
422 if c[0] == 'r':
421 if c[0] == 'r':
423 removed.append(fn)
422 removed.append(fn)
424 else:
423 else:
425 deleted.append(fn)
424 deleted.append(fn)
426 return (lookup, modified, added, removed + deleted, unknown)
425 return (lookup, modified, added, removed + deleted, unknown)
General Comments 0
You need to be logged in to leave comments. Login now