Index: tg/tests/test_tg_controller_dispatch.py
===================================================================
--- tg/tests/test_tg_controller_dispatch.py	(revision 3887)
+++ tg/tests/test_tg_controller_dispatch.py	(working copy)
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 
 import tg, pylons
-from tg.controllers import TurboGearsController
+from tg.controllers import TurboGearsController, url
 from pylons.decorators import expose
 from routes import Mapper
 from routes.middleware import RoutesMiddleware
@@ -77,6 +77,10 @@
         tg.flash("Wow, flash!")
         return tg.get_flash()
 
+    @expose()
+    def do_url(self, tgpath, **kw):
+        return url(tgpath.split(','), **kw)
+
 class TestTGController(TestWSGIController):
     def __init__(self, *args, **kargs):
         TestWSGIController.__init__(self, *args, **kargs)
@@ -143,3 +147,13 @@
         resp = self.app.get('/flash_unicode').follow()
         content = resp.body.decode('utf8')
         self.failUnless(u'Привет, мир!' in content, resp)
+
+    def test_url(self):
+        resp = self.app.get('/do_url?tgpath=/foo')
+        assert '/foo' in resp.body
+        resp = self.app.get('/do_url?tgpath=foo,bar')
+        assert 'foo/bar' in resp.body
+        resp = self.app.get('/do_url?tgpath=/foo&bar=1&baz=2')
+        assert resp.body in ['/foo?bar=1&baz=2', '/foo?baz=2&bar=1']
+        resp = self.app.get('/do_url?tgpath=/foo&bar=1&baz=2')
+        assert resp.body in ['/foo?bar=1&baz=2', '/foo?baz=2&bar=1']
Index: tg/controllers.py
===================================================================
--- tg/controllers.py	(revision 3887)
+++ tg/controllers.py	(working copy)
@@ -18,7 +18,7 @@
     def _dispatch_call(self):
         return self._perform_call(None, None)
 
-def redirect(url, params={}, **kw):
+def redirect(redirect_url, redirect_params={}, **kw):
     """Generate an HTTP redirect. The function raises an exception internally,
     which is handled by the framework. The URL may be either absolute (e.g.
     http://example.com or /myfile.html) or relative. Relative URLs are
@@ -27,25 +27,26 @@
     browser; if the request is POST, the browser will issue GET for the second
     request.
     """
-    url = urlparse.urljoin(request.path_info, url)
-    params.update(kw)
-    if params:
-        url += (('?' in url) and '&' or '?') + urllib.urlencode(params, True)
-    found = HTTPFound(url)
+    tgpath = url(redirect_url, redirect_params, **kw)
+    tgpath = urlparse.urljoin(request.path_info, tgpath)
+    found = HTTPFound(tgpath)
     # Merging cookies in global response into redirect
     for c in response.cookies.values():
         found.headers.append(('Set-Cookie', c.output(header='')))
     raise found
 
-def url(tgpath, tgparams=None, **kw):
+def url(tgpath, tgparams={}, **kw):
     """Broken url() re-implementation from TG1.
 
     See #1649 for more info.
     """
-    from tg import request
     if not isinstance(tgpath, basestring):
         tgpath = "/".join(list(tgpath))
-    path = request.relative_url(tgpath)
-    print 'path', path
-    base_url = request.path_url
-    return path[len(base_url):]
+    tgpath = urlparse.urljoin(request.path_info, tgpath)
+    if tgpath.startswith(request.path_info):
+        tgpath = tgpath[len(request.path_info):]
+    tgparams = tgparams.copy()
+    tgparams.update(kw)
+    if tgparams:
+        tgpath += (('?' in tgpath) and '&' or '?') + urllib.urlencode(tgparams, True)
+    return tgpath

