import os

ZULIP_VERSION = "2.0.4+git"
# Add information on number of commits and commit hash to version, if available
zulip_git_version_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'zulip-git-version')
if os.path.exists(zulip_git_version_file):
    with open(zulip_git_version_file) as f:
        version = f.read().strip()
        if version:
            ZULIP_VERSION = version

LATEST_MAJOR_VERSION = "2.0"
LATEST_RELEASE_VERSION = "2.0.4"
LATEST_RELEASE_ANNOUNCEMENT = "https://blog.zulip.org/2019/03/01/zulip-2-0-released/"

# Bump the minor PROVISION_VERSION to indicate that folks should provision
# only when going from an old version of the code to a newer version. Bump
# the major version to indicate that folks should provision in both
# directions.

# Typically,
# * adding a dependency only requires a minor version bump;
# * removing a dependency requires a major version bump;
# * upgrading a dependency requires a major version bump, unless the
#   upgraded dependency is backwards compatible with all of our
#   historical commits sharing the same major version, in which case a
#   minor version bump suffices.

PROVISION_VERSION = '49.1'

version.py
import os

ZULIP_VERSION = "2.0.4+git"
# Add information on number of commits and commit hash to version, if available
zulip_git_version_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'zulip-git-version')
if os.path.exists(zulip_git_version_file):
    with open(zulip_git_version_file) as f:
        version = f.read().strip()
        if version:
            ZULIP_VERSION = version

LATEST_MAJOR_VERSION = "2.0"
LATEST_RELEASE_VERSION = "2.0.4"
LATEST_RELEASE_ANNOUNCEMENT = "https://blog.zulip.org/2019/03/01/zulip-2-0-released/"

# Bump the minor PROVISION_VERSION to indicate that folks should provision
# only when going from an old version of the code to a newer version. Bump
# the major version to indicate that folks should provision in both
# directions.

# Typically,
# * adding a dependency only requires a minor version bump;
# * removing a dependency requires a major version bump;
# * upgrading a dependency requires a major version bump, unless the
#   upgraded dependency is backwards compatible with all of our
#   historical commits sharing the same major version, in which case a
#   minor version bump suffices.

PROVISION_VERSION = '49.2'

version.py
diff --git a/requirements/common.in b/requirements/common.in
index 4976af70d857c..de910b739d73e 100644
--- a/requirements/common.in
+++ b/requirements/common.in
@@ -49,13 +49,13 @@ python-gcm==0.4
 # Needed for the email mirror
 html2text==2018.1.9
 httplib2==0.13.0
--e git+https://github.com/zulip/talon.git@7d8bdc4dbcfcc5a73298747293b99fe53da55315#egg=talon==1.2.10.zulip1
+git+https://github.com/zulip/talon.git@7d8bdc4dbcfcc5a73298747293b99fe53da55315#egg=talon==1.2.10.zulip1
 
 # Needed for hipchat import
 hypchat==0.21
 
 # For doing highly usable Python profiling
--e git+https://github.com/zulip/line_profiler.git#egg=line_profiler==2.1.2.zulip1
+git+https://github.com/zulip/line_profiler.git#egg=line_profiler==2.1.2.zulip1
 
 # Needed for inlining the CSS in emails
 premailer==3.5.0
@@ -104,7 +104,7 @@ sourcemap==0.2.1
 tornado==4.5.3
 
 # Fast JSON parser
--e git+https://github.com/zulip/ultrajson@70ac02bec#egg=ujson==1.35+git
+git+https://github.com/zulip/ultrajson@70ac02bec#egg=ujson==1.35+git
 
 # Django extension for serving webpack modules
 django-webpack-loader==0.6.0
@@ -132,8 +132,8 @@ python-magic==0.4.15
 # these tightly, including fetching content not included in the normal
 # release tarballs (which is a bug).  So we need to pin it makes sense
 # to pin a version from Git rather than a release.
--e "git+https://github.com/zulip/python-zulip-api.git@804501610b6a205334e71b4e441fca60acf650da#egg=zulip==0.6.1_git&subdirectory=zulip"
--e "git+https://github.com/zulip/python-zulip-api.git@804501610b6a205334e71b4e441fca60acf650da#egg=zulip_bots==0.6.1+git&subdirectory=zulip_bots"
+git+https://github.com/zulip/python-zulip-api.git@804501610b6a205334e71b4e441fca60acf650da#egg=zulip==0.6.1_git&subdirectory=zulip
+git+https://github.com/zulip/python-zulip-api.git@804501610b6a205334e71b4e441fca60acf650da#egg=zulip_bots==0.6.1+git&subdirectory=zulip_bots
 
 # Used for Hesiod lookups, etc.
 py3dns==3.2.0
@@ -169,7 +169,7 @@ yamole==2.1.6
 
 # Needed for signing thumbnail requests so that they can be authenticated on the
 # other end.  Using a fork to eliminate a really slow pkgresources import.
--e "git+https://github.com/zulip/libthumbor.git@60ed2431c07686a12f2770b2d852c5650f3ccfc6#egg=libthumbor==1.3.2zulip"
+git+https://github.com/zulip/libthumbor.git@60ed2431c07686a12f2770b2d852c5650f3ccfc6#egg=libthumbor==1.3.2zulip
 
 # Needed for string matching in AlertWordProcessor
 pyahocorasick==1.4.0
diff --git a/requirements/dev.in b/requirements/dev.in
index ee6bc136fd55b..196613baae52b 100644
--- a/requirements/dev.in
+++ b/requirements/dev.in
@@ -50,9 +50,9 @@ transifex-client==0.12.5
 python-digitalocean==1.14.0
 
 # Needed for updating the locked pip dependencies
-pip-tools==2.0.2
+pip-tools==3.8.0
 
 # zulip's linting framework - zulint
--e git+https://github.com/zulip/zulint@aaed679f1ad38b230090eadd3870b7682500f60c#egg=zulint==0.0.1
+git+https://github.com/zulip/zulint@aaed679f1ad38b230090eadd3870b7682500f60c#egg=zulint==0.0.1
 
 -r mypy.in
diff --git a/requirements/dev.txt b/requirements/dev.txt
index a581482cbbc69..740a1669169ba 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -9,13 +9,6 @@
 #
 --no-binary psycopg2
 
-git+https://github.com/zulip/libthumbor.git@60ed2431c07686a12f2770b2d852c5650f3ccfc6#egg=libthumbor==1.3.2zulip
-git+https://github.com/zulip/line_profiler.git#egg=line_profiler==2.1.2.zulip1
-git+https://github.com/zulip/talon.git@7d8bdc4dbcfcc5a73298747293b99fe53da55315#egg=talon==1.2.10.zulip1
-git+https://github.com/zulip/ultrajson@70ac02bec#egg=ujson==1.35+git
-git+https://github.com/zulip/zulint@aaed679f1ad38b230090eadd3870b7682500f60c#egg=zulint==0.0.1
-git+https://github.com/zulip/python-zulip-api.git@804501610b6a205334e71b4e441fca60acf650da#egg=zulip==0.6.1_git&subdirectory=zulip
-git+https://github.com/zulip/python-zulip-api.git@804501610b6a205334e71b4e441fca60acf650da#egg=zulip_bots==0.6.1+git&subdirectory=zulip_bots
 alabaster==0.7.12         # via sphinx
 apns2==0.5.0
 argon2-cffi==19.1.0
@@ -63,7 +56,7 @@ docker==4.0.2             # via moto
 docutils==0.14            # via botocore, recommonmark, sphinx
 ecdsa==0.13.2             # via python-jose, sshpubkeys
 fakeldap==0.6.1
-first==2.0.2              # via pip-tools
+# future==0.17.1            # via commonmark, python-jose, python-twitter
 gitlint==0.12.0
 h2==2.6.2                 # via hyper
 hpack==3.0.0              # via h2
@@ -89,6 +82,8 @@ jsonpatch==1.23           # via cfn-lint
 jsonpickle==1.2           # via aws-xray-sdk, python-digitalocean
 jsonpointer==2.0          # via jsonpatch
 jsonschema==3.0.1         # via aws-sam-translator, cfn-lint
+git+https://github.com/zulip/libthumbor.git@60ed2431c07686a12f2770b2d852c5650f3ccfc6#egg=libthumbor==1.3.2zulip
+git+https://github.com/zulip/line_profiler.git#egg=line_profiler==2.1.2.zulip1
 lxml==4.3.4
 markdown-include==0.5.1
 markdown==3.1.1
@@ -107,7 +102,7 @@ phonenumberslite==8.10.15
 pickleshare==0.7.5        # via ipython
 pika==0.13.0
 pillow==6.1.0
-pip-tools==2.0.2
+pip-tools==3.8.0
 polib==1.1.0
 premailer==3.5.0
 prompt-toolkit==1.0.16    # via ipython
@@ -171,6 +166,7 @@ sqlalchemy==1.3.6
 sshpubkeys==3.1.0         # via moto
 statsd==3.3.0             # via django-statsd-mozilla
 stripe==2.35.0
+git+https://github.com/zulip/talon.git@7d8bdc4dbcfcc5a73298747293b99fe53da55315#egg=talon==1.2.10.zulip1
 tblib==1.4.0
 tornado==4.5.3
 traitlets==4.3.2          # via ipython
@@ -179,6 +175,7 @@ twilio==6.29.2
 twisted==19.2.1
 typed-ast==1.4.0          # via mypy
 typing-extensions==3.7.4
+git+https://github.com/zulip/ultrajson@70ac02bec#egg=ujson==1.35+git
 urllib3==1.25.3           # via botocore, requests, transifex-client
 virtualenv-clone==0.5.3
 w3lib==1.20.0             # via parsel, scrapy
@@ -189,3 +186,10 @@ wrapt==1.11.2             # via aws-xray-sdk
 xmltodict==0.12.0         # via moto
 yamole==2.1.6
 zope.interface==4.6.0     # via datetime, twisted
+git+https://github.com/zulip/zulint@aaed679f1ad38b230090eadd3870b7682500f60c#egg=zulint==0.0.1
+git+https://github.com/zulip/python-zulip-api.git@804501610b6a205334e71b4e441fca60acf650da#egg=zulip==0.6.1_git&subdirectory=zulip
+git+https://github.com/zulip/python-zulip-api.git@804501610b6a205334e71b4e441fca60acf650da#egg=zulip_bots==0.6.1+git&subdirectory=zulip_bots
+
+# The following packages are considered to be unsafe in a requirements file:
+# pip==19.2.2
+# setuptools==41.2.0        # via cfn-lint, ipython, jsonschema, markdown, pyhamcrest, sphinx, zope.interface
diff --git a/requirements/docs.txt b/requirements/docs.txt
index 5f01d33d617e4..6d955e57d8d73 100644
--- a/requirements/docs.txt
+++ b/requirements/docs.txt
@@ -13,6 +13,7 @@ certifi==2019.6.16        # via requests
 chardet==3.0.4            # via requests
 commonmark==0.9.0
 docutils==0.14            # via recommonmark, sphinx
+# future==0.17.1            # via commonmark
 idna==2.8                 # via requests
 imagesize==1.1.0          # via sphinx
 jinja2==2.10.1            # via sphinx
@@ -29,3 +30,6 @@ sphinx-rtd-theme==0.4.3
 sphinx==1.8.4
 sphinxcontrib-websupport==1.1.2  # via sphinx
 urllib3==1.25.3           # via requests
+
+# The following packages are considered to be unsafe in a requirements file:
+# setuptools==41.2.0        # via sphinx
diff --git a/requirements/pip.txt b/requirements/pip.txt
index 6c6f7dd5992e1..38d68afa0dded 100644
--- a/requirements/pip.txt
+++ b/requirements/pip.txt
@@ -1,4 +1,4 @@
 # Dependencies for setting up pip to install our requirements.txt file.
-pip==19.2.1
+pip==19.1.1
 setuptools==41.0.1
 wheel==0.33.4
diff --git a/requirements/prod.txt b/requirements/prod.txt
index 913cc7abc4108..6081e0d99f0f9 100644
--- a/requirements/prod.txt
+++ b/requirements/prod.txt
@@ -9,12 +9,6 @@
 #
 --no-binary psycopg2
 
-git+https://github.com/zulip/libthumbor.git@60ed2431c07686a12f2770b2d852c5650f3ccfc6#egg=libthumbor==1.3.2zulip
-git+https://github.com/zulip/line_profiler.git#egg=line_profiler==2.1.2.zulip1
-git+https://github.com/zulip/talon.git@7d8bdc4dbcfcc5a73298747293b99fe53da55315#egg=talon==1.2.10.zulip1
-git+https://github.com/zulip/ultrajson@70ac02bec#egg=ujson==1.35+git
-git+https://github.com/zulip/python-zulip-api.git@804501610b6a205334e71b4e441fca60acf650da#egg=zulip==0.6.1_git&subdirectory=zulip
-git+https://github.com/zulip/python-zulip-api.git@804501610b6a205334e71b4e441fca60acf650da#egg=zulip_bots==0.6.1+git&subdirectory=zulip_bots
 apns2==0.5.0
 argon2-cffi==19.1.0
 asn1crypto==0.24.0        # via cryptography
@@ -44,6 +38,7 @@ django-statsd-mozilla==0.4.0
 django-two-factor-auth==1.9.1
 django-webpack-loader==0.6.0
 django==1.11.23
+# future==0.17.1            # via python-twitter
 h2==2.6.2                 # via hyper
 hpack==3.0.0              # via h2
 html2text==2018.1.9
@@ -58,6 +53,8 @@ ipython-genutils==0.2.0   # via traitlets
 ipython==6.5.0
 jedi==0.14.0              # via ipython
 jinja2==2.10.1
+git+https://github.com/zulip/libthumbor.git@60ed2431c07686a12f2770b2d852c5650f3ccfc6#egg=libthumbor==1.3.2zulip
+git+https://github.com/zulip/line_profiler.git#egg=line_profiler==2.1.2.zulip1
 lxml==4.3.4
 markdown-include==0.5.1
 markdown==3.1.1
@@ -110,12 +107,20 @@ sourcemap==0.2.1
 sqlalchemy==1.3.6
 statsd==3.3.0             # via django-statsd-mozilla
 stripe==2.35.0
+git+https://github.com/zulip/talon.git@7d8bdc4dbcfcc5a73298747293b99fe53da55315#egg=talon==1.2.10.zulip1
 tornado==4.5.3
 traitlets==4.3.2          # via ipython
 twilio==6.29.2
 typing_extensions==3.7.4
+git+https://github.com/zulip/ultrajson@70ac02bec#egg=ujson==1.35+git
 urllib3==1.25.3           # via requests
 uwsgi==2.0.17.1
 virtualenv-clone==0.5.3
 wcwidth==0.1.7            # via prompt-toolkit
 yamole==2.1.6
+git+https://github.com/zulip/python-zulip-api.git@804501610b6a205334e71b4e441fca60acf650da#egg=zulip==0.6.1_git&subdirectory=zulip
+git+https://github.com/zulip/python-zulip-api.git@804501610b6a205334e71b4e441fca60acf650da#egg=zulip_bots==0.6.1+git&subdirectory=zulip_bots
+
+# The following packages are considered to be unsafe in a requirements file:
+# pip==19.2.2
+# setuptools==41.2.0        # via ipython, markdown
diff --git a/requirements/unupgradable.json b/requirements/unupgradable.json
index 3cc5688576ca8..bbd10d0a0d749 100644
--- a/requirements/unupgradable.json
+++ b/requirements/unupgradable.json
@@ -14,8 +14,11 @@
     "transifex-client": {
         "issue": "https://github.com/zulip/zulip/issues/8914"
     },
+    "pip": {
+        "issue": "https://github.com/zulip/zulip/issues/13067"
+    },
     "pip-tools": {
-        "issue": "https://github.com/zulip/zulip/issues/10802"
+        "issue": "https://github.com/zulip/zulip/issues/13067"
     },
     "defusedxml": {
         "issue": "https://github.com/zulip/zulip/issues/12191"
diff --git a/tools/test-locked-requirements b/tools/test-locked-requirements
index 43649564c056d..6e3cbb1093d51 100755
--- a/tools/test-locked-requirements
+++ b/tools/test-locked-requirements
@@ -31,8 +31,7 @@ def print_diff(path_file1, path_file2):
                 fromfile=path_file1,
                 tofile=path_file2,
             )
-    for line in diff:
-        print(line)
+    sys.stdout.writelines(diff)
 
 def test_locked_requirements(tmp_dir):
     # type: (str) -> bool
@@ -44,8 +43,7 @@ def test_locked_requirements(tmp_dir):
         locked_file = os.path.join(REQS_DIR, fn)
         test_locked_file = os.path.join(tmp_dir, fn)
         shutil.copyfile(locked_file, test_locked_file)
-    subprocess.check_call([os.path.join(TOOLS_DIR, 'update-locked-requirements'), '--output-dir', tmp_dir],
-                          stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+    subprocess.check_call([os.path.join(TOOLS_DIR, 'update-locked-requirements'), '--output-dir', tmp_dir])
 
     same = True
     for fn in LOCKED_REQS_FILE_NAMES:
@@ -114,7 +112,7 @@ def main():
         for fn in LOCKED_REQS_FILE_NAMES:
             locked_file = os.path.join(REQS_DIR, fn)
             test_locked_file = os.path.join(tmp_dir, fn)
-            print_diff(test_locked_file, locked_file)
+            print_diff(locked_file, test_locked_file)
         # Flush the output to ensure we print the error at the end.
         sys.stdout.flush()
         raise Exception("It looks like you have updated some python dependencies but haven't "
diff --git a/tools/update-locked-requirements b/tools/update-locked-requirements
index 24cc00829261a..2bf4034db0e5f 100755
--- a/tools/update-locked-requirements
+++ b/tools/update-locked-requirements
@@ -12,22 +12,18 @@ compile_requirements () {
     output="$2"
     python_version="$3"
 
-    pip-compile --output-file "$output" "$source"
-
-    # Remove the editable flag.  It's there because pip-compile can't
-    # yet do without it (see
-    # https://github.com/jazzband/pip-tools/issues/272 upstream), but
-    # in the output of pip-compile it's no longer needed.
-    sed -i 's/-e //' "$output"
+    pip-compile --quiet --no-header --output-file "$output" "$source"
 
     if [ "$python_version" != "py2" ]; then
-        # pip-tools bug; future, futures are obsolete in python3
-        sed -i '/futures==/d' "$output"
-        sed -i '/future==/d' "$output"
+        # Remove an unnecessary future requirement declared by commonmark,
+        # python-jose, and python-twitter.
+        # https://github.com/readthedocs/commonmark.py/pull/188
+        # https://github.com/mpdavis/python-jose/pull/134
+        # https://github.com/bear/python-twitter/pull/614
+        sed -i 's/^future==/# &/' "$output"
     fi
 
-    (
-        cat <<EOF
+    cat - "$output" <<EOF | sponge "$output"
 #
 # This file is GENERATED.  Don't edit directly.
 #
@@ -38,9 +34,9 @@ compile_requirements () {
 # For details, see requirements/README.md .
 #
 EOF
-        # This perl invocation strips the existing block of header comments.
-        perl -0pe 's/\A(^#.*\n)*//m' "$output"
-    ) | sponge "$output"
+
+    # Work around https://github.com/jazzband/pip-tools/issues/268
+    chmod a+r "$output"
 }
 
 OUTPUT_BASE_DIR='requirements'
diff --git a/version.py b/version.py
index 917b8ea611390..5c3901c63af96 100644
--- a/version.py
+++ b/version.py
@@ -26,4 +26,4 @@
 #   historical commits sharing the same major version, in which case a
 #   minor version bump suffices.
 
-PROVISION_VERSION = '49.1'
+PROVISION_VERSION = '49.2'

Clean up `update-locked-requirements` and `requirements.in` files to remove `-e` hackery.
It looks like https://github.com/jazzband/pip-tools/pull/807 was included in the latest `pip-tools` release 12 days ago.  I think this may mean we can get rid of our semantically incorrect usage of `-e` in our requirements files, which in turn may mean we can remove most of the messy code in `tools/update-locked-requirements` related to hackily removing the `-e` lines. 
 See `compile_requirements` in that file for details.  

My guess is that this means if we upgrade pip-tools, we can delete 50% of the code in `update-locked-requirements` and clean up our `requirements.in` files to not use `-e`.  

@hackerkid this might be a good project for you.
Clean up `update-locked-requirements` and `requirements.in` files to remove `-e` hackery.
It looks like https://github.com/jazzband/pip-tools/pull/807 was included in the latest `pip-tools` release 12 days ago.  I think this may mean we can get rid of our semantically incorrect usage of `-e` in our requirements files, which in turn may mean we can remove most of the messy code in `tools/update-locked-requirements` related to hackily removing the `-e` lines. 
 See `compile_requirements` in that file for details.  

My guess is that this means if we upgrade pip-tools, we can delete 50% of the code in `update-locked-requirements` and clean up our `requirements.in` files to not use `-e`.  

@hackerkid this might be a good project for you.
