diff hgext/mq.py @ 4959:97b734fb9c6f

Use try/finally pattern to cleanup locks and transactions
author Matt Mackall <mpm@selenic.com>
date Sat, 21 Jul 2007 16:02:10 -0500
parents 30847b8af7ca
children 126f527b3ba3
line wrap: on
line diff
--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -439,24 +439,28 @@ class queue:
     def apply(self, repo, series, list=False, update_status=True,
               strict=False, patchdir=None, merge=None, wlock=None,
               all_files={}):
-        if not wlock:
-            wlock = repo.wlock()
-        lock = repo.lock()
-        tr = repo.transaction()
+        lock = tr = None
         try:
-            ret = self._apply(tr, repo, series, list, update_status,
-                              strict, patchdir, merge, wlock,
-                              lock=lock, all_files=all_files)
-            tr.close()
-            self.save_dirty()
-            return ret
-        except:
+            if not wlock:
+                wlock = repo.wlock()
+            lock = repo.lock()
+            tr = repo.transaction()
             try:
-                tr.abort()
-            finally:
-                repo.invalidate()
-                repo.dirstate.invalidate()
-            raise
+                ret = self._apply(tr, repo, series, list, update_status,
+                                  strict, patchdir, merge, wlock,
+                                  lock=lock, all_files=all_files)
+                tr.close()
+                self.save_dirty()
+                return ret
+            except:
+                try:
+                    tr.abort()
+                finally:
+                    repo.invalidate()
+                    repo.dirstate.invalidate()
+                raise
+        finally:
+            del lock, wlock, tr
 
     def _apply(self, tr, repo, series, list=False, update_status=True,
                strict=False, patchdir=None, merge=None, wlock=None,
@@ -616,44 +620,51 @@ class queue:
         commitfiles = m + a + r
         self.check_toppatch(repo)
         wlock = repo.wlock()
-        insert = self.full_series_end()
-        if msg:
-            n = repo.commit(commitfiles, msg, force=True, wlock=wlock)
-        else:
-            n = repo.commit(commitfiles,
-                            "[mq]: %s" % patch, force=True, wlock=wlock)
-        if n == None:
-            raise util.Abort(_("repo commit failed"))
-        self.full_series[insert:insert] = [patch]
-        self.applied.append(statusentry(revlog.hex(n), patch))
-        self.parse_series()
-        self.series_dirty = 1
-        self.applied_dirty = 1
-        p = self.opener(patch, "w")
-        if msg:
-            msg = msg + "\n"
-            p.write(msg)
-        p.close()
-        wlock = None
-        r = self.qrepo()
-        if r: r.add([patch])
-        if commitfiles:
-            self.refresh(repo, short=True)
-        self.removeundo(repo)
+        try:
+            insert = self.full_series_end()
+            if msg:
+                n = repo.commit(commitfiles, msg, force=True, wlock=wlock)
+            else:
+                n = repo.commit(commitfiles,
+                                "[mq]: %s" % patch, force=True, wlock=wlock)
+            if n == None:
+                raise util.Abort(_("repo commit failed"))
+            self.full_series[insert:insert] = [patch]
+            self.applied.append(statusentry(revlog.hex(n), patch))
+            self.parse_series()
+            self.series_dirty = 1
+            self.applied_dirty = 1
+            p = self.opener(patch, "w")
+            if msg:
+                msg = msg + "\n"
+                p.write(msg)
+            p.close()
+            wlock = None
+            r = self.qrepo()
+            if r: r.add([patch])
+            if commitfiles:
+                self.refresh(repo, short=True)
+            self.removeundo(repo)
+        finally:
+            del wlock
 
     def strip(self, repo, rev, update=True, backup="all", wlock=None):
-        if not wlock:
-            wlock = repo.wlock()
-        lock = repo.lock()
+        lock = None
+        try:
+            if not wlock:
+                wlock = repo.wlock()
+            lock = repo.lock()
 
-        if update:
-            self.check_localchanges(repo, refresh=False)
-            urev = self.qparents(repo, rev)
-            hg.clean(repo, urev, wlock=wlock)
-            repo.dirstate.write()
+            if update:
+                self.check_localchanges(repo, refresh=False)
+                urev = self.qparents(repo, rev)
+                hg.clean(repo, urev, wlock=wlock)
+                repo.dirstate.write()
 
-        self.removeundo(repo)
-        repair.strip(self.ui, repo, rev, backup)
+            self.removeundo(repo)
+            repair.strip(self.ui, repo, rev, backup)
+        finally:
+            del lock, wlock
 
     def isapplied(self, patch):
         """returns (index, rev, patch)"""
@@ -740,69 +751,74 @@ class queue:
              mergeq=None, wlock=None):
         if not wlock:
             wlock = repo.wlock()
