diff mercurial/localrepo.py @ 4915: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 9a2a73ea6135
children 5c5d23d93447
line wrap: on
line diff
--- a/mercurial/localrepo.py	Sat Jul 21 16:02:09 2007 -0500
+++ b/mercurial/localrepo.py	Sat Jul 21 16:02:10 2007 -0500
@@ -516,28 +516,34 @@
 
     def recover(self):
         l = self.lock()
-        if os.path.exists(self.sjoin("journal")):
-            self.ui.status(_("rolling back interrupted transaction\n"))
-            transaction.rollback(self.sopener, self.sjoin("journal"))
-            self.invalidate()
-            return True
-        else:
-            self.ui.warn(_("no interrupted transaction available\n"))
-            return False
+        try:
+            if os.path.exists(self.sjoin("journal")):
+                self.ui.status(_("rolling back interrupted transaction\n"))
+                transaction.rollback(self.sopener, self.sjoin("journal"))
+                self.invalidate()
+                return True
+            else:
+                self.ui.warn(_("no interrupted transaction available\n"))
+                return False
+        finally:
+            del l
 
     def rollback(self, wlock=None, lock=None):
-        if not wlock:
-            wlock = self.wlock()
-        if not lock:
-            lock = self.lock()
-        if os.path.exists(self.sjoin("undo")):
-            self.ui.status(_("rolling back last transaction\n"))
-            transaction.rollback(self.sopener, self.sjoin("undo"))
-            util.rename(self.join("undo.dirstate"), self.join("dirstate"))
-            self.invalidate()
-            self.dirstate.invalidate()
-        else:
-            self.ui.warn(_("no rollback information available\n"))
+        try:
+            if not wlock:
+                wlock = self.wlock()
+            if not lock:
+                lock = self.lock()
+            if os.path.exists(self.sjoin("undo")):
+                self.ui.status(_("rolling back last transaction\n"))
+                transaction.rollback(self.sopener, self.sjoin("undo"))
+                util.rename(self.join("undo.dirstate"), self.join("dirstate"))
+                self.invalidate()
+                self.dirstate.invalidate()
+            else:
+                self.ui.warn(_("no rollback information available\n"))
+        finally:
+            del wlock, lock
 
     def invalidate(self):
         for a in "changelog manifest".split():
@@ -639,164 +645,169 @@
     def commit(self, files=None, text="", user=None, date=None,
                match=util.always, force=False, lock=None, wlock=None,
                force_editor=False, p1=None, p2=None, extra={}):
-
-        commit = []
-        remove = []
-        changed = []
-        use_dirstate = (p1 is None) # not rawcommit
-        extra = extra.copy()
+        tr = None
+        try:
+            commit = []
+            remove = []
+            changed = []
+            use_dirstate = (p1 is None) # not rawcommit
+            extra = extra.copy()
 
-        if use_dirstate:
-            if files:
-                for f in files:
-                    s = self.dirstate[f]
-                    if s in 'nma':
-                        commit.append(f)
-                    elif s == 'r':
-                        remove.append(f)
-                    else:
-                        self.ui.warn(_("%s not tracked!\n") % f)
+            if use_dirstate:
+                if files:
+                    for f in files:
+                        s = self.dirstate[f]
+                        if s in 'nma':
+                            commit.append(f)
+                        elif s == 'r':
+                            remove.append(f)
+                        else:
+                            self.ui.warn(_("%s not tracked!\n") % f)
+                else:
+                    changes = self.status(match=match)[:5]
+                    modified, added, removed, deleted, unknown = changes
+                    commit = modified + added
+                    remove = removed
             else:
-                changes = self.status(match=match)[:5]
-                modified, added, removed, deleted, unknown = changes
-                commit = modified + added
-                remove = removed
-        else:
-            commit = files
+                commit = files
 
-        if use_dirstate:
-            p1, p2 = self.dirstate.parents()
-            update_dirstate = True
-        else:
-            p1, p2 = p1, p2 or nullid
-            update_dirstate = (self.dirstate.parents()[0] == p1)
+            if use_dirstate:
+                p1, p2 = self.dirstate.parents()
+                update_dirstate = True
+            else:
+                p1, p2 = p1, p2 or nullid
+                update_dirstate = (self.dirstate.parents()[0] == p1)
 
