diff --git a/hgext/bookmarks.py b/hgext/bookmarks.py --- a/hgext/bookmarks.py +++ b/hgext/bookmarks.py @@ -249,12 +249,12 @@ def reposetup(ui, repo): key = self._bookmarks[key] return super(bookmark_repo, self).lookup(key) - def commit(self, *k, **kw): + def commitctx(self, ctx, error=False): """Add a revision to the repository and move the bookmark""" wlock = self.wlock() # do both commit and bookmark with lock held try: - node = super(bookmark_repo, self).commit(*k, **kw) + node = super(bookmark_repo, self).commitctx(ctx, error) if node is None: return None parents = self.changelog.parents(node) @@ -262,12 +262,13 @@ def reposetup(ui, repo): parents = (parents[0],) marks = parse(self) update = False - for mark, n in marks.items(): - if ui.configbool('bookmarks', 'track.current'): - if mark == current(self) and n in parents: - marks[mark] = node - update = True - else: + if ui.configbool('bookmarks', 'track.current'): + mark = current(self) + if mark and marks[mark] in parents: + marks[mark] = node + update = True + else: + for mark, n in marks.items(): if n in parents: marks[mark] = node update = True @@ -288,10 +289,16 @@ def reposetup(ui, repo): node = self.changelog.tip() marks = parse(self) update = False - for mark, n in marks.items(): - if n in parents: + if ui.configbool('bookmarks', 'track.current'): + mark = current(self) + if mark and marks[mark] in parents: marks[mark] = node update = True + else: + for mark, n in marks.items(): + if n in parents: + marks[mark] = node + update = True if update: write(self, marks) return result diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -59,7 +59,7 @@ class dirstate(object): def _foldmap(self): f = {} for name in self._map: - f[os.path.normcase(name)] = name + f[util.realpath(self._join(name))] = name return f @propertycache @@ -340,7 +340,7 @@ class dirstate(object): self._ui.warn(_("not in dirstate: %s\n") % f) def _normalize(self, path, knownpath): - norm_path = os.path.normcase(path) + norm_path = util.realpath(self._join(path)) fold_path = self._foldmap.get(norm_path, None) if fold_path is None: if knownpath or not os.path.exists(os.path.join(self._root, path)): diff --git a/mercurial/hg.py b/mercurial/hg.py --- a/mercurial/hg.py +++ b/mercurial/hg.py @@ -138,7 +138,7 @@ def share(ui, source, dest=None, update= try: uprev = r.lookup(test) break - except: + except LookupError: continue _update(r, uprev) diff --git a/mercurial/mail.py b/mercurial/mail.py --- a/mercurial/mail.py +++ b/mercurial/mail.py @@ -36,7 +36,10 @@ def _smtp(ui): if username and password: ui.note(_('(authenticating to mail server as %s)\n') % (username)) - s.login(username, password) + try: + s.login(username, password) + except smtplib.SMTPException, inst: + raise util.Abort(inst) def send(sender, recipients, msg): try: diff --git a/mercurial/patch.py b/mercurial/patch.py --- a/mercurial/patch.py +++ b/mercurial/patch.py @@ -182,6 +182,7 @@ def readgitpatch(lr): lineno = 0 for line in lr: lineno += 1 + line = line.rstrip(' \r\n') if line.startswith('diff --git'): m = gitre.match(line) if m: @@ -200,23 +201,23 @@ def readgitpatch(lr): continue if line.startswith('rename from '): gp.op = 'RENAME' - gp.oldpath = line[12:].rstrip() + gp.oldpath = line[12:] elif line.startswith('rename to '): - gp.path = line[10:].rstrip() + gp.path = line[10:] elif line.startswith('copy from '): gp.op = 'COPY' - gp.oldpath = line[10:].rstrip() + gp.oldpath = line[10:] elif line.startswith('copy to '): - gp.path = line[8:].rstrip() + gp.path = line[8:] elif line.startswith('deleted file'): gp.op = 'DELETE' # is the deleted file a symlink? - gp.setmode(int(line.rstrip()[-6:], 8)) + gp.setmode(int(line[-6:], 8)) elif line.startswith('new file mode '): gp.op = 'ADD' - gp.setmode(int(line.rstrip()[-6:], 8)) + gp.setmode(int(line[-6:], 8)) elif line.startswith('new mode '): - gp.setmode(int(line.rstrip()[-6:], 8)) + gp.setmode(int(line[-6:], 8)) elif line.startswith('GIT binary patch'): dopatch |= GP_BINARY gp.binary = True diff --git a/mercurial/posix.py b/mercurial/posix.py --- a/mercurial/posix.py +++ b/mercurial/posix.py @@ -7,7 +7,7 @@ from i18n import _ import osutil -import os, sys, errno, stat, getpass, pwd, grp +import os, sys, errno, stat, getpass, pwd, grp, fcntl posixfile = open nulldev = '/dev/null' @@ -104,6 +104,44 @@ def pconvert(path): def localpath(path): return path +if sys.platform == 'darwin': + def realpath(path): + ''' + Returns the true, canonical file system path equivalent to the given + path. + + Equivalent means, in this case, resulting in the same, unique + file system link to the path. Every file system entry, whether a file, + directory, hard link or symbolic link or special, will have a single + path preferred by the system, but may allow multiple, differing path + lookups to point to it. + + Most regular UNIX file systems only allow a file system entry to be + looked up by its distinct path. Obviously, this does not apply to case + insensitive file systems, whether case preserving or not. The most + complex issue to deal with is file systems transparently reencoding the + path, such as the non-standard Unicode normalisation required for HFS+ + and HFSX. + ''' + # Constants copied from /usr/include/sys/fcntl.h + F_GETPATH = 50 + O_SYMLINK = 0x200000 + + try: + fd = os.open(path, O_SYMLINK) + except OSError, err: + if err.errno is errno.ENOENT: + return path + raise + + try: + return fcntl.fcntl(fd, F_GETPATH, '\0' * 1024).rstrip('\0') + finally: + os.close(fd) +else: + # Fallback to the likely inadequate Python builtin function. + realpath = os.path.realpath + def shellquote(s): if os.sys.platform == 'OpenVMS': return '"%s"' % s diff --git a/mercurial/windows.py b/mercurial/windows.py --- a/mercurial/windows.py +++ b/mercurial/windows.py @@ -126,6 +126,15 @@ def localpath(path): def normpath(path): return pconvert(os.path.normpath(path)) +def realpath(path): + ''' + Returns the true, canonical file system path equivalent to the given + path. + ''' + # TODO: There may be a more clever way to do this that also handles other, + # less common file systems. + return os.path.normpath(os.path.normcase(os.path.realpath(path))) + def samestat(s1, s2): return False diff --git a/tests/hghave b/tests/hghave --- a/tests/hghave +++ b/tests/hghave @@ -39,7 +39,7 @@ def has_bzr114(): try: import bzrlib return (bzrlib.__doc__ != None - and bzrlib.version_info[:2] == (1, 14)) + and bzrlib.version_info[:2] >= (1, 14)) except ImportError: return False diff --git a/tests/test-path-normalization b/tests/test-path-normalization new file mode 100755 --- /dev/null +++ b/tests/test-path-normalization @@ -0,0 +1,4 @@ +#!/bin/sh + +hg clone --quiet $TESTDIR/test-path-normalization.hg t +exec hg st -R t diff --git a/tests/test-path-normalization.hg b/tests/test-path-normalization.hg new file mode 100644 index 0000000000000000000000000000000000000000..73ea086c424d1d011310533a43f74582f1835ff5 GIT binary patch literal 404 zc$@*00c-w9M=>x$T4*^jL0KkKStkp}5&!@R|Nq~TKxj-4f938JM*u(X-2nmwAP|az z0s;&$10huyG9fSl8pgpYk?AI$L@-87ni^muCL`4J0s|%l+5^=wri4-IYGX{1noLQi znWl!ON2!PgOcCl~44PyOG%$$N2*?4D0B8UJ05kv^001FOxrvJ)oInhWL;!$1gcJZN zHZl@eeuSoK!cnRk*OImct{HOm*$28g5RV}0Q)qZk!TafYGR#&(9PZYW4Opx#oDjRt zXR7Q0(0w=z^+uQ?2z9ZtxtUbzFjEt-rUu77!kqwjg{AlpOJ