-        patch = self.lookup(patch)
-        # Suppose our series file is: A B C and the current 'top' patch is B.
-        # qpush C should be performed (moving forward)
-        # qpush B is a NOP (no change)
-        # qpush A is an error (can't go backwards with qpush)
-        if patch:
-            info = self.isapplied(patch)
-            if info:
-                if info[0] < len(self.applied) - 1:
-                    raise util.Abort(_("cannot push to a previous patch: %s") %
-                                     patch)
-                if info[0] < len(self.series) - 1:
-                    self.ui.warn(_('qpush: %s is already at the top\n') % patch)
-                else:
-                    self.ui.warn(_('all patches are currently applied\n'))
-                return
+        try:
+            patch = self.lookup(patch)
+            # Suppose our series file is: A B C and the current 'top'
+            # patch is B. qpush C should be performed (moving forward)
+            # qpush B is a NOP (no change) qpush A is an error (can't
+            # go backwards with qpush)
+            if patch:
+                info = self.isapplied(patch)
+                if info:
+                    if info[0] < len(self.applied) - 1:
+                        raise util.Abort(
+                            _("cannot push to a previous patch: %s") % patch)
+                    if info[0] < len(self.series) - 1:
+                        self.ui.warn(
+                            _('qpush: %s is already at the top\n') % patch)
+                    else:
+                        self.ui.warn(_('all patches are currently applied\n'))
+                    return
 
-        # Following the above example, starting at 'top' of B:
-        #  qpush should be performed (pushes C), but a subsequent qpush without
-        #  an argument is an error (nothing to apply). This allows a loop
-        #  of "...while hg qpush..." to work as it detects an error when done
-        if self.series_end() == len(self.series):
-            self.ui.warn(_('patch series already fully applied\n'))
-            return 1
-        if not force:
-            self.check_localchanges(repo)
+            # Following the above example, starting at 'top' of B:
+            # qpush should be performed (pushes C), but a subsequent
+            # qpush without an argument is an error (nothing to
+            # apply). This allows a loop of "...while hg qpush..." to
+            # work as it detects an error when done
+            if self.series_end() == len(self.series):
+                self.ui.warn(_('patch series already fully applied\n'))
+                return 1
+            if not force:
+                self.check_localchanges(repo)
 
-        self.applied_dirty = 1;
-        start = self.series_end()
-        if start > 0:
-            self.check_toppatch(repo)
-        if not patch:
-            patch = self.series[start]
-            end = start + 1
-        else:
-            end = self.series.index(patch, start) + 1
-        s = self.series[start:end]
-        all_files = {}
-        try:
-            if mergeq:
-                ret = self.mergepatch(repo, mergeq, s, wlock)
+            self.applied_dirty = 1;
+            start = self.series_end()
+            if start > 0:
+                self.check_toppatch(repo)
+            if not patch:
+                patch = self.series[start]
+                end = start + 1
             else:
-                ret = self.apply(repo, s, list, wlock=wlock,
-                                 all_files=all_files)
-        except:
-            self.ui.warn(_('cleaning up working directory...'))
-            node = repo.dirstate.parents()[0]
-            hg.revert(repo, node, None, wlock)
-            unknown = repo.status(wlock=wlock)[4]
-            # only remove unknown files that we know we touched or
-            # created while patching
-            for f in unknown:
-                if f in all_files:
-                    util.unlink(repo.wjoin(f))
-            self.ui.warn(_('done\n'))
-            raise
-        top = self.applied[-1].name
-        if ret[0]:
-            self.ui.write("Errors during apply, please fix and refresh %s\n" %
-                          top)
-        else:
-            self.ui.write("Now at: %s\n" % top)
-        return ret[0]
+                end = self.series.index(patch, start) + 1
+            s = self.series[start:end]
+            all_files = {}
+            try:
+                if mergeq:
+                    ret = self.mergepatch(repo, mergeq, s, wlock)
+                else:
+                    ret = self.apply(repo, s, list, wlock=wlock,
+                                     all_files=all_files)
+            except:
+                self.ui.warn(_('cleaning up working directory...'))
+                node = repo.dirstate.parents()[0]
+                hg.revert(repo, node, None, wlock)
+                unknown = repo.status(wlock=wlock)[4]
+                # only remove unknown files that we know we touched or
+                # created while patching
+                for f in unknown:
+                    if f in all_files:
+                        util.unlink(repo.wjoin(f))
+                self.ui.warn(_('done\n'))
+                raise
+            top = self.applied[-1].name
+            if ret[0]:
+                self.ui.write(
+                    "Errors during apply, please fix and refresh %s\n" % top)
+            else:
+                self.ui.write("Now at: %s\n" % top)
+            return ret[0]
+        finally:
+            del wlock
 
     def pop(self, repo, patch=None, force=False, update=True, all=False,
             wlock=None):
@@ -812,82 +828,85 @@ class queue:
 
         if not wlock:
             wlock = repo.wlock()
-        if patch:
-            # index, rev, patch
-            info = self.isapplied(patch)
-            if not info:
-                patch = self.lookup(patch)
-            info = self.isapplied(patch)
-            if not info:
-                raise util.Abort(_("patch %s is not applied") % patch)
+        try:
+            if patch:
+                # index, rev, patch
+                info = self.isapplied(patch)
+                if not info:
+                    patch = self.lookup(patch)
+                info = self.isapplied(patch)
+                if not info:
+                    raise util.Abort(_("patch %s is not applied") % patch)
 
-        if len(self.applied) == 0:
-            # Allow qpop -a to work repeatedly,
-            # but not qpop without an argument
-            self.ui.warn(_("no patches applied\n"))
-            return not all
+            if len(self.applied) == 0:
+                # Allow qpop -a to work repeatedly,
+                # but not qpop without an argument
+                self.ui.warn(_("no patches applied\n"))
+                return not all
 
-        if not update:
-            parents = repo.dirstate.parents()
-            rr = [ revlog.bin(x.rev) for x in self.applied ]
-            for p in parents:
-                if p in rr:
-                    self.ui.warn("qpop: forcing dirstate update\n")
-                    update = True
+            if not update:
+                parents = repo.dirstate.parents()
+                rr = [ revlog.bin(x.rev) for x in self.applied ]
+                for p in parents:
+                    if p in rr:
+                        self.ui.warn("qpop: forcing dirstate update\n")
+                        update = True
 
-        if not force and update:
-            self.check_localchanges(repo)
+            if not force and update:
+                self.check_localchanges(repo)
 
-        self.applied_dirty = 1;
-        end = len(self.applied)
-        if not patch:
-            if all:
-                popi = 0
+            self.applied_dirty = 1;
+            end = len(self.applied)
+            if not patch:
+                if all:
+                    popi = 0
+                else:
+                    popi = len(self.applied) - 1
             else:
-                popi = len(self.applied) - 1
-        else:
-            popi = info[0] + 1
-            if popi >= end:
-                self.ui.warn("qpop: %s is already at the top\n" % patch)
-                return
-        info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
+                popi = info[0] + 1
+                if popi >= end:
+                    self.ui.warn("qpop: %s is already at the top\n" % patch)
+                    return
+            info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
 
-        start = info[0]
-        rev = revlog.bin(info[1])
+            start = info[0]
+            rev = revlog.bin(info[1])
 
-        # we know there are no local changes, so we can make a simplified
-        # form of hg.update.
-        if update:
-            top = self.check_toppatch(repo)
-            qp = self.qparents(repo, rev)
-            changes = repo.changelog.read(qp)
-            mmap = repo.manifest.read(changes[0])
-            m, a, r, d, u = repo.status(qp, top)[:5]
-            if d:
-                raise util.Abort("deletions found between repo revs")
-            for f in m:
-                getfile(f, mmap[f])
-            for f in r:
-                getfile(f, mmap[f])
-                util.set_exec(repo.wjoin(f), mmap.execf(f))
-            for f in m + r:
-                repo.dirstate.normal(f)
-            for f in a:
-                try:
-                    os.unlink(repo.wjoin(f))
-                except OSError, e:
-                    if e.errno != errno.ENOENT:
-                        raise
-                try: os.removedirs(os.path.dirname(repo.wjoin(f)))
-                except: pass
-                repo.dirstate.forget(f)
-            repo.dirstate.setparents(qp, revlog.nullid)
-        self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
-        del self.applied[start:end]
-        if len(self.applied):
-            self.ui.write("Now at: %s\n" % self.applied[-1].name)
-        else:
-            self.ui.write("Patch queue now empty\n")
+            # we know there are no local changes, so we can make a simplified
+            # form of hg.update.
+            if update:
+                top = self.check_toppatch(repo)
+                qp = self.qparents(repo, rev)
+                changes = repo.changelog.read(qp)
+                mmap = repo.manifest.read(changes[0])
+                m, a, r, d, u = repo.status(qp, top)[:5]
+                if d:
+                    raise util.Abort("deletions found between repo revs")
+                for f in m:
+                    getfile(f, mmap[f])
+                for f in r:
+                    getfile(f, mmap[f])
+                    util.set_exec(repo.wjoin(f), mmap.execf(f))
+                for f in m + r:
+                    repo.dirstate.normal(f)
+                for f in a:
+                    try:
+                        os.unlink(repo.wjoin(f))
+                    except OSError, e:
+                        if e.errno != errno.ENOENT:
+                            raise
+                    try: os.removedirs(os.path.dirname(repo.wjoin(f)))
+                    except: pass
+                    repo.dirstate.forget(f)
+                repo.dirstate.setparents(qp, revlog.nullid)
+            self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
+            del self.applied[start:end]
+            if len(self.applied):
+                self.ui.write("Now at: %s\n" % self.applied[-1].name)
+            else:
+                self.ui.write("Patch queue now empty\n")
+        finally:
+            del wlock
 
     def diff(self, repo, pats, opts):
         top = self.check_toppatch(repo)
@@ -904,179 +923,184 @@ class queue:
             self.ui.write("No patches applied\n")
             return 1
         wlock = repo.wlock()
-        self.check_toppatch(repo)
-        (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
-        top = revlog.bin(top)
-        cparents = repo.changelog.parents(top)
-        patchparent = self.qparents(repo, top)
-        message, comments, user, date, patchfound = self.readheaders(patchfn)
-
-        patchf = self.opener(patchfn, 'r+')
-
-        # if the patch was a git patch, refresh it as a git patch
-        for line in patchf:
-            if line.startswith('diff --git'):
-                self.diffopts().git = True
-                break
-        patchf.seek(0)
-        patchf.truncate()
+        try:
+            self.check_toppatch(repo)
+            (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
+            top = revlog.bin(top)
+            cparents = repo.changelog.parents(top)
+            patchparent = self.qparents(repo, top)
+            message, comments, user, date, patchfound = self.readheaders(patchfn)
 
-        msg = opts.get('msg', '').rstrip()
-        if msg:
-            if comments:
-                # Remove existing message.
-                ci = 0
-                subj = None
-                for mi in xrange(len(message)):
-                    if comments[ci].lower().startswith('subject: '):
-                        subj = comments[ci][9:]
-                    while message[mi] != comments[ci] and message[mi] != subj:
-                        ci += 1
-                    del comments[ci]
-            comments.append(msg)
-        if comments:
-            comments = "\n".join(comments) + '\n\n'
-            patchf.write(comments)
+            patchf = self.opener(patchfn, 'r+')
+
+            # if the patch was a git patch, refresh it as a git patch
+            for line in patchf:
+                if line.startswith('diff --git'):
+                    self.diffopts().git = True
+                    break
+            patchf.seek(0)
+            patchf.truncate()
 
-        if opts.get('git'):
-            self.diffopts().git = True
-        fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
-        tip = repo.changelog.tip()
-        if top == tip:
-            # if the top of our patch queue is also the tip, there is an
-            # optimization here.  We update the dirstate in place and strip
-            # off the tip commit.  Then just commit the current directory
-            # tree.  We can also send repo.commit the list of files
-            # changed to speed up the diff
-            #
-            # in short mode, we only diff the files included in the
-            # patch already
-            #
-            # this should really read:
-            #   mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
-            # but we do it backwards to take advantage of manifest/chlog
-            # caching against the next repo.status call
-            #
-            mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
-            changes = repo.changelog.read(tip)
-            man = repo.manifest.read(changes[0])
-            aaa = aa[:]
-            if opts.get('short'):
-                filelist = mm + aa + dd
-                match = dict.fromkeys(filelist).__contains__
-            else:
-                filelist = None
-                match = util.always
-            m, a, r, d, u = repo.status(files=filelist, match=match)[:5]
+            msg = opts.get('msg', '').rstrip()
+            if msg:
+                if comments:
+                    # Remove existing message.
+                    ci = 0
+                    subj = None
+                    for mi in xrange(len(message)):
+                        if comments[ci].lower().startswith('subject: '):
+                            subj = comments[ci][9:]
+                        while message[mi] != comments[ci] and message[mi] != subj:
+                            ci += 1
+                        del comments[ci]
+                comments.append(msg)
+            if comments:
+                comments = "\n".join(comments) + '\n\n'
+                patchf.write(comments)
 
-            # we might end up with files that were added between tip and
-            # the dirstate parent, but then changed in the local dirstate.
-            # in this case, we want them to only show up in the added section
-            for x in m:
-                if x not in aa:
-                    mm.append(x)
-            # we might end up with files added by the local dirstate that
-            # were deleted by the patch.  In this case, they should only
-            # show up in the changed section.
-            for x in a:
-                if x in dd:
-                    del dd[dd.index(x)]
-                    mm.append(x)
+            if opts.get('git'):
+                self.diffopts().git = True
+            fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
+            tip = repo.changelog.tip()
+            if top == tip:
+                # if the top of our patch queue is also the tip, there is an
+                # optimization here.  We update the dirstate in place and strip
+                # off the tip commit.  Then just commit the current directory
+                # tree.  We can also send repo.commit the list of files
+                # changed to speed up the diff
+                #
+                # in short mode, we only diff the files included in the
+                # patch already
+                #
+                # this should really read:
+                #   mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
+                # but we do it backwards to take advantage of manifest/chlog
+                # caching against the next repo.status call
+                #
+                mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
+                changes = repo.changelog.read(tip)
+                man = repo.manifest.read(changes[0])
+                aaa = aa[:]
+                if opts.get('short'):
+                    filelist = mm + aa + dd
+                    match = dict.fromkeys(filelist).__contains__
                 else:
-                    aa.append(x)
-            # make sure any files deleted in the local dirstate
-            # are not in the add or change column of the patch
-            forget = []
-            for x in d + r:
-                if x in aa:
-                    del aa[aa.index(x)]
-                    forget.append(x)
-                    continue
-                elif x in mm:
-                    del mm[mm.index(x)]
-                dd.append(x)
+                    filelist = None
+                    match = util.always
+                m, a, r, d, u = repo.status(files=filelist, match=match)[:5]
 
-            m = util.unique(mm)
-            r = util.unique(dd)
-            a = util.unique(aa)
-            c = [filter(matchfn, l) for l in (m, a, r, [], u)]
-            filelist = util.unique(c[0] + c[1] + c[2])
-            patch.diff(repo, patchparent, files=filelist, match=matchfn,
-                       fp=patchf, changes=c, opts=self.diffopts())
-            patchf.close()
+                # we might end up with files that were added between
+                # tip and the dirstate parent, but then changed in the
+                # local dirstate. in this case, we want them to only
+                # show up in the added section
+                for x in m:
+                    if x not in aa:
+                        mm.append(x)
+                # we might end up with files added by the local dirstate that
+                # were deleted by the patch.  In this case, they should only
+                # show up in the changed section.
+                for x in a:
+                    if x in dd:
+                        del dd[dd.index(x)]
+                        mm.append(x)
+                    else:
+                        aa.append(x)
+                # make sure any files deleted in the local dirstate
+                # are not in the add or change column of the patch
+                forget = []
+                for x in d + r:
+                    if x in aa:
+                        del aa[aa.index(x)]
+                        forget.append(x)
+                        continue
+                    elif x in mm:
+                        del mm[mm.index(x)]
+                    dd.append(x)
+
+                m = util.unique(mm)
+                r = util.unique(dd)
+                a = util.unique(aa)
+                c = [filter(matchfn, l) for l in (m, a, r, [], u)]
+                filelist = util.unique(c[0] + c[1] + c[2])
+                patch.diff(repo, patchparent, files=filelist, match=matchfn,
+                           fp=patchf, changes=c, opts=self.diffopts())
+                patchf.close()
 
-            repo.dirstate.setparents(*cparents)
-            copies = {}
-            for dst in a:
-                src = repo.dirstate.copied(dst)
-                if src is None:
-                    continue
-                copies.setdefault(src, []).append(dst)
-                repo.dirstate.add(dst)
-            # remember the copies between patchparent and tip
-            # this may be slow, so don't do it if we're not tracking copies
-            if self.diffopts().git:
-                for dst in aaa:
-                    f = repo.file(dst)
-                    src = f.renamed(man[dst])
-                    if src:
-                        copies[src[0]] = copies.get(dst, [])
-                        if dst in a:
-                            copies[src[0]].append(dst)
-                    # we can't copy a file created by the patch itself
-                    if dst in copies:
-                        del copies[dst]
-            for src, dsts in copies.iteritems():
-                for dst in dsts:
-                    repo.dirstate.copy(src, dst)
-            for f in r:
-                repo.dirstate.remove(f)
-            # if the patch excludes a modified file, mark that file with mtime=0
-            # so status can see it.
-            mm = []
-            for i in xrange(len(m)-1, -1, -1):
-                if not matchfn(m[i]):
-                    mm.append(m[i])
-                    del m[i]
-            for f in m:
-                repo.dirstate.normal(f)
-            for f in mm:
-                repo.dirstate.normaldirty(f)
-            for f in forget:
-                repo.dirstate.forget(f)
+                repo.dirstate.setparents(*cparents)
+                copies = {}
+                for dst in a:
+                    src = repo.dirstate.copied(dst)
+                    if src is None:
+                        continue
+                    copies.setdefault(src, []).append(dst)
+                    repo.dirstate.add(dst)
+                # remember the copies between patchparent and tip
+                # this may be slow, so don't do it if we're not tracking copies
+                if self.diffopts().git:
+                    for dst in aaa:
+                        f = repo.file(dst)
+                        src = f.renamed(man[dst])
+                        if src:
+                            copies[src[0]] = copies.get(dst, [])
+                            if dst in a:
+                                copies[src[0]].append(dst)
+                        # we can't copy a file created by the patch itself
+                        if dst in copies:
+                            del copies[dst]
+                for src, dsts in copies.iteritems():
+                    for dst in dsts:
+                        repo.dirstate.copy(src, dst)
+                for f in r:
+                    repo.dirstate.remove(f)
+                # if the patch excludes a modified file, mark that
+                # file with mtime=0 so status can see it.
+                mm = []
+                for i in xrange(len(m)-1, -1, -1):
+                    if not matchfn(m[i]):
+                        mm.append(m[i])
+                        del m[i]
+                for f in m:
+                    repo.dirstate.normal(f)
+                for f in mm:
+                    repo.dirstate.normaldirty(f)
+                for f in forget:
+                    repo.dirstate.forget(f)
 
-            if not msg:
-                if not message:
-                    message = "[mq]: %s\n" % patchfn
+                if not msg:
+                    if not message:
+                        message = "[mq]: %s\n" % patchfn
+                    else:
+                        message = "\n".join(message)
                 else:
-                    message = "\n".join(message)
-            else:
-                message = msg
+                    message = msg
 
-            self.strip(repo, top, update=False, backup='strip', wlock=wlock)
-            n = repo.commit(filelist, message, changes[1], match=matchfn,
-                            force=1, wlock=wlock)
-            self.applied[-1] = statusentry(revlog.hex(n), patchfn)
-            self.applied_dirty = 1
-            self.removeundo(repo)
-        else:
-            self.printdiff(repo, patchparent, fp=patchf)
-            patchf.close()
-            added = repo.status()[1]
-            for a in added:
-                f = repo.wjoin(a)
-                try:
-                    os.unlink(f)
-                except OSError, e:
-                    if e.errno != errno.ENOENT:
-                        raise
-                try: os.removedirs(os.path.dirname(f))
-                except: pass
-                # forget the file copies in the dirstate
-                # push should readd the files later on
-                repo.dirstate.forget(a)
-            self.pop(repo, force=True, wlock=wlock)
-            self.push(repo, force=True, wlock=wlock)
+                self.strip(repo, top, update=False,
+                           backup='strip', wlock=wlock)
+                n = repo.commit(filelist, message, changes[1], match=matchfn,
+                                force=1, wlock=wlock)
+                self.applied[-1] = statusentry(revlog.hex(n), patchfn)
+                self.applied_dirty = 1
+                self.removeundo(repo)
+            else:
+                self.printdiff(repo, patchparent, fp=patchf)
+                patchf.close()
+                added = repo.status()[1]
+                for a in added:
+                    f = repo.wjoin(a)
+                    try:
+                        os.unlink(f)
+                    except OSError, e:
+                        if e.errno != errno.ENOENT:
+                            raise
+                    try: os.removedirs(os.path.dirname(f))
+                    except: pass
+                    # forget the file copies in the dirstate
+                    # push should readd the files later on
+                    repo.dirstate.forget(a)
+                self.pop(repo, force=True, wlock=wlock)
+                self.push(repo, force=True, wlock=wlock)
+        finally:
+            del wlock
 
     def init(self, repo, create=False):
         if not create and os.path.isdir(self.path):
@@ -1872,10 +1896,13 @@ def rename(ui, repo, patch, name=None, *
     r = q.qrepo()
     if r:
         wlock = r.wlock()
-        if r.dirstate[name] == 'r':
-            r.undelete([name], wlock)
-        r.copy(patch, name, wlock)
-        r.remove([patch], False, wlock)
+        try:
+            if r.dirstate[name] == 'r':
+                r.undelete([name], wlock)
+            r.copy(patch, name, wlock)
+            r.remove([patch], False, wlock)
+        finally:
+            del wlock
 
     q.save_dirty()