-        c1 = self.changelog.read(p1)
-        c2 = self.changelog.read(p2)
-        m1 = self.manifest.read(c1[0]).copy()
-        m2 = self.manifest.read(c2[0])
+            c1 = self.changelog.read(p1)
+            c2 = self.changelog.read(p2)
+            m1 = self.manifest.read(c1[0]).copy()
+            m2 = self.manifest.read(c2[0])
 
-        if use_dirstate:
-            branchname = self.workingctx().branch()
-            try:
-                branchname = branchname.decode('UTF-8').encode('UTF-8')
-            except UnicodeDecodeError:
-                raise util.Abort(_('branch name not in UTF-8!'))
-        else:
-            branchname = ""
+            if use_dirstate:
+                branchname = self.workingctx().branch()
+                try:
+                    branchname = branchname.decode('UTF-8').encode('UTF-8')
+                except UnicodeDecodeError:
+                    raise util.Abort(_('branch name not in UTF-8!'))
+            else:
+                branchname = ""
 
-        if use_dirstate:
-            oldname = c1[5].get("branch") # stored in UTF-8
-            if (not commit and not remove and not force and p2 == nullid
-                and branchname == oldname):
-                self.ui.status(_("nothing changed\n"))
-                return None
+            if use_dirstate:
+                oldname = c1[5].get("branch") # stored in UTF-8
+                if (not commit and not remove and not force and p2 == nullid
+                    and branchname == oldname):
+                    self.ui.status(_("nothing changed\n"))
+                    return None
 
-        xp1 = hex(p1)
-        if p2 == nullid: xp2 = ''
-        else: xp2 = hex(p2)
+            xp1 = hex(p1)
+            if p2 == nullid: xp2 = ''
+            else: xp2 = hex(p2)
 
-        self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
+            self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
 
-        if not wlock:
-            wlock = self.wlock()
-        if not lock:
-            lock = self.lock()
-        tr = self.transaction()
+            if not wlock:
+                wlock = self.wlock()
+            if not lock:
+                lock = self.lock()
+            tr = self.transaction()
 
-        # check in files
-        new = {}
-        linkrev = self.changelog.count()
-        commit.sort()
-        is_exec = util.execfunc(self.root, m1.execf)
-        is_link = util.linkfunc(self.root, m1.linkf)
-        for f in commit:
-            self.ui.note(f + "\n")
-            try:
-                new[f] = self.filecommit(f, m1, m2, linkrev, tr, changed)
-                new_exec = is_exec(f)
-                new_link = is_link(f)
-                if not changed or changed[-1] != f:
-                    # mention the file in the changelog if some flag changed,
-                    # even if there was no content change.
-                    old_exec = m1.execf(f)
-                    old_link = m1.linkf(f)
-                    if old_exec != new_exec or old_link != new_link:
-                        changed.append(f)
-                m1.set(f, new_exec, new_link)
-            except (OSError, IOError):
-                if use_dirstate:
-                    self.ui.warn(_("trouble committing %s!\n") % f)
-                    raise
-                else:
-                    remove.append(f)
+            # check in files
+            new = {}
+            linkrev = self.changelog.count()
+            commit.sort()
+            is_exec = util.execfunc(self.root, m1.execf)
+            is_link = util.linkfunc(self.root, m1.linkf)
+            for f in commit:
+                self.ui.note(f + "\n")
+                try:
+                    new[f] = self.filecommit(f, m1, m2, linkrev, tr, changed)
+                    new_exec = is_exec(f)
+                    new_link = is_link(f)
+                    if not changed or changed[-1] != f:
+                        # mention the file in the changelog if some
+                        # flag changed, even if there was no content
+                        # change.
+                        old_exec = m1.execf(f)
+                        old_link = m1.linkf(f)
+                        if old_exec != new_exec or old_link != new_link:
+                            changed.append(f)
+                    m1.set(f, new_exec, new_link)
+                except (OSError, IOError):
+                    if use_dirstate:
+                        self.ui.warn(_("trouble committing %s!\n") % f)
+                        raise
+                    else:
+                        remove.append(f)
 
