diff --git a/doc/hg.1.txt b/doc/hg.1.txt --- a/doc/hg.1.txt +++ b/doc/hg.1.txt @@ -73,8 +73,8 @@ annotate [-r -u -n -c] [files ...] place. options: - -I, --include include directories matching the given patterns - -X, --exclude exclude directories matching the given patterns + -I, --include include names matching the given patterns + -X, --exclude exclude names matching the given patterns -r, --revision annotate the specified revision -u, --user list the author -c, --changeset list the changeset @@ -100,11 +100,11 @@ clone [-U] [dest]:: options: -U, --noupdate do not update the new working directory -commit [-A -t -l -m -u -d ] [files...]:: +commit [options] [files...]:: Commit changes to the given files into the repository. If a list of files is omitted, all changes reported by "hg status" - will be commited. + from the root of the repository will be commited. The HGEDITOR or EDITOR environment variables are used to start an editor to add a commit comment. @@ -112,6 +112,8 @@ commit [-A -t -l -m -u include names matching the given patterns + -X, --exclude exclude names matching the given patterns -m, --message use as commit message -l, --logfile show the commit message for the given file -d, --date record datecode as commit date @@ -136,8 +138,8 @@ diff [-r revision] [-r revision] [files to its parent. options: - -I, --include include directories matching the given patterns - -X, --exclude exclude directories matching the given patterns + -I, --include include names matching the given patterns + -X, --exclude exclude names matching the given patterns export [-o filespec] [revision] ...:: Print the changeset header and diffs for one or more revisions. @@ -161,9 +163,13 @@ export [-o filespec] [revision] ...:: -o, --output print output to file with formatted named -forget [files]:: +forget [options] [files]:: Undo an 'hg add' scheduled for the next commit. + options: + -I, --include include names matching the given patterns + -X, --exclude exclude names matching the given patterns + heads:: Show all repository head changesets. @@ -213,9 +219,9 @@ locate [options] [files]:: -0, --print0 end filenames with NUL, for use with xargs -f, --fullpath print complete paths from the filesystem root - -I, --include include directories matching the given patterns + -I, --include include names matching the given patterns -r, --rev search the repository as it stood at rev - -X, --exclude exclude directories matching the given patterns + -X, --exclude exclude names matching the given patterns log [-r revision ...] [-p] [file]:: Print the revision history of the specified file or the entire project. @@ -339,8 +345,8 @@ status [options] [files]:: options: - -I, --include include directories matching the given patterns - -X, --exclude exclude directories matching the given patterns + -I, --include include names matching the given patterns + -X, --exclude exclude names matching the given patterns tag [-l -m -d -u ] [revision]:: Name a particular revision using . diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -47,7 +47,8 @@ def walk(repo, pats, opts, head = ''): cwd = repo.getcwd() c = 0 if cwd: c = len(cwd) + 1 - for src, fn in repo.walk(match = matchpats(cwd, pats, opts, head)): + files, matchfn = matchpats(cwd, pats, opts, head) + for src, fn in repo.walk(files = files, match = matchfn): yield src, fn, fn[c:] revrangesep = ':' @@ -339,17 +340,17 @@ def add(ui, repo, *pats, **opts): def addremove(ui, repo, *pats, **opts): """add all new files, delete all missing files""" q = dict(zip(pats, pats)) - cwd = repo.getcwd() - n = (cwd and len(cwd) + 1) or 0 - c, a, d, u = repo.changes(match = matchpats(cwd, pats, opts)) - for f in u: - if f not in q: - ui.status('adding %s\n' % f[n:]) - repo.add(u) - for f in d: - if f not in q: - ui.status('removing %s\n' % f[n:]) - repo.remove(d) + add, remove = [], [] + for src, abs, rel in walk(repo, pats, opts): + if src == 'f': + if repo.dirstate.state(abs) == '?': + add.append(abs) + if rel not in q: ui.status('adding ', rel, '\n') + elif repo.dirstate.state(abs) != 'r' and not os.path.exists(rel): + remove.append(abs) + if rel not in q: ui.status('removing ', rel, '\n') + repo.add(add) + repo.remove(remove) def annotate(ui, repo, *pats, **opts): """show changeset information per file line""" @@ -467,7 +468,7 @@ def clone(ui, source, dest=None, **opts) d.close() -def commit(ui, repo, *files, **opts): +def commit(ui, repo, *pats, **opts): """commit the specified files or all outstanding changes""" if opts['text']: ui.warn("Warning: -t and --text is deprecated," @@ -481,8 +482,18 @@ def commit(ui, repo, *files, **opts): ui.warn("Can't read commit message %s: %s\n" % (logfile, why)) if opts['addremove']: - addremove(ui, repo, *files) - repo.commit(relpath(repo, files), message, opts['user'], opts['date']) + addremove(ui, repo, *pats, **opts) + cwd = repo.getcwd() + if not pats and cwd: + opts['include'] = [os.path.join(cwd, i) for i in opts['include']] + opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']] + fns, match = matchpats((pats and repo.getcwd()) or '', pats, opts) + if pats: + c, a, d, u = repo.changes(files = fns, match = match) + files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r'] + else: + files = [] + repo.commit(files, message, opts['user'], opts['date'], match) def copy(ui, repo, source, dest): """mark a file as copied or renamed for the next commit""" @@ -604,9 +615,15 @@ def export(ui, repo, *changesets, **opts seqno += 1 doexport(ui, repo, cset, seqno, total, revwidth, opts) -def forget(ui, repo, file1, *files): +def forget(ui, repo, *pats, **opts): """don't add the specified files on the next commit""" - repo.forget(relpath(repo, (file1,) + files)) + q = dict(zip(pats, pats)) + forget = [] + for src, abs, rel in walk(repo, pats, opts): + if repo.dirstate.state(abs) == 'a': + forget.append(abs) + if rel not in q: ui.status('forgetting ', rel, '\n') + repo.forget(forget) def heads(ui, repo): """show current repository heads""" @@ -1004,7 +1021,8 @@ def status(ui, repo, *pats, **opts): R = removed ? = not tracked''' - (c, a, d, u) = repo.changes(match = matchpats(repo.getcwd(), pats, opts)) + files, matchfn = matchpats(repo.getcwd(), pats, opts) + (c, a, d, u) = repo.changes(files = files, match = matchfn) (c, a, d, u) = map(lambda x: relfilter(repo, x), (c, a, d, u)) for f in c: @@ -1135,6 +1153,8 @@ table = { "^commit|ci": (commit, [('A', 'addremove', None, 'run add/remove during commit'), + ('I', 'include', [], 'include path in search'), + ('X', 'exclude', [], 'exclude path from search'), ('m', 'message', "", 'commit message'), ('t', 'text', "", 'commit message (deprecated: use -m)'), ('l', 'logfile', "", 'commit message file'), @@ -1156,7 +1176,10 @@ table = { (export, [('o', 'output', "", 'output to file')], "hg export [-o OUTFILE] REV..."), - "forget": (forget, [], "hg forget FILE..."), + "forget": (forget, + [('I', 'include', [], 'include path in search'), + ('X', 'exclude', [], 'exclude path from search')], + "hg forget FILE..."), "heads": (heads, [], 'hg heads'), "help": (help_, [], 'hg help [COMMAND]'), "identify|id": (identify, [], 'hg identify'), diff --git a/mercurial/hg.py b/mercurial/hg.py --- a/mercurial/hg.py +++ b/mercurial/hg.py @@ -773,7 +773,8 @@ class localrepository: if update_dirstate: self.dirstate.setparents(n, nullid) - def commit(self, files = None, text = "", user = None, date = None): + def commit(self, files = None, text = "", user = None, date = None, + match = util.always): commit = [] remove = [] if files: @@ -786,7 +787,7 @@ class localrepository: else: self.ui.warn("%s not tracked!\n" % f) else: - (c, a, d, u) = self.changes() + (c, a, d, u) = self.changes(match = match) commit = c + a remove = d diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -66,7 +66,15 @@ def globre(pat, head = '^', tail = '$'): res += re.escape(c) return head + res + tail -def matcher(cwd, pats, inc, exc, head = ''): +_globchars = {'[': 1, '{': 1, '*': 1, '?': 1} + +def matcher(cwd, names, inc, exc, head = ''): + def patlike(name): + for prefix in 're:', 'glob:', 'path:': + if name.startswith(prefix): return True + for c in name: + if c in _globchars: return True + def regex(name, tail): '''convert a pattern into a regular expression''' if name.startswith('re:'): @@ -77,6 +85,8 @@ def matcher(cwd, pats, inc, exc, head = return head + globre(name[5:], '', tail) return head + globre(name, '', tail) + cwdsep = cwd + os.sep + def under(fn): """check if fn is under our cwd""" return not cwd or fn.startswith(cwdsep) @@ -86,16 +96,25 @@ def matcher(cwd, pats, inc, exc, head = if pats: pat = '(?:%s)' % '|'.join([regex(p, tail) for p in pats]) if cwd: - pat = re.escape(cwd + os.sep) + pat + pat = re.escape(cwdsep) + pat return re.compile(pat).match - cwdsep = cwd + os.sep - patmatch = matchfn(pats, '$') or (lambda fn: True) + pats = filter(patlike, names) + files = [n for n in names if not patlike(n)] + if pats: plain = [] + elif cwd: plain = [cwdsep + f for f in files] + else: plain = files + + patmatch = matchfn(pats, '$') + filematch = matchfn(files, '(?:/|$)') incmatch = matchfn(inc, '(?:/|$)') or under excmatch = matchfn(exc, '(?:/|$)') or (lambda fn: False) - return lambda fn: (incmatch(fn) and not excmatch(fn) and - (fn.endswith('/') or patmatch(fn))) + return plain, lambda fn: (incmatch(fn) and not excmatch(fn) and + (fn.endswith('/') or + (not pats and not files) or + (pats and patmatch(fn)) or + (files and filematch(fn)))) def system(cmd, errprefix=None): """execute a shell command that must succeed"""