# HG changeset patch # User Matt Mackall # Date 1275328654 18000 # Node ID fd516380732aae20f3b70f15c50b1bc12b8ca17f # Parent 258c98567aff13ea1fd4405ff74a3ff242ef6cf0# Parent d83ca89577dab228818c81299676aec8ffaa3b06 Merge with i18n diff -r d83ca89577da -r fd516380732a doc/hgrc.5.txt --- a/doc/hgrc.5.txt Tue May 25 13:24:49 2010 -0300 +++ b/doc/hgrc.5.txt Mon May 31 12:57:34 2010 -0500 @@ -90,7 +90,8 @@ ------ A configuration file consists of sections, led by a ``[section]`` header -and followed by ``name = value`` entries:: +and followed by ``name = value`` entries (sometimes called +``configuration keys``):: [spam] eggs=ham @@ -102,10 +103,55 @@ removed from values. Empty lines are skipped. Lines beginning with ``#`` or ``;`` are ignored and may be used to provide comments. +Configuration keys can be set multiple times, in which case mercurial +will use the value that was configured last. As an example:: + + [spam] + eggs=large + ham=serrano + eggs=small + +This would set the configuration key named ``eggs`` to ``small``. + +It is also possible to define a section multiple times. A section can +be redefined on the same and/or on different hgrc files. For example:: + + [foo] + eggs=large + ham=serrano + eggs=small + + [bar] + eggs=ham + green= + eggs + + [foo] + ham=prosciutto + eggs=medium + bread=toasted + +This would set the ``eggs``, ``ham``, and ``bread`` configuration keys +of the ``foo`` section to ``medium``, ``prosciutto``, and ``toasted``, +respectively. As you can see there only thing that matters is the last +value that was set for each of the configuration keys. + +If a configuration key is set multiple times in different +configuration files the final value will depend on the order in which +the different configuration files are read, with settings from earlier +paths overriding later ones as described on the ``Files`` section +above. + A line of the form ``%include file`` will include ``file`` into the current configuration file. The inclusion is recursive, which means that included files can include other files. Filenames are relative to the configuration file in which the ``%include`` directive is found. +Environment variables and ``~user`` constructs are expanded in +``file``. This lets you do something like:: + + %include ~/.hgrc.d/$HOST.rc + +to include a different configuration file on each computer you use. A line with ``%unset name`` will remove ``name`` from the current section, if it has been set previously. @@ -853,7 +899,8 @@ ``. Default is ``$EMAIL`` or ``username@hostname``. If the username in hgrc is empty, it has to be specified manually or in a different hgrc file (e.g. ``$HOME/.hgrc``, if the admin set - ``username =`` in the system hgrc). + ``username =`` in the system hgrc). Environment variables in the + username are expanded. ``verbose`` Increase the amount of output printed. True or False. Default is False. diff -r d83ca89577da -r fd516380732a hgext/mq.py --- a/hgext/mq.py Tue May 25 13:24:49 2010 -0300 +++ b/hgext/mq.py Mon May 31 12:57:34 2010 -0500 @@ -37,6 +37,9 @@ preserving existing git patches upon qrefresh. If set to 'yes' or 'no', mq will override the [diff] section and always generate git or regular patches, possibly losing data in the second case. + +You will by default be managing a patch queue named "patches". You can +create other, independent patch queues with the :hg:`qqueue` command. ''' from mercurial.i18n import _ @@ -234,7 +237,12 @@ class queue(object): def __init__(self, ui, path, patchdir=None): self.basepath = path - self.path = patchdir or os.path.join(path, "patches") + try: + fh = open(os.path.join(path, '.queue')) + curpath = os.path.join(path, fh.read().rstrip()) + except IOError: + curpath = os.path.join(path, 'patches') + self.path = patchdir or curpath self.opener = util.opener(self.path) self.ui = ui self.applied_dirty = 0 @@ -608,8 +616,7 @@ repo.dirstate.invalidate() raise finally: - del tr - release(lock, wlock) + release(tr, lock, wlock) self.removeundo(repo) def _apply(self, repo, series, list=False, update_status=True, @@ -741,7 +748,6 @@ raise util.Abort(_('qdelete requires at least one revision or ' 'patch name')) - realpatches = [] for patch in patches: patch = self.lookup(patch, strict=True) info = self.isapplied(patch) @@ -749,8 +755,8 @@ raise util.Abort(_("cannot delete applied patch %s") % patch) if patch not in self.series: raise util.Abort(_("patch %s not in series file") % patch) - realpatches.append(patch) + patches = list(patches) numrevs = 0 if opts.get('rev'): if not self.applied: @@ -759,10 +765,10 @@ if len(revs) > 1 and revs[0] > revs[1]: revs.reverse() revpatches = self._revpatches(repo, revs) - realpatches += revpatches + patches += revpatches numrevs = len(revpatches) - self._cleanup(realpatches, numrevs, opts.get('keep')) + self._cleanup(patches, numrevs, opts.get('keep')) def check_toppatch(self, repo): if self.applied: @@ -2534,6 +2540,107 @@ q.save_dirty() return 0 +def qqueue(ui, repo, name=None, **opts): + '''manage multiple patch queues + + Supports switching between different patch queues, as well as creating + new patch queues and deleting existing ones. + + Omitting a queue name or specifying -l/--list will show you the registered + queues - by default the "normal" patches queue is registered. The currently + active queue will be marked with "(active)". + + To create a new queue, use -c/--create. The queue is automatically made + active, except in the case where there are applied patches from the + currently active queue in the repository. Then the queue will only be + created and switching will fail. + + To delete an existing queue, use --delete. You cannot delete the currently + active queue. + ''' + + q = repo.mq + + _defaultqueue = 'patches' + _allqueues = '.queues' + _activequeue = '.queue' + + def _getcurrent(): + return os.path.basename(q.path) + + def _noqueues(): + try: + fh = repo.opener(_allqueues, 'r') + fh.close() + except IOError: + return True + + return False + + def _getqueues(): + current = _getcurrent() + + try: + fh = repo.opener(_allqueues, 'r') + queues = [queue.strip() for queue in fh if queue.strip()] + if current not in queues: + queues.append(current) + except IOError: + queues = [_defaultqueue] + + return sorted(queues) + + def _setactive(name): + if q.applied: + raise util.Abort(_('patches applied - cannot set new queue active')) + + fh = repo.opener(_activequeue, 'w') + fh.write(name) + fh.close() + + def _addqueue(name): + fh = repo.opener(_allqueues, 'a') + fh.write('%s\n' % (name,)) + fh.close() + + if not name or opts.get('list'): + current = _getcurrent() + for queue in _getqueues(): + ui.write('%s' % (queue,)) + if queue == current: + ui.write(_(' (active)\n')) + else: + ui.write('\n') + return + + existing = _getqueues() + + if name not in existing and opts.get('delete'): + raise util.Abort(_('cannot delete queue that does not exist')) + elif name not in existing and not opts.get('create'): + raise util.Abort(_('use --create to create a new queue')) + + if opts.get('create'): + if _noqueues(): + _addqueue(_defaultqueue) + _addqueue(name) + _setactive(name) + elif opts.get('delete'): + current = _getcurrent() + + if name == current: + raise util.Abort(_('cannot delete currently active queue')) + + fh = repo.opener('.queues.new', 'w') + for queue in existing: + if queue == name: + continue + fh.write('%s\n' % (queue,)) + fh.close() + util.rename(repo.join('.queues.new'), repo.join(_allqueues)) + else: + _setactive(name) + def reposetup(ui, repo): class mqrepo(repo.__class__): @util.propertycache @@ -2840,6 +2947,14 @@ (finish, [('a', 'applied', None, _('finish all applied changesets'))], _('hg qfinish [-a] [REV]...')), + 'qqueue': + (qqueue, + [ + ('l', 'list', False, _('list all available queues')), + ('c', 'create', False, _('create new queue')), + ('', 'delete', False, _('delete reference to queue')), + ], + _('[OPTION] [QUEUE]')), } colortable = {'qguard.negative': 'red', diff -r d83ca89577da -r fd516380732a hgext/pager.py diff -r d83ca89577da -r fd516380732a hgext/record.py --- a/hgext/record.py Tue May 25 13:24:49 2010 -0300 +++ b/hgext/record.py Mon May 31 12:57:34 2010 -0500 @@ -296,9 +296,9 @@ ui.write("\n") if r == 7: # ? doc = gettext(record.__doc__) - c = doc.find(_('y - record this change')) + c = doc.find('::') + 2 for l in doc[c:].splitlines(): - if l: + if l.startswith(' '): ui.write(l.strip(), '\n') continue elif r == 0: # yes @@ -380,7 +380,9 @@ a - record all changes to all remaining files q - quit, recording no changes - ? - display help''' + ? - display help + + This command is not available when committing a merge.''' dorecord(ui, repo, commands.commit, *pats, **opts) @@ -419,10 +421,15 @@ After the actual job is done by non-interactive command, working dir state is restored to original. - In the end we'll record intresting changes, and everything else will be + In the end we'll record interesting changes, and everything else will be left in place, so the user can continue his work. """ + merge = len(repo[None].parents()) > 1 + if merge: + raise util.Abort(_('cannot partially commit a merge ' + '(use hg commit instead)')) + changes = repo.status(match=match)[:3] diffopts = mdiff.diffopts(git=True, nodates=True) chunks = patch.diff(repo, changes=changes, opts=diffopts) diff -r d83ca89577da -r fd516380732a mercurial/cmdutil.py --- a/mercurial/cmdutil.py Tue May 25 13:24:49 2010 -0300 +++ b/mercurial/cmdutil.py Mon May 31 12:57:34 2010 -0500 @@ -8,7 +8,7 @@ from node import hex, nullid, nullrev, short from i18n import _ import os, sys, errno, re, glob, tempfile -import mdiff, bdiff, util, templater, patch, error, encoding, templatekw +import util, templater, patch, error, encoding, templatekw import match as _match import similar @@ -351,12 +351,13 @@ def walkpat(pat): srcs = [] + badstates = after and '?' or '?r' m = match(repo, [pat], opts, globbed=True) for abs in repo.walk(m): state = repo.dirstate[abs] rel = m.rel(abs) exact = m.exact(abs) - if state in '?r': + if state in badstates: if exact and state == '?': ui.warn(_('%s: not copying - file is not managed\n') % rel) if exact and state == 'r': diff -r d83ca89577da -r fd516380732a mercurial/commands.py --- a/mercurial/commands.py Tue May 25 13:24:49 2010 -0300 +++ b/mercurial/commands.py Mon May 31 12:57:34 2010 -0500 @@ -2530,19 +2530,26 @@ def push(ui, repo, dest=None, **opts): """push changes to the specified destination - Push changes from the local repository to the specified destination. - - This is the symmetrical operation for pull. It moves changes from - the current repository to a different one. If the destination is - local this is identical to a pull in that directory from the - current one. - - By default, push will refuse to run if it detects the result would - increase the number of remote heads. This generally indicates the - user forgot to pull and merge before pushing. - - If -r/--rev is used, the named revision and all its ancestors will - be pushed to the remote repository. + Push changesets from the local repository to the specified + destination. + + This operation is symmetrical to pull: it is identical to a pull + in the destination repository from the current one. + + By default, push will not allow creation of new heads at the + destination, since multiple heads would make it unclear which head + to use. In this situation, it is recommended to pull and merge + before pushing. + + Use --new-branch if you want to allow push to create a new named + branch that is not present at the destination. This allows you to + only create a new branch without forcing other changes. + + Use -f/--force to override the default behavior and push all + changesets on all branches. + + If -r/--rev is used, the specified revision and all its ancestors + will be pushed to the remote repository. Please see :hg:`help urls` for important details about ``ssh://`` URLs. If DESTINATION is omitted, a default path will be used. diff -r d83ca89577da -r fd516380732a mercurial/config.py --- a/mercurial/config.py Tue May 25 13:24:49 2010 -0300 +++ b/mercurial/config.py Mon May 31 12:57:34 2010 -0500 @@ -6,7 +6,7 @@ # GNU General Public License version 2 or any later version. from i18n import _ -import error +import error, util import re, os class sortdict(dict): @@ -96,7 +96,7 @@ cont = False m = includere.match(l) if m: - inc = m.group(1) + inc = util.expandpath(m.group(1)) base = os.path.dirname(src) inc = os.path.normpath(os.path.join(base, inc)) if include: diff -r d83ca89577da -r fd516380732a mercurial/hg.py --- a/mercurial/hg.py Tue May 25 13:24:49 2010 -0300 +++ b/mercurial/hg.py Mon May 31 12:57:34 2010 -0500 @@ -277,6 +277,7 @@ % dest) raise + hardlink = None for f in src_repo.store.copylist(): src = os.path.join(src_repo.sharedpath, f) dst = os.path.join(dest_path, f) @@ -287,7 +288,7 @@ if dst.endswith('data'): # lock to avoid premature writing to the target dest_lock = lock.lock(os.path.join(dstbase, "lock")) - util.copyfiles(src, dst) + hardlink = util.copyfiles(src, dst, hardlink) # we need to re-init the repo after manually copying the data # into it diff -r d83ca89577da -r fd516380732a mercurial/hgweb/hgwebdir_mod.py diff -r d83ca89577da -r fd516380732a mercurial/localrepo.py --- a/mercurial/localrepo.py Tue May 25 13:24:49 2010 -0300 +++ b/mercurial/localrepo.py Mon May 31 12:57:34 2010 -0500 @@ -971,7 +971,8 @@ self.branchtags() return n finally: - del tr + if tr: + tr.release() lock.release() def destroyed(self): @@ -1041,7 +1042,9 @@ match.bad = bad if working: # we need to scan the working dir - subrepos = ctx1.substate.keys() + subrepos = [] + if '.hgsub' in self.dirstate: + subrepos = ctx1.substate.keys() s = self.dirstate.status(match, subrepos, listignored, listclean, listunknown) cmp, modified, added, removed, deleted, unknown, ignored, clean = s @@ -2192,7 +2195,7 @@ tr.close() finally: - del tr + tr.release() if changesets > 0: # forcefully update the on-disk branch cache diff -r d83ca89577da -r fd516380732a mercurial/transaction.py --- a/mercurial/transaction.py Tue May 25 13:24:49 2010 -0300 +++ b/mercurial/transaction.py Mon May 31 12:57:34 2010 -0500 @@ -43,6 +43,7 @@ class transaction(object): def __init__(self, report, opener, journal, after=None, createmode=None): self.count = 1 + self.usages = 1 self.report = report self.opener = opener self.after = after @@ -108,8 +109,16 @@ @active def nest(self): self.count += 1 + self.usages += 1 return self + def release(self): + if self.count > 0: + self.usages -= 1 + # of the transaction scopes are left without being closed, fail + if self.count > 0 and self.usages == 0: + self._abort() + def running(self): return self.count > 0 @@ -136,6 +145,7 @@ def _abort(self): self.count = 0 + self.usages = 0 self.file.close() try: diff -r d83ca89577da -r fd516380732a mercurial/ui.py --- a/mercurial/ui.py Tue May 25 13:24:49 2010 -0300 +++ b/mercurial/ui.py Mon May 31 12:57:34 2010 -0500 @@ -266,6 +266,8 @@ user = os.environ.get("HGUSER") if user is None: user = self.config("ui", "username") + if user is not None: + user = os.path.expandvars(user) if user is None: user = os.environ.get("EMAIL") if user is None and self.configbool("ui", "askusername"): diff -r d83ca89577da -r fd516380732a mercurial/util.py --- a/mercurial/util.py Tue May 25 13:24:49 2010 -0300 +++ b/mercurial/util.py Mon May 31 12:57:34 2010 -0500 @@ -458,7 +458,7 @@ for name, kind in osutil.listdir(src): srcname = os.path.join(src, name) dstname = os.path.join(dst, name) - copyfiles(srcname, dstname, hardlink) + hardlink = copyfiles(srcname, dstname, hardlink) else: if hardlink: try: @@ -469,6 +469,8 @@ else: shutil.copy(src, dst) + return hardlink + class path_auditor(object): '''ensure that a filesystem path contains no banned components. the following properties of a path are checked: diff -r d83ca89577da -r fd516380732a tests/test-hgrc --- a/tests/test-hgrc Tue May 25 13:24:49 2010 -0300 +++ b/tests/test-hgrc Mon May 31 12:57:34 2010 -0500 @@ -23,8 +23,33 @@ > $HGRCPATH hg showconfig foo -echo '%include /no-such-file' > $HGRCPATH +FAKEPATH=/path/to/nowhere +export FAKEPATH +echo '%include $FAKEPATH/no-such-file' > $HGRCPATH hg version 2>&1 | sed -e "s|$HGRCPATH|\$HGRCPATH|" +unset FAKEPATH + +echo "% username expansion" +olduser=$HGUSER +unset HGUSER + +FAKEUSER='John Doe' +export FAKEUSER +echo '[ui]' > $HGRCPATH +echo 'username = $FAKEUSER' >> $HGRCPATH + +hg init usertest +cd usertest +touch bar +hg commit --addremove --quiet -m "added bar" +hg log --template "{author}\n" +cd .. + +hg showconfig | sed -e "s:$p:...:" + +unset FAKEUSER +HGUSER=$olduser +export HGUSER # HGPLAIN cd .. diff -r d83ca89577da -r fd516380732a tests/test-hgrc.out --- a/tests/test-hgrc.out Tue May 25 13:24:49 2010 -0300 +++ b/tests/test-hgrc.out Mon May 31 12:57:34 2010 -0500 @@ -9,7 +9,10 @@ hg: config error at $HGRCPATH:2: ' x = y' foo.bar=a\nb\nc\nde\nfg foo.baz=bif cb -hg: config error at $HGRCPATH:1: cannot include /no-such-file (No such file or directory) +hg: config error at $HGRCPATH:1: cannot include /path/to/nowhere/no-such-file (No such file or directory) +% username expansion +John Doe +ui.username=$FAKEUSER % customized hgrc read config from: .../.hgrc .../.hgrc:13: alias.log=log -g diff -r d83ca89577da -r fd516380732a tests/test-mq-qqueue --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-qqueue Mon May 31 12:57:34 2010 -0500 @@ -0,0 +1,47 @@ +#!/bin/sh + +echo "[extensions]" >> $HGRCPATH +echo "mq=" >> $HGRCPATH + +hg init foo +cd foo +echo a > a +hg ci -qAm a + +echo %% default queue +hg qqueue + +echo b > a +hg qnew -fgDU somestuff + +echo %% applied patches in default queue +hg qap + +echo %% try to change patch \(create succeeds, switch fails\) +hg qqueue foo --create +hg qqueue + +echo %% empty default queue +hg qpop + +echo %% switch queue +hg qqueue foo +hg qqueue + +echo %% unapplied patches +hg qun +echo c > a +hg qnew -fgDU otherstuff + +echo %% fail switching back +hg qqueue patches + +echo %% fail deleting current +hg qqueue foo --delete + +echo %% switch back and delete foo +hg qpop -a +hg qqueue patches +hg qqueue foo --delete +hg qqueue +cd .. diff -r d83ca89577da -r fd516380732a tests/test-mq-qqueue.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-mq-qqueue.out Mon May 31 12:57:34 2010 -0500 @@ -0,0 +1,23 @@ +%% default queue +patches (active) +%% applied patches in default queue +somestuff +%% try to change patch (create succeeds, switch fails) +abort: patches applied - cannot set new queue active +foo +patches (active) +%% empty default queue +popping somestuff +patch queue now empty +%% switch queue +foo (active) +patches +%% unapplied patches +%% fail switching back +abort: patches applied - cannot set new queue active +%% fail deleting current +abort: cannot delete currently active queue +%% switch back and delete foo +popping otherstuff +patch queue now empty +patches (active) diff -r d83ca89577da -r fd516380732a tests/test-mq.out --- a/tests/test-mq.out Tue May 25 13:24:49 2010 -0300 +++ b/tests/test-mq.out Mon May 31 12:57:34 2010 -0500 @@ -32,6 +32,9 @@ will override the [diff] section and always generate git or regular patches, possibly losing data in the second case. +You will by default be managing a patch queue named "patches". You can create +other, independent patch queues with the "hg qqueue" command. + list of commands: qapplied print the patches already applied @@ -49,6 +52,7 @@ qpop pop the current patch off the stack qprev print the name of the previous patch qpush push the next patch onto the stack + qqueue manage multiple patch queues qrefresh update the current patch qrename rename a patch qselect set or print guarded patches to push diff -r d83ca89577da -r fd516380732a tests/test-record --- a/tests/test-record Tue May 25 13:24:49 2010 -0300 +++ b/tests/test-record Mon May 31 12:57:34 2010 -0500 @@ -301,9 +301,20 @@ EOF echo; hg tip --config diff.git=True -p +cd .. + +echo % abort early when a merge is in progress +hg up 4 +touch iwillmergethat +hg add iwillmergethat +hg branch thatbranch +hg ci -m'new head' +hg up default +hg merge thatbranch +echo; hg record -m'will abort' +hg up -C echo % with win32ext -cd .. echo '[extensions]' >> .hg/hgrc echo 'win32text = ' >> .hg/hgrc echo '[decode]' >> .hg/hgrc diff -r d83ca89577da -r fd516380732a tests/test-record.out --- a/tests/test-record.out Tue May 25 13:24:49 2010 -0300 +++ b/tests/test-record.out Mon May 31 12:57:34 2010 -0500 @@ -24,6 +24,8 @@ ? - display help + This command is not available when committing a merge. + options: -A --addremove mark new/missing files as added/removed before committing @@ -626,6 +628,15 @@ b +c +% abort early when a merge is in progress +1 files updated, 0 files merged, 5 files removed, 0 files unresolved +marked working directory as branch thatbranch +5 files updated, 0 files merged, 2 files removed, 0 files unresolved +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) + +abort: cannot partially commit a merge (use hg commit instead) +0 files updated, 0 files merged, 1 files removed, 0 files unresolved % with win32ext diff --git a/subdir/f1 b/subdir/f1 1 hunks, 1 lines changed @@ -637,8 +648,9 @@ +d record this change to 'subdir/f1'? [Ynsfdaq?] -changeset: 25:5bacc1f6e9cf +changeset: 26:5bacc1f6e9cf tag: tip +parent: 24:1460f6e47966 user: test date: Thu Jan 01 00:00:23 1970 +0000 summary: w1 diff -r d83ca89577da -r fd516380732a tests/test-rename --- a/tests/test-rename Tue May 25 13:24:49 2010 -0300 +++ b/tests/test-rename Mon May 31 12:57:34 2010 -0500 @@ -23,6 +23,14 @@ hg update -C rm d2/c +echo '# rename --after a single file when src and tgt already tracked' +mv d1/d11/a1 d2/c +hg addrem +hg rename --after d1/d11/a1 d2/c +hg status -C +hg update -C +rm d2/c + echo "# rename --after a single file to a nonexistant target filename" hg rename --after d1/a dummy diff -r d83ca89577da -r fd516380732a tests/test-rename.out --- a/tests/test-rename.out Tue May 25 13:24:49 2010 -0300 +++ b/tests/test-rename.out Mon May 31 12:57:34 2010 -0500 @@ -8,6 +8,13 @@ d1/d11/a1 R d1/d11/a1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved +# rename --after a single file when src and tgt already tracked +removing d1/d11/a1 +adding d2/c +A d2/c + d1/d11/a1 +R d1/d11/a1 +1 files updated, 0 files merged, 0 files removed, 0 files unresolved # rename --after a single file to a nonexistant target filename d1/a: not recording move - dummy does not exist # move a single file to an existing directory