-        # update manifest
-        m1.update(new)
-        remove.sort()
-        removed = []
+            # update manifest
+            m1.update(new)
+            remove.sort()
+            removed = []
 
-        for f in remove:
-            if f in m1:
-                del m1[f]
-                removed.append(f)
-            elif f in m2:
-                removed.append(f)
-        mn = self.manifest.add(m1, tr, linkrev, c1[0], c2[0], (new, removed))
+            for f in remove:
+                if f in m1:
+                    del m1[f]
+                    removed.append(f)
+                elif f in m2:
+                    removed.append(f)
+            mn = self.manifest.add(m1, tr, linkrev, c1[0], c2[0],
+                                   (new, removed))
 
-        # add changeset
-        new = new.keys()
-        new.sort()
+            # add changeset
+            new = new.keys()
+            new.sort()
 
-        user = user or self.ui.username()
-        if not text or force_editor:
-            edittext = []
-            if text:
-                edittext.append(text)
-            edittext.append("")
-            edittext.append("HG: user: %s" % user)
-            if p2 != nullid:
-                edittext.append("HG: branch merge")
-            if branchname:
-                edittext.append("HG: branch %s" % util.tolocal(branchname))
-            edittext.extend(["HG: changed %s" % f for f in changed])
-            edittext.extend(["HG: removed %s" % f for f in removed])
-            if not changed and not remove:
-                edittext.append("HG: no files changed")
-            edittext.append("")
-            # run editor in the repository root
-            olddir = os.getcwd()
-            os.chdir(self.root)
-            text = self.ui.edit("\n".join(edittext), user)
-            os.chdir(olddir)
+            user = user or self.ui.username()
+            if not text or force_editor:
+                edittext = []
+                if text:
+                    edittext.append(text)
+                edittext.append("")
+                edittext.append("HG: user: %s" % user)
+                if p2 != nullid:
+                    edittext.append("HG: branch merge")
+                if branchname:
+                    edittext.append("HG: branch %s" % util.tolocal(branchname))
+                edittext.extend(["HG: changed %s" % f for f in changed])
+                edittext.extend(["HG: removed %s" % f for f in removed])
+                if not changed and not remove:
+                    edittext.append("HG: no files changed")
+                edittext.append("")
+                # run editor in the repository root
+                olddir = os.getcwd()
+                os.chdir(self.root)
+                text = self.ui.edit("\n".join(edittext), user)
+                os.chdir(olddir)
 
-        lines = [line.rstrip() for line in text.rstrip().splitlines()]
-        while lines and not lines[0]:
-            del lines[0]
-        if not lines:
-            return None
-        text = '\n'.join(lines)
-        if branchname:
-            extra["branch"] = branchname
-        n = self.changelog.add(mn, changed + removed, text, tr, p1, p2,
-                               user, date, extra)
-        self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
-                  parent2=xp2)
-        tr.close()
+            lines = [line.rstrip() for line in text.rstrip().splitlines()]
+            while lines and not lines[0]:
+                del lines[0]
+            if not lines:
+                return None
+            text = '\n'.join(lines)
+            if branchname:
+                extra["branch"] = branchname
+            n = self.changelog.add(mn, changed + removed, text, tr, p1, p2,
+                                   user, date, extra)
+            self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
+                      parent2=xp2)
+            tr.close()
 
-        if self.branchcache and "branch" in extra:
-            self.branchcache[util.tolocal(extra["branch"])] = n
+            if self.branchcache and "branch" in extra:
+                self.branchcache[util.tolocal(extra["branch"])] = n
 
-        if use_dirstate or update_dirstate:
-            self.dirstate.setparents(n)
-            if use_dirstate:
-                for f in new:
-                    self.dirstate.normal(f)
-                for f in removed:
-                    self.dirstate.forget(f)
+            if use_dirstate or update_dirstate:
+                self.dirstate.setparents(n)
+                if use_dirstate:
+                    for f in new:
+                        self.dirstate.normal(f)
+                    for f in removed:
+                        self.dirstate.forget(f)
 
-        self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
-        return n
+            self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
+            return n
+        finally:
+            del lock, wlock, tr
 
     def walk(self, node=None, files=[], match=util.always, badmatch=None):
         '''
@@ -895,18 +906,18 @@
 
                     # update dirstate for files that are actually clean
                     if fixup:
-                        cleanup = False
-                        if not wlock:
-                            try:
-                                wlock = self.wlock(False)
-                                cleanup = True
-                            except lock.LockException:
-                                pass
-                        if wlock:
-                            for f in fixup:
-                                self.dirstate.normal(f)
-                        if cleanup:
-                                wlock.release()
+                        fixlock = wlock
+                        try:
+                            if not fixlock:
+                                try:
+                                    fixlock = self.wlock(False)
+                                except lock.LockException:
+                                    pass
+                            if fixlock:
+                                for f in fixup:
+                                    self.dirstate.normal(f)
+                        finally:
+                            del fixlock
             else:
                 # we are comparing working dir against non-parent
                 # generate a pseudo-manifest for the working dir
@@ -954,84 +965,99 @@
         return (modified, added, removed, deleted, unknown, ignored, clean)
 
     def add(self, list, wlock=None):
-        if not wlock:
-            wlock = self.wlock()
-        for f in list:
-            p = self.wjoin(f)
-            try:
-                st = os.lstat(p)
-            except:
-                self.ui.warn(_("%s does not exist!\n") % f)
-                continue
-            if st.st_size > 10000000:
-                self.ui.warn(_("%s: files over 10MB may cause memory and"
-                               " performance problems\n"
-                               "(use 'hg revert %s' to unadd the file)\n")
-                               % (f, f))
-            if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
-                self.ui.warn(_("%s not added: only files and symlinks "
-                               "supported currently\n") % f)
-            elif self.dirstate[f] in 'an':
-                self.ui.warn(_("%s already tracked!\n") % f)
-            else:
-                self.dirstate.add(f)
+        try:
+            if not wlock:
+                wlock = self.wlock()
+            for f in list:
+                p = self.wjoin(f)
+                try:
+                    st = os.lstat(p)
+                except:
+                    self.ui.warn(_("%s does not exist!\n") % f)
+                    continue
+                if st.st_size > 10000000:
+                    self.ui.warn(_("%s: files over 10MB may cause memory and"
+                                   " performance problems\n"
+                                   "(use 'hg revert %s' to unadd the file)\n")
+                                   % (f, f))
+                if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
+                    self.ui.warn(_("%s not added: only files and symlinks "
+                                   "supported currently\n") % f)
+                elif self.dirstate[f] in 'an':
+                    self.ui.warn(_("%s already tracked!\n") % f)
+                else:
+                    self.dirstate.add(f)
+        finally:
+            del wlock
 
     def forget(self, list, wlock=None):
-        if not wlock:
-            wlock = self.wlock()
-        for f in list:
-            if self.dirstate[f] != 'a':
-                self.ui.warn(_("%s not added!\n") % f)
-            else:
-                self.dirstate.forget(f)
+        try:
+            if not wlock:
+                wlock = self.wlock()
+            for f in list:
+                if self.dirstate[f] != 'a':
+                    self.ui.warn(_("%s not added!\n") % f)
+                else:
+                    self.dirstate.forget(f)
+        finally:
+            del wlock
 
     def remove(self, list, unlink=False, wlock=None):
-        if unlink:
+        try:
+            if unlink:
+                for f in list:
+                    try:
+                        util.unlink(self.wjoin(f))
+                    except OSError, inst:
+                        if inst.errno != errno.ENOENT:
+                            raise
+            if not wlock:
+                wlock = self.wlock()
             for f in list:
-                try:
-                    util.unlink(self.wjoin(f))
-                except OSError, inst:
-                    if inst.errno != errno.ENOENT:
-                        raise
-        if not wlock:
-            wlock = self.wlock()
-        for f in list:
-            if unlink and os.path.exists(self.wjoin(f)):
-                self.ui.warn(_("%s still exists!\n") % f)
-            elif self.dirstate[f] == 'a':
-                self.dirstate.forget(f)
-            elif f not in self.dirstate:
-                self.ui.warn(_("%s not tracked!\n") % f)
-            else:
-                self.dirstate.remove(f)
+                if unlink and os.path.exists(self.wjoin(f)):
+                    self.ui.warn(_("%s still exists!\n") % f)
+                elif self.dirstate[f] == 'a':
+                    self.dirstate.forget(f)
+                elif f not in self.dirstate:
+                    self.ui.warn(_("%s not tracked!\n") % f)
+                else:
+                    self.dirstate.remove(f)
+        finally:
+            del wlock
 
     def undelete(self, list, wlock=None):
-        p = self.dirstate.parents()[0]
-        mn = self.changelog.read(p)[0]
-        m = self.manifest.read(mn)
-        if not wlock:
-            wlock = self.wlock()
-        for f in list:
-            if self.dirstate[f] != 'r':
-                self.ui.warn("%s not removed!\n" % f)
-            else:
-                t = self.file(f).read(m[f])
-                self.wwrite(f, t, m.flags(f))
-                self.dirstate.normal(f)
+        try:
+            p = self.dirstate.parents()[0]
+            mn = self.changelog.read(p)[0]
+            m = self.manifest.read(mn)
+            if not wlock:
+                wlock = self.wlock()
+            for f in list:
+                if self.dirstate[f] != 'r':
+                    self.ui.warn("%s not removed!\n" % f)
+                else:
+                    t = self.file(f).read(m[f])
+                    self.wwrite(f, t, m.flags(f))
+                    self.dirstate.normal(f)
+        finally:
+            del wlock
 
     def copy(self, source, dest, wlock=None):
-        p = self.wjoin(dest)
-        if not (os.path.exists(p) or os.path.islink(p)):
-            self.ui.warn(_("%s does not exist!\n") % dest)
-        elif not (os.path.isfile(p) or os.path.islink(p)):
-            self.ui.warn(_("copy failed: %s is not a file or a "
-                           "symbolic link\n") % dest)
-        else:
-            if not wlock:
-                wlock = self.wlock()
-            if dest not in self.dirstate:
-                self.dirstate.add(dest)
-            self.dirstate.copy(source, dest)
+        try:
+            p = self.wjoin(dest)
+            if not (os.path.exists(p) or os.path.islink(p)):
+                self.ui.warn(_("%s does not exist!\n") % dest)
+            elif not (os.path.isfile(p) or os.path.islink(p)):
+                self.ui.warn(_("copy failed: %s is not a file or a "
+                               "symbolic link\n") % dest)
+            else:
+                if not wlock:
+                    wlock = self.wlock()
+                if dest not in self.dirstate:
+                    self.dirstate.add(dest)
+                self.dirstate.copy(source, dest)
+        finally:
+            del wlock
 
     def heads(self, start=None):
         heads = self.changelog.heads(start)
@@ -1309,12 +1335,9 @@
             return subset
 
     def pull(self, remote, heads=None, force=False, lock=None):
-        mylock = False
-        if not lock:
-            lock = self.lock()
-            mylock = True
-
         try:
+            if not lock:
+                lock = self.lock()
             fetch = self.findincoming(remote, force=force)
             if fetch == [nullid]:
                 self.ui.status(_("requesting all changes\n"))
@@ -1331,8 +1354,7 @@
                 cg = remote.changegroupsubset(fetch, heads, 'pull')
             return self.addchangegroup(cg, 'pull', remote.url())
         finally:
-            if mylock:
-                lock.release()
+            del lock
 
     def push(self, remote, force=False, revs=None):
         # there are two ways to push to remote repo:
@@ -1405,12 +1427,14 @@
 
     def push_addchangegroup(self, remote, force, revs):
         lock = remote.lock()
-
-        ret = self.prepush(remote, force, revs)
-        if ret[0] is not None:
-            cg, remote_heads = ret
-            return remote.addchangegroup(cg, 'push', self.url())
-        return ret[1]
+        try:
+            ret = self.prepush(remote, force, revs)
+            if ret[0] is not None:
+                cg, remote_heads = ret
+                return remote.addchangegroup(cg, 'push', self.url())
+            return ret[1]
+        finally:
+            del lock
 
     def push_unbundle(self, remote, force, revs):
         # local repo finds heads on server, finds out what revs it
@@ -1794,65 +1818,67 @@
 
         changesets = files = revisions = 0
 
-        tr = self.transaction()
-
         # write changelog data to temp files so concurrent readers will not see
         # inconsistent view
         cl = self.changelog
         cl.delayupdate()
         oldheads = len(cl.heads())
 
-        # pull off the changeset group
-        self.ui.status(_("adding changesets\n"))
-        cor = cl.count() - 1
-        chunkiter = changegroup.chunkiter(source)
-        if cl.addgroup(chunkiter, csmap, tr, 1) is None:
-            raise util.Abort(_("received changelog group is empty"))
-        cnr = cl.count() - 1
-        changesets = cnr - cor
+        tr = self.transaction()
+        try:
+            # pull off the changeset group
+            self.ui.status(_("adding changesets\n"))
+            cor = cl.count() - 1
+            chunkiter = changegroup.chunkiter(source)
+            if cl.addgroup(chunkiter, csmap, tr, 1) is None:
+                raise util.Abort(_("received changelog group is empty"))
+            cnr = cl.count() - 1
+            changesets = cnr - cor
 
-        # pull off the manifest group
-        self.ui.status(_("adding manifests\n"))
-        chunkiter = changegroup.chunkiter(source)
-        # no need to check for empty manifest group here:
-        # if the result of the merge of 1 and 2 is the same in 3 and 4,
-        # no new manifest will be created and the manifest group will
-        # be empty during the pull
-        self.manifest.addgroup(chunkiter, revmap, tr)
+            # pull off the manifest group
+            self.ui.status(_("adding manifests\n"))
+            chunkiter = changegroup.chunkiter(source)
+            # no need to check for empty manifest group here:
+            # if the result of the merge of 1 and 2 is the same in 3 and 4,
+            # no new manifest will be created and the manifest group will
+            # be empty during the pull
+            self.manifest.addgroup(chunkiter, revmap, tr)
 
-        # process the files
-        self.ui.status(_("adding file changes\n"))
-        while 1:
-            f = changegroup.getchunk(source)
-            if not f:
-                break
-            self.ui.debug(_("adding %s revisions\n") % f)
-            fl = self.file(f)
-            o = fl.count()
-            chunkiter = changegroup.chunkiter(source)
-            if fl.addgroup(chunkiter, revmap, tr) is None:
-                raise util.Abort(_("received file revlog group is empty"))
-            revisions += fl.count() - o
-            files += 1
+            # process the files
+            self.ui.status(_("adding file changes\n"))
+            while 1:
+                f = changegroup.getchunk(source)
+                if not f:
+                    break
+                self.ui.debug(_("adding %s revisions\n") % f)
+                fl = self.file(f)
+                o = fl.count()
+                chunkiter = changegroup.chunkiter(source)
+                if fl.addgroup(chunkiter, revmap, tr) is None:
+                    raise util.Abort(_("received file revlog group is empty"))
+                revisions += fl.count() - o
+                files += 1
+
+            # make changelog see real files again
+            cl.finalize(tr)
 
-        # make changelog see real files again
-        cl.finalize(tr)
+            newheads = len(self.changelog.heads())
+            heads = ""
+            if oldheads and newheads != oldheads:
+                heads = _(" (%+d heads)") % (newheads - oldheads)
 
-        newheads = len(self.changelog.heads())
-        heads = ""
-        if oldheads and newheads != oldheads:
-            heads = _(" (%+d heads)") % (newheads - oldheads)
+            self.ui.status(_("added %d changesets"
+                             " with %d changes to %d files%s\n")
+                             % (changesets, revisions, files, heads))
 
-        self.ui.status(_("added %d changesets"
-                         " with %d changes to %d files%s\n")
-                         % (changesets, revisions, files, heads))
+            if changesets > 0:
+                self.hook('pretxnchangegroup', throw=True,
+                          node=hex(self.changelog.node(cor+1)), source=srctype,
+                          url=url)
 
-        if changesets > 0:
-            self.hook('pretxnchangegroup', throw=True,
-                      node=hex(self.changelog.node(cor+1)), source=srctype,
-                      url=url)
-
-        tr.close()
+            tr.close()
+        finally:
+            del tr
 
         if changesets > 0:
             self.hook("changegroup", node=hex(self.changelog.node(cor+1)),