--- a/hgext/mq.py Sat Apr 07 04:45:27 2007 -0300
+++ b/hgext/mq.py Tue Apr 10 14:05:15 2007 -0500
@@ -471,8 +471,16 @@
patcherr = not patcherr
if merge and files:
- # Mark as merged and update dirstate parent info
- repo.dirstate.update(repo.dirstate.filterfiles(files.keys()), 'm')
+ # Mark as removed/merged and update dirstate parent info
+ removed = []
+ merged = []
+ for f in files:
+ if os.path.exists(repo.dirstate.wjoin(f)):
+ merged.append(f)
+ else:
+ removed.append(f)
+ repo.dirstate.update(repo.dirstate.filterfiles(removed), 'r')
+ repo.dirstate.update(repo.dirstate.filterfiles(merged), 'm')
p1, p2 = repo.dirstate.parents()
repo.dirstate.setparents(p1, merge)
files = patch.updatedir(self.ui, repo, files, wlock=wlock)
--- a/mercurial/dirstate.py Sat Apr 07 04:45:27 2007 -0300
+++ b/mercurial/dirstate.py Tue Apr 10 14:05:15 2007 -0500
@@ -314,7 +314,7 @@
def write(self):
if not self.dirty:
return
- st = self.opener("dirstate", "w", atomic=True)
+ st = self.opener("dirstate", "w", atomictemp=True)
st.write("".join(self.pl))
for f, e in self.map.items():
c = self.copied(f)
@@ -322,6 +322,7 @@
f = f + "\0" + c
e = struct.pack(self.format, e[0], e[1], e[2], e[3], len(f))
st.write(e + f)
+ st.rename()
self.dirty = 0
def filterfiles(self, files):
--- a/mercurial/localrepo.py Sat Apr 07 04:45:27 2007 -0300
+++ b/mercurial/localrepo.py Tue Apr 10 14:05:15 2007 -0500
@@ -417,10 +417,11 @@
def _writebranchcache(self, branches, tip, tiprev):
try:
- f = self.opener("branch.cache", "w")
+ f = self.opener("branch.cache", "w", atomictemp=True)
f.write("%s %s\n" % (hex(tip), tiprev))
for label, node in branches.iteritems():
f.write("%s %s\n" % (hex(node), label))
+ f.rename()
except IOError:
pass
--- a/mercurial/templater.py Sat Apr 07 04:45:27 2007 -0300
+++ b/mercurial/templater.py Tue Apr 10 14:05:15 2007 -0500
@@ -27,7 +27,7 @@
is treated as name of template file.
templater is asked to expand a key in map. it looks up key, and
- looks for atrings like this: {foo}. it expands {foo} by looking up
+ looks for strings like this: {foo}. it expands {foo} by looking up
foo in map, and substituting it. expansion is recursive: it stops
when there is no more {foo} to replace.
--- a/mercurial/util.py Sat Apr 07 04:45:27 2007 -0300
+++ b/mercurial/util.py Tue Apr 10 14:05:15 2007 -0500
@@ -766,6 +766,9 @@
except:
return True
+_umask = os.umask(0)
+os.umask(_umask)
+
def checkexec(path):
"""
Check whether the given path is on a filesystem with UNIX-like exec flags
@@ -1103,18 +1106,32 @@
p = base
audit_p = audit
- def mktempcopy(name):
+ def mktempcopy(name, emptyok=False):
d, fn = os.path.split(name)
fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
os.close(fd)
- ofp = posixfile(temp, "wb")
+ # Temporary files are created with mode 0600, which is usually not
+ # what we want. If the original file already exists, just copy
+ # its mode. Otherwise, manually obey umask.
+ try:
+ st_mode = os.lstat(name).st_mode
+ except OSError, inst:
+ if inst.errno != errno.ENOENT:
+ raise
+ st_mode = 0666 & ~_umask
+ os.chmod(temp, st_mode)
+ if emptyok:
+ return temp
try:
try:
ifp = posixfile(name, "rb")
except IOError, inst:
+ if inst.errno == errno.ENOENT:
+ return temp
if not getattr(inst, 'filename', None):
inst.filename = name
raise
+ ofp = posixfile(temp, "wb")
for chunk in filechunkiter(ifp):
ofp.write(chunk)
ifp.close()
@@ -1123,15 +1140,13 @@
try: os.unlink(temp)
except: pass
raise
- st = os.lstat(name)
- os.chmod(temp, st.st_mode)
return temp
class atomictempfile(posixfile):
"""the file will only be copied when rename is called"""
def __init__(self, name, mode):
self.__name = name
- self.temp = mktempcopy(name)
+ self.temp = mktempcopy(name, emptyok=('w' in mode))
posixfile.__init__(self, self.temp, mode)
def rename(self):
if not self.closed:
@@ -1165,16 +1180,16 @@
try:
nlink = nlinks(f)
except OSError:
+ nlink = 0
d = os.path.dirname(f)
if not os.path.isdir(d):
os.makedirs(d)
- else:
- if atomic:
- return atomicfile(f, mode)
- elif atomictemp:
- return atomictempfile(f, mode)
- if nlink > 1:
- rename(mktempcopy(f), f)
+ if atomic:
+ return atomicfile(f, mode)
+ elif atomictemp:
+ return atomictempfile(f, mode)
+ if nlink > 1:
+ rename(mktempcopy(f), f)
return posixfile(f, mode)
return o
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-merge Tue Apr 10 14:05:15 2007 -0500
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+# Test issue 529 - mq aborts when merging patch deleting files
+
+rewrite_path()
+{
+ sed -e 's:\\:/:g' -e 's:[^ ]*/t/::g'
+}
+
+echo "[extensions]" >> $HGRCPATH
+echo "hgext.mq=" >> $HGRCPATH
+
+# Commit two dummy files in "init" changeset
+hg init t
+cd t
+echo a > a
+echo b > b
+hg ci -Am init
+hg tag -l init
+
+# Create a patch removing a
+hg qnew rm_a
+hg rm a
+hg qrefresh -m "rm a"
+
+# Save the patch queue so we can merge it later
+hg qsave -c -e 2>&1 | rewrite_path
+
+# Update b and commit in an "update" changeset
+hg up -C init
+echo b >> b
+hg st
+hg ci -m update
+
+# Here, qpush used to abort with :
+# The system cannot find the file specified => a
+hg manifest
+hg qpush -a -m 2>&1 | rewrite_path
+hg manifest
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-merge.out Tue Apr 10 14:05:15 2007 -0500
@@ -0,0 +1,11 @@
+adding a
+adding b
+copy .hg/patches to .hg/patches.1
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+M b
+a
+b
+merging with queue at: .hg/patches.1
+applying rm_a
+Now at: rm_a
+b