1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
|
<!DOCTYPE html>
<html>
<head>
<title>
The Codex »
Stop Using Git Pull To Deploy
</title>
<link
rel='stylesheet'
type='text/css'
href='http://fonts.googleapis.com/css?family=Buenard:400,700&subset=latin,latin-ext'>
<link
rel="stylesheet"
type="text/css"
href="../media/css/reset.css">
<link
rel="stylesheet"
type="text/css"
href="../media/css/grimoire.css">
</head>
<body>
<div id="shell">
<ol id="breadcrumbs">
<li class="crumb-0 not-last">
<a href="../">index</a>
</li>
<li class="crumb-1 not-last">
<a href="./">git</a>
</li>
<li class="crumb-2 last">
stop-using-git-pull-to-deploy
</li>
</ol>
<div id="article">
<h1 id="stop-using-git-pull-for-deployment">Stop using <code>git pull</code> for deployment!</h1>
<h2 id="the-problem">The problem</h2>
<ul>
<li>You have a Git repository containing your project.</li>
<li>You want to “deploy” that code when it changes.</li>
<li>You'd rather not download the entire project from scratch for each
deployment.</li>
</ul>
<h2 id="the-antipattern">The antipattern</h2>
<p>“I know, I'll use <code>git pull</code> in my deployment script!”</p>
<p>Stop doing this. Stop teaching other people to do this. It's wrong, and it
will eventually lead to deploying something you didn't want.</p>
<p>Deployment should be based on predictable, known versions of your code.
Ideally, every deployable version has a tag (and you deploy exactly that tag),
but even less formal processes, where you deploy a branch tip, should still be
deploying exactly the code designated for release. <code>git pull</code>, however, can
introduce new commits.</p>
<p><code>git pull</code> is a two-step process:</p>
<ol>
<li>Fetch the current branch's designated upstream remote, to obtain all of the
remote's new commits.</li>
<li>Merge the current branch's designated upstream branch into the current
branch.</li>
</ol>
<p>The merge commit means the actual deployed tree might <em>not</em> be identical to
the intended deployment tree. Local changes (intentional or otherwise) will be
preserved (and merged) into the deployment, for example; once this happens,
the actual deployed commit will <em>never</em> match the intended commit.</p>
<p><code>git pull</code> will approximate the right thing “by accident”: if the current
local branch (generally <code>master</code>) for people using <code>git pull</code> is always clean,
and always tracks the desired deployment branch, then <code>git pull</code> will update
to the intended commit exactly. This is pretty fragile, though; many git
commands can cause the local branch to diverge from its upstream branch, and
once that happens, <code>git pull</code> will always create new commits. You can patch
around the fragility a bit using the <code>--ff-only</code> option, but that only tells
you when your deployment environment has diverged and doesn't fix it.</p>
<h2 id="the-right-pattern">The right pattern</h2>
<p>Quoting <a href="http://gitolite.com/the-list-and-irc/deploy.html">Sitaram Chamarty</a>:</p>
<blockquote>
<p>Here's what we expect from a deployment tool. Note the rule numbers --
we'll be referring to some of them simply by number later.</p>
<ol>
<li>
<p>All files in the branch being deployed should be copied to the
deployment directory.</p>
</li>
<li>
<p>Files that were deleted in the git repo since the last deployment
should get deleted from the deployment directory.</p>
</li>
<li>
<p>Any changes to tracked files in the deployment directory after the
last deployment should be ignored when following rules 1 and 2.</p>
<p>However, sometimes you might want to detect such changes and abort if
you found any.</p>
</li>
<li>
<p>Untracked files in the deploy directory should be left alone.</p>
<p>Again, some people might want to detect this and abort the deployment.</p>
</li>
</ol>
</blockquote>
<p>Sitaram's own documentation talks about how to accomplish these when
“deploying” straight out of a bare repository. That's unwise (not to mention
impractical) in most cases; deployment should use a dedicated clone of the
canonical repository.</p>
<p>I also disagree with point 3, preferring to keep deployment-related changes
outside of tracked files. This makes it much easier to argue that the changes
introduced to configure the project for deployment do not introduce new bugs
or other surprise features.</p>
<p>My deployment process, given a dedicated clone at <code>$DEPLOY_TREE</code>, is as
follows:</p>
<pre><code>cd "${DEPLOY_TREE}"
git fetch --all
git checkout --force "${TARGET}"
# Following two lines only required if you use submodules
git submodule sync
git submodule update --init --recursive
# Follow with actual deployment steps (run fabric/capistrano/make/etc)
</code></pre>
<p><code>$TARGET</code> is either a tag name (<code>v1.2.1</code>) or a remote branch name
(<code>origin/master</code>), but could also be a commit hash or anything else Git
recognizes as a revision. This will detach the head of the <code>$DEPLOY_TREE</code>
repository, which is fine as no new changes should be authored in this
repository (so the local branches are irrelevant). The warning Git emits when
<code>HEAD</code> becomes detached is unimportant in this case.</p>
<p>The tracked contents of <code>$DEPLOY_TREE</code> will end up identical to the desired
commit, discarding local changes. The pattern above is very similar to what
most continuous integration servers use when building from Git repositories,
for much the same reason.</p>
</div>
<div id="comments">
<div id="disqus_thread"></div>
<script type="text/javascript">
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
var disqus_shortname = 'grimoire'; // required: replace example with your forum shortname
/* * * DON'T EDIT BELOW THIS LINE * * */
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
<a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
</div>
<div id="footer">
<p>
The Codex —
Powered by <a href="http://markdoc.org/">Markdoc</a>.
<a href="https://bitbucket.org/ojacobson/grimoire.ca/src/master/wiki/git/stop-using-git-pull-to-deploy.md">See this page on Bitbucket</a> (<a href="https://bitbucket.org/ojacobson/grimoire.ca/history-node/master/wiki/git/stop-using-git-pull-to-deploy.md">history</a>).
</p>
</div>
</div>
</body>
</html>
|