We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
github_deploy broken if no remote `gh-pages` branch
If you just started out with Nikola and your remote doesn't have a `gh-pages` branch yet (which will be the case for most people), then `github_deploy` crashes with:

```
ERROR: github_deploy: Failed GitHub deployment — command ['git', 'pull', 'origin', 'gh-pages:gh-pages'] returned 1
```

Since `git pull origin gh-pages:gh-pages` won't work if there's no remote `gh-pages` branch.  This is a big problem, because creating and pushing the blank branch is possible but nontrivial, and is one of the main automation "bonuses" of having a `nikola github_deploy` in the first place.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `nikola/plugins/command/github_deploy.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 
3 # Copyright © 2014 Puneeth Chaganti and others.
4 
5 # Permission is hereby granted, free of charge, to any
6 # person obtaining a copy of this software and associated
7 # documentation files (the "Software"), to deal in the
8 # Software without restriction, including without limitation
9 # the rights to use, copy, modify, merge, publish,
10 # distribute, sublicense, and/or sell copies of the
11 # Software, and to permit persons to whom the Software is
12 # furnished to do so, subject to the following conditions:
13 #
14 # The above copyright notice and this permission notice
15 # shall be included in all copies or substantial portions of
16 # the Software.
17 #
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
19 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
20 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
21 # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
22 # OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23 # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24 # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 
27 from __future__ import print_function
28 import os
29 import shutil
30 import subprocess
31 import sys
32 from textwrap import dedent
33 
34 from nikola.plugin_categories import Command
35 from nikola.plugins.command.check import real_scan_files
36 from nikola.utils import ask_yesno, get_logger
37 from nikola.__main__ import main
38 from nikola import __version__
39 
40 
41 def uni_check_output(*args, **kwargs):
42     o = subprocess.check_output(*args, **kwargs)
43     return o.decode('utf-8')
44 
45 
46 class CommandGitHubDeploy(Command):
47     """ Deploy site to GitHub pages. """
48     name = 'github_deploy'
49 
50     doc_usage = ''
51     doc_purpose = 'deploy the site to GitHub pages'
52     doc_description = dedent(
53         """\
54         This command can be used to deploy your site to GitHub pages.
55         It performs the following actions:
56 
57         1. Ensure that your site is a git repository, and git is on the PATH.
58         2. Ensure that the output directory is not committed on the
59            source branch.
60         3. Check for changes, and prompt the user to continue, if required.
61         4. Build the site
62         5. Clean any files that are "unknown" to Nikola.
63         6. Create a deploy branch, if one doesn't exist.
64         7. Commit the output to this branch.  (NOTE: Any untracked source
65            files, may get committed at this stage, on the wrong branch!)
66         8. Push and deploy!
67 
68         NOTE: This command needs your site to be a git repository, with a
69         master branch (or a different branch, configured using
70         GITHUB_SOURCE_BRANCH if you are pushing to user.github
71         .io/organization.github.io pages) containing the sources of your
72         site.  You also, obviously, need to have `git` on your PATH,
73         and should be able to push to the repository specified as the remote
74         (origin, by default).
75         """
76     )
77 
78     logger = None
79 
80     _deploy_branch = ''
81     _source_branch = ''
82     _remote_name = ''
83 
84     def _execute(self, command, args):
85 
86         self.logger = get_logger(
87             CommandGitHubDeploy.name, self.site.loghandlers
88         )
89         self._source_branch = self.site.config.get(
90             'GITHUB_SOURCE_BRANCH', 'master'
91         )
92         self._deploy_branch = self.site.config.get(
93             'GITHUB_DEPLOY_BRANCH', 'gh-pages'
94         )
95         self._remote_name = self.site.config.get(
96             'GITHUB_REMOTE_NAME', 'origin'
97         )
98 
99         self._ensure_git_repo()
100 
101         self._exit_if_output_committed()
102 
103         if not self._prompt_continue():
104             return
105 
106         build = main(['build'])
107         if build != 0:
108             self.logger.error('Build failed, not deploying to GitHub')
109             sys.exit(build)
110 
111         only_on_output, _ = real_scan_files(self.site)
112         for f in only_on_output:
113             os.unlink(f)
114 
115         self._checkout_deploy_branch()
116 
117         self._copy_output()
118 
119         self._commit_and_push()
120 
121         return
122 
123     def _commit_and_push(self):
124         """ Commit all the files and push. """
125 
126         deploy = self._deploy_branch
127         source = self._source_branch
128         remote = self._remote_name
129 
130         source_commit = uni_check_output(['git', 'rev-parse', source])
131         commit_message = (
132             'Nikola auto commit.\n\n'
133             'Source commit: %s'
134             'Nikola version: %s' % (source_commit, __version__)
135         )
136 
137         commands = [
138             ['git', 'pull', '--rebase=false', remote, '%s:%s' % (deploy, deploy)],
139             ['git', 'add', '-A'],
140             ['git', 'commit', '-m', commit_message],
141             ['git', 'push', '--force', remote, '%s:%s' % (deploy, deploy)],
142             ['git', 'checkout', source],
143         ]
144 
145         for command in commands:
146             self.logger.info("==> {0}".format(command))
147             try:
148                 subprocess.check_call(command)
149             except subprocess.CalledProcessError as e:
150                 self.logger.error(
151                     'Failed GitHub deployment — command {0} '
152                     'returned {1}'.format(e.cmd, e.returncode)
153                 )
154                 sys.exit(e.returncode)
155 
156     def _copy_output(self):
157         """ Copy all output to the top level directory. """
158         output_folder = self.site.config['OUTPUT_FOLDER']
159         for each in os.listdir(output_folder):
160             if os.path.exists(each):
161                 if os.path.isdir(each):
162                     shutil.rmtree(each)
163 
164                 else:
165                     os.unlink(each)
166 
167             shutil.move(os.path.join(output_folder, each), '.')
168 
169     def _checkout_deploy_branch(self):
170         """ Check out the deploy branch
171 
172         Creates an orphan branch if not present.
173 
174         """
175 
176         deploy = self._deploy_branch
177 
178         try:
179             subprocess.check_call(
180                 [
181                     'git', 'show-ref', '--verify', '--quiet',
182                     'refs/heads/%s' % deploy
183                 ]
184             )
185         except subprocess.CalledProcessError:
186             self._create_orphan_deploy_branch()
187         else:
188             subprocess.check_call(['git', 'checkout', deploy])
189 
190     def _create_orphan_deploy_branch(self):
191         """ Create an orphan deploy branch """
192 
193         result = subprocess.check_call(
194             ['git', 'checkout', '--orphan', self._deploy_branch]
195         )
196         if result != 0:
197             self.logger.error('Failed to create a deploy branch')
198             sys.exit(1)
199 
200         result = subprocess.check_call(['git', 'rm', '-rf', '.'])
201         if result != 0:
202             self.logger.error('Failed to create a deploy branch')
203             sys.exit(1)
204 
205         with open('.gitignore', 'w') as f:
206             f.write('%s\n' % self.site.config['OUTPUT_FOLDER'])
207             f.write('%s\n' % self.site.config['CACHE_FOLDER'])
208             f.write('*.pyc\n')
209             f.write('*.db\n')
210 
211         subprocess.check_call(['git', 'add', '.gitignore'])
212         subprocess.check_call(['git', 'commit', '-m', 'Add .gitignore'])
213 
214     def _ensure_git_repo(self):
215         """ Ensure that the site is a git-repo.
216 
217         Also make sure that a remote with the specified name exists.
218 
219         """
220 
221         try:
222             remotes = uni_check_output(['git', 'remote'])
223         except subprocess.CalledProcessError as e:
224             self.logger.notice('github_deploy needs a git repository!')
225             sys.exit(e.returncode)
226         except OSError as e:
227             import errno
228             self.logger.error('Running git failed with {0}'.format(e))
229             if e.errno == errno.ENOENT:
230                 self.logger.notice('Is git on the PATH?')
231             sys.exit(1)
232         else:
233             if self._remote_name not in remotes:
234                 self.logger.error(
235                     'Need a remote called "%s" configured' % self._remote_name
236                 )
237                 sys.exit(1)
238 
239     def _exit_if_output_committed(self):
240         """ Exit if the output folder is committed on the source branch. """
241 
242         source = self._source_branch
243         subprocess.check_call(['git', 'checkout', source])
244 
245         output_folder = self.site.config['OUTPUT_FOLDER']
246         output_log = uni_check_output(
247             ['git', 'ls-files', '--', output_folder]
248         )
249 
250         if len(output_log.strip()) > 0:
251             self.logger.error(
252                 'Output folder is committed on the source branch. '
253                 'Cannot proceed until it is removed.'
254             )
255             sys.exit(1)
256 
257     def _prompt_continue(self):
258         """ Show uncommitted changes, and ask if user wants to continue. """
259 
260         changes = uni_check_output(['git', 'status', '--porcelain'])
261         if changes.strip():
262             changes = uni_check_output(['git', 'status']).strip()
263             message = (
264                 "You have the following changes:\n%s\n\n"
265                 "Anything not committed, and unknown to Nikola may be lost, "
266                 "or committed onto the wrong branch. Do you wish to continue?"
267             ) % changes
268             proceed = ask_yesno(message, False)
269         else:
270             proceed = True
271 
272         return proceed
273
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/nikola/plugins/command/github_deploy.py b/nikola/plugins/command/github_deploy.py
--- a/nikola/plugins/command/github_deploy.py
+++ b/nikola/plugins/command/github_deploy.py
@@ -95,6 +95,9 @@
         self._remote_name = self.site.config.get(
             'GITHUB_REMOTE_NAME', 'origin'
         )
+        self._pull_before_commit = self.site.config.get(
+            'GITHUB_PULL_BEFORE_COMMIT', False
+        )
 
         self._ensure_git_repo()
 
@@ -135,13 +138,15 @@
         )
 
         commands = [
-            ['git', 'pull', '--rebase=false', remote, '%s:%s' % (deploy, deploy)],
             ['git', 'add', '-A'],
             ['git', 'commit', '-m', commit_message],
             ['git', 'push', '--force', remote, '%s:%s' % (deploy, deploy)],
             ['git', 'checkout', source],
         ]
 
+        if self._pull_before_commit:
+            commands.insert(0, ['git', 'pull', '--rebase=false', remote, '%s:%s' % (deploy, deploy)])
+
         for command in commands:
             self.logger.info("==> {0}".format(command))
             try:

{"golden_diff": "diff --git a/nikola/plugins/command/github_deploy.py b/nikola/plugins/command/github_deploy.py\n--- a/nikola/plugins/command/github_deploy.py\n+++ b/nikola/plugins/command/github_deploy.py\n@@ -95,6 +95,9 @@\n         self._remote_name = self.site.config.get(\n             'GITHUB_REMOTE_NAME', 'origin'\n         )\n+        self._pull_before_commit = self.site.config.get(\n+            'GITHUB_PULL_BEFORE_COMMIT', False\n+        )\n \n         self._ensure_git_repo()\n \n@@ -135,13 +138,15 @@\n         )\n \n         commands = [\n-            ['git', 'pull', '--rebase=false', remote, '%s:%s' % (deploy, deploy)],\n             ['git', 'add', '-A'],\n             ['git', 'commit', '-m', commit_message],\n             ['git', 'push', '--force', remote, '%s:%s' % (deploy, deploy)],\n             ['git', 'checkout', source],\n         ]\n \n+        if self._pull_before_commit:\n+            commands.insert(0, ['git', 'pull', '--rebase=false', remote, '%s:%s' % (deploy, deploy)])\n+\n         for command in commands:\n             self.logger.info(\"==> {0}\".format(command))\n             try:\n", "issue": "github_deploy broken if no remote `gh-pages` branch\nIf you just started out with Nikola and your remote doesn't have a `gh-pages` branch yet (which will be the case for most people), then `github_deploy` crashes with:\n\n```\nERROR: github_deploy: Failed GitHub deployment \u2014 command ['git', 'pull', 'origin', 'gh-pages:gh-pages'] returned 1\n```\n\nSince `git pull origin gh-pages:gh-pages` won't work if there's no remote `gh-pages` branch.  This is a big problem, because creating and pushing the blank branch is possible but nontrivial, and is one of the main automation \"bonuses\" of having a `nikola github_deploy` in the first place.\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\n# Copyright \u00a9 2014 Puneeth Chaganti and others.\n\n# Permission is hereby granted, free of charge, to any\n# person obtaining a copy of this software and associated\n# documentation files (the \"Software\"), to deal in the\n# Software without restriction, including without limitation\n# the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the\n# Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice\n# shall be included in all copies or substantial portions of\n# the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\n# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\n# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\n# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\n# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nfrom __future__ import print_function\nimport os\nimport shutil\nimport subprocess\nimport sys\nfrom textwrap import dedent\n\nfrom nikola.plugin_categories import Command\nfrom nikola.plugins.command.check import real_scan_files\nfrom nikola.utils import ask_yesno, get_logger\nfrom nikola.__main__ import main\nfrom nikola import __version__\n\n\ndef uni_check_output(*args, **kwargs):\n    o = subprocess.check_output(*args, **kwargs)\n    return o.decode('utf-8')\n\n\nclass CommandGitHubDeploy(Command):\n    \"\"\" Deploy site to GitHub pages. \"\"\"\n    name = 'github_deploy'\n\n    doc_usage = ''\n    doc_purpose = 'deploy the site to GitHub pages'\n    doc_description = dedent(\n        \"\"\"\\\n        This command can be used to deploy your site to GitHub pages.\n        It performs the following actions:\n\n        1. Ensure that your site is a git repository, and git is on the PATH.\n        2. Ensure that the output directory is not committed on the\n           source branch.\n        3. Check for changes, and prompt the user to continue, if required.\n        4. Build the site\n        5. Clean any files that are \"unknown\" to Nikola.\n        6. Create a deploy branch, if one doesn't exist.\n        7. Commit the output to this branch.  (NOTE: Any untracked source\n           files, may get committed at this stage, on the wrong branch!)\n        8. Push and deploy!\n\n        NOTE: This command needs your site to be a git repository, with a\n        master branch (or a different branch, configured using\n        GITHUB_SOURCE_BRANCH if you are pushing to user.github\n        .io/organization.github.io pages) containing the sources of your\n        site.  You also, obviously, need to have `git` on your PATH,\n        and should be able to push to the repository specified as the remote\n        (origin, by default).\n        \"\"\"\n    )\n\n    logger = None\n\n    _deploy_branch = ''\n    _source_branch = ''\n    _remote_name = ''\n\n    def _execute(self, command, args):\n\n        self.logger = get_logger(\n            CommandGitHubDeploy.name, self.site.loghandlers\n        )\n        self._source_branch = self.site.config.get(\n            'GITHUB_SOURCE_BRANCH', 'master'\n        )\n        self._deploy_branch = self.site.config.get(\n            'GITHUB_DEPLOY_BRANCH', 'gh-pages'\n        )\n        self._remote_name = self.site.config.get(\n            'GITHUB_REMOTE_NAME', 'origin'\n        )\n\n        self._ensure_git_repo()\n\n        self._exit_if_output_committed()\n\n        if not self._prompt_continue():\n            return\n\n        build = main(['build'])\n        if build != 0:\n            self.logger.error('Build failed, not deploying to GitHub')\n            sys.exit(build)\n\n        only_on_output, _ = real_scan_files(self.site)\n        for f in only_on_output:\n            os.unlink(f)\n\n        self._checkout_deploy_branch()\n\n        self._copy_output()\n\n        self._commit_and_push()\n\n        return\n\n    def _commit_and_push(self):\n        \"\"\" Commit all the files and push. \"\"\"\n\n        deploy = self._deploy_branch\n        source = self._source_branch\n        remote = self._remote_name\n\n        source_commit = uni_check_output(['git', 'rev-parse', source])\n        commit_message = (\n            'Nikola auto commit.\\n\\n'\n            'Source commit: %s'\n            'Nikola version: %s' % (source_commit, __version__)\n        )\n\n        commands = [\n            ['git', 'pull', '--rebase=false', remote, '%s:%s' % (deploy, deploy)],\n            ['git', 'add', '-A'],\n            ['git', 'commit', '-m', commit_message],\n            ['git', 'push', '--force', remote, '%s:%s' % (deploy, deploy)],\n            ['git', 'checkout', source],\n        ]\n\n        for command in commands:\n            self.logger.info(\"==> {0}\".format(command))\n            try:\n                subprocess.check_call(command)\n            except subprocess.CalledProcessError as e:\n                self.logger.error(\n                    'Failed GitHub deployment \u2014 command {0} '\n                    'returned {1}'.format(e.cmd, e.returncode)\n                )\n                sys.exit(e.returncode)\n\n    def _copy_output(self):\n        \"\"\" Copy all output to the top level directory. \"\"\"\n        output_folder = self.site.config['OUTPUT_FOLDER']\n        for each in os.listdir(output_folder):\n            if os.path.exists(each):\n                if os.path.isdir(each):\n                    shutil.rmtree(each)\n\n                else:\n                    os.unlink(each)\n\n            shutil.move(os.path.join(output_folder, each), '.')\n\n    def _checkout_deploy_branch(self):\n        \"\"\" Check out the deploy branch\n\n        Creates an orphan branch if not present.\n\n        \"\"\"\n\n        deploy = self._deploy_branch\n\n        try:\n            subprocess.check_call(\n                [\n                    'git', 'show-ref', '--verify', '--quiet',\n                    'refs/heads/%s' % deploy\n                ]\n            )\n        except subprocess.CalledProcessError:\n            self._create_orphan_deploy_branch()\n        else:\n            subprocess.check_call(['git', 'checkout', deploy])\n\n    def _create_orphan_deploy_branch(self):\n        \"\"\" Create an orphan deploy branch \"\"\"\n\n        result = subprocess.check_call(\n            ['git', 'checkout', '--orphan', self._deploy_branch]\n        )\n        if result != 0:\n            self.logger.error('Failed to create a deploy branch')\n            sys.exit(1)\n\n        result = subprocess.check_call(['git', 'rm', '-rf', '.'])\n        if result != 0:\n            self.logger.error('Failed to create a deploy branch')\n            sys.exit(1)\n\n        with open('.gitignore', 'w') as f:\n            f.write('%s\\n' % self.site.config['OUTPUT_FOLDER'])\n            f.write('%s\\n' % self.site.config['CACHE_FOLDER'])\n            f.write('*.pyc\\n')\n            f.write('*.db\\n')\n\n        subprocess.check_call(['git', 'add', '.gitignore'])\n        subprocess.check_call(['git', 'commit', '-m', 'Add .gitignore'])\n\n    def _ensure_git_repo(self):\n        \"\"\" Ensure that the site is a git-repo.\n\n        Also make sure that a remote with the specified name exists.\n\n        \"\"\"\n\n        try:\n            remotes = uni_check_output(['git', 'remote'])\n        except subprocess.CalledProcessError as e:\n            self.logger.notice('github_deploy needs a git repository!')\n            sys.exit(e.returncode)\n        except OSError as e:\n            import errno\n            self.logger.error('Running git failed with {0}'.format(e))\n            if e.errno == errno.ENOENT:\n                self.logger.notice('Is git on the PATH?')\n            sys.exit(1)\n        else:\n            if self._remote_name not in remotes:\n                self.logger.error(\n                    'Need a remote called \"%s\" configured' % self._remote_name\n                )\n                sys.exit(1)\n\n    def _exit_if_output_committed(self):\n        \"\"\" Exit if the output folder is committed on the source branch. \"\"\"\n\n        source = self._source_branch\n        subprocess.check_call(['git', 'checkout', source])\n\n        output_folder = self.site.config['OUTPUT_FOLDER']\n        output_log = uni_check_output(\n            ['git', 'ls-files', '--', output_folder]\n        )\n\n        if len(output_log.strip()) > 0:\n            self.logger.error(\n                'Output folder is committed on the source branch. '\n                'Cannot proceed until it is removed.'\n            )\n            sys.exit(1)\n\n    def _prompt_continue(self):\n        \"\"\" Show uncommitted changes, and ask if user wants to continue. \"\"\"\n\n        changes = uni_check_output(['git', 'status', '--porcelain'])\n        if changes.strip():\n            changes = uni_check_output(['git', 'status']).strip()\n            message = (\n                \"You have the following changes:\\n%s\\n\\n\"\n                \"Anything not committed, and unknown to Nikola may be lost, \"\n                \"or committed onto the wrong branch. Do you wish to continue?\"\n            ) % changes\n            proceed = ask_yesno(message, False)\n        else:\n            proceed = True\n\n        return proceed\n", "path": "nikola/plugins/command/github_deploy.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\n# Copyright \u00a9 2014 Puneeth Chaganti and others.\n\n# Permission is hereby granted, free of charge, to any\n# person obtaining a copy of this software and associated\n# documentation files (the \"Software\"), to deal in the\n# Software without restriction, including without limitation\n# the rights to use, copy, modify, merge, publish,\n# distribute, sublicense, and/or sell copies of the\n# Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice\n# shall be included in all copies or substantial portions of\n# the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY\n# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\n# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\n# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\n# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nfrom __future__ import print_function\nimport os\nimport shutil\nimport subprocess\nimport sys\nfrom textwrap import dedent\n\nfrom nikola.plugin_categories import Command\nfrom nikola.plugins.command.check import real_scan_files\nfrom nikola.utils import ask_yesno, get_logger\nfrom nikola.__main__ import main\nfrom nikola import __version__\n\n\ndef uni_check_output(*args, **kwargs):\n    o = subprocess.check_output(*args, **kwargs)\n    return o.decode('utf-8')\n\n\nclass CommandGitHubDeploy(Command):\n    \"\"\" Deploy site to GitHub pages. \"\"\"\n    name = 'github_deploy'\n\n    doc_usage = ''\n    doc_purpose = 'deploy the site to GitHub pages'\n    doc_description = dedent(\n        \"\"\"\\\n        This command can be used to deploy your site to GitHub pages.\n        It performs the following actions:\n\n        1. Ensure that your site is a git repository, and git is on the PATH.\n        2. Ensure that the output directory is not committed on the\n           source branch.\n        3. Check for changes, and prompt the user to continue, if required.\n        4. Build the site\n        5. Clean any files that are \"unknown\" to Nikola.\n        6. Create a deploy branch, if one doesn't exist.\n        7. Commit the output to this branch.  (NOTE: Any untracked source\n           files, may get committed at this stage, on the wrong branch!)\n        8. Push and deploy!\n\n        NOTE: This command needs your site to be a git repository, with a\n        master branch (or a different branch, configured using\n        GITHUB_SOURCE_BRANCH if you are pushing to user.github\n        .io/organization.github.io pages) containing the sources of your\n        site.  You also, obviously, need to have `git` on your PATH,\n        and should be able to push to the repository specified as the remote\n        (origin, by default).\n        \"\"\"\n    )\n\n    logger = None\n\n    _deploy_branch = ''\n    _source_branch = ''\n    _remote_name = ''\n\n    def _execute(self, command, args):\n\n        self.logger = get_logger(\n            CommandGitHubDeploy.name, self.site.loghandlers\n        )\n        self._source_branch = self.site.config.get(\n            'GITHUB_SOURCE_BRANCH', 'master'\n        )\n        self._deploy_branch = self.site.config.get(\n            'GITHUB_DEPLOY_BRANCH', 'gh-pages'\n        )\n        self._remote_name = self.site.config.get(\n            'GITHUB_REMOTE_NAME', 'origin'\n        )\n        self._pull_before_commit = self.site.config.get(\n            'GITHUB_PULL_BEFORE_COMMIT', False\n        )\n\n        self._ensure_git_repo()\n\n        self._exit_if_output_committed()\n\n        if not self._prompt_continue():\n            return\n\n        build = main(['build'])\n        if build != 0:\n            self.logger.error('Build failed, not deploying to GitHub')\n            sys.exit(build)\n\n        only_on_output, _ = real_scan_files(self.site)\n        for f in only_on_output:\n            os.unlink(f)\n\n        self._checkout_deploy_branch()\n\n        self._copy_output()\n\n        self._commit_and_push()\n\n        return\n\n    def _commit_and_push(self):\n        \"\"\" Commit all the files and push. \"\"\"\n\n        deploy = self._deploy_branch\n        source = self._source_branch\n        remote = self._remote_name\n\n        source_commit = uni_check_output(['git', 'rev-parse', source])\n        commit_message = (\n            'Nikola auto commit.\\n\\n'\n            'Source commit: %s'\n            'Nikola version: %s' % (source_commit, __version__)\n        )\n\n        commands = [\n            ['git', 'add', '-A'],\n            ['git', 'commit', '-m', commit_message],\n            ['git', 'push', '--force', remote, '%s:%s' % (deploy, deploy)],\n            ['git', 'checkout', source],\n        ]\n\n        if self._pull_before_commit:\n            commands.insert(0, ['git', 'pull', '--rebase=false', remote, '%s:%s' % (deploy, deploy)])\n\n        for command in commands:\n            self.logger.info(\"==> {0}\".format(command))\n            try:\n                subprocess.check_call(command)\n            except subprocess.CalledProcessError as e:\n                self.logger.error(\n                    'Failed GitHub deployment \u2014 command {0} '\n                    'returned {1}'.format(e.cmd, e.returncode)\n                )\n                sys.exit(e.returncode)\n\n    def _copy_output(self):\n        \"\"\" Copy all output to the top level directory. \"\"\"\n        output_folder = self.site.config['OUTPUT_FOLDER']\n        for each in os.listdir(output_folder):\n            if os.path.exists(each):\n                if os.path.isdir(each):\n                    shutil.rmtree(each)\n\n                else:\n                    os.unlink(each)\n\n            shutil.move(os.path.join(output_folder, each), '.')\n\n    def _checkout_deploy_branch(self):\n        \"\"\" Check out the deploy branch\n\n        Creates an orphan branch if not present.\n\n        \"\"\"\n\n        deploy = self._deploy_branch\n\n        try:\n            subprocess.check_call(\n                [\n                    'git', 'show-ref', '--verify', '--quiet',\n                    'refs/heads/%s' % deploy\n                ]\n            )\n        except subprocess.CalledProcessError:\n            self._create_orphan_deploy_branch()\n        else:\n            subprocess.check_call(['git', 'checkout', deploy])\n\n    def _create_orphan_deploy_branch(self):\n        \"\"\" Create an orphan deploy branch \"\"\"\n\n        result = subprocess.check_call(\n            ['git', 'checkout', '--orphan', self._deploy_branch]\n        )\n        if result != 0:\n            self.logger.error('Failed to create a deploy branch')\n            sys.exit(1)\n\n        result = subprocess.check_call(['git', 'rm', '-rf', '.'])\n        if result != 0:\n            self.logger.error('Failed to create a deploy branch')\n            sys.exit(1)\n\n        with open('.gitignore', 'w') as f:\n            f.write('%s\\n' % self.site.config['OUTPUT_FOLDER'])\n            f.write('%s\\n' % self.site.config['CACHE_FOLDER'])\n            f.write('*.pyc\\n')\n            f.write('*.db\\n')\n\n        subprocess.check_call(['git', 'add', '.gitignore'])\n        subprocess.check_call(['git', 'commit', '-m', 'Add .gitignore'])\n\n    def _ensure_git_repo(self):\n        \"\"\" Ensure that the site is a git-repo.\n\n        Also make sure that a remote with the specified name exists.\n\n        \"\"\"\n\n        try:\n            remotes = uni_check_output(['git', 'remote'])\n        except subprocess.CalledProcessError as e:\n            self.logger.notice('github_deploy needs a git repository!')\n            sys.exit(e.returncode)\n        except OSError as e:\n            import errno\n            self.logger.error('Running git failed with {0}'.format(e))\n            if e.errno == errno.ENOENT:\n                self.logger.notice('Is git on the PATH?')\n            sys.exit(1)\n        else:\n            if self._remote_name not in remotes:\n                self.logger.error(\n                    'Need a remote called \"%s\" configured' % self._remote_name\n                )\n                sys.exit(1)\n\n    def _exit_if_output_committed(self):\n        \"\"\" Exit if the output folder is committed on the source branch. \"\"\"\n\n        source = self._source_branch\n        subprocess.check_call(['git', 'checkout', source])\n\n        output_folder = self.site.config['OUTPUT_FOLDER']\n        output_log = uni_check_output(\n            ['git', 'ls-files', '--', output_folder]\n        )\n\n        if len(output_log.strip()) > 0:\n            self.logger.error(\n                'Output folder is committed on the source branch. '\n                'Cannot proceed until it is removed.'\n            )\n            sys.exit(1)\n\n    def _prompt_continue(self):\n        \"\"\" Show uncommitted changes, and ask if user wants to continue. \"\"\"\n\n        changes = uni_check_output(['git', 'status', '--porcelain'])\n        if changes.strip():\n            changes = uni_check_output(['git', 'status']).strip()\n            message = (\n                \"You have the following changes:\\n%s\\n\\n\"\n                \"Anything not committed, and unknown to Nikola may be lost, \"\n                \"or committed onto the wrong branch. Do you wish to continue?\"\n            ) % changes\n            proceed = ask_yesno(message, False)\n        else:\n            proceed = True\n\n        return proceed\n", "path": "nikola/plugins/command/github_deploy.py"}]}