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 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
|
<chapter id="misc-docs-best_practices">
<title>Best Practices</title>
<simplesect>
<para>Tips to use Subversion more effectively.</para>
<para>In this chapter, we'll focus on how to avoid some pitfalls
of version control systems in general and Subversion
specifically.</para>
</simplesect>
<!-- ================================================================= -->
<!-- ======================== SECTION 1 ============================== -->
<!-- ================================================================= -->
<sect1 id="misc-docs-best_practices-sect-1">
<title>Source Code Formatting</title>
<para>Subversion diffs and merges text files work on a
line-by-line basis. They don't understand the syntax of
programming languages or even know when you've just reflowed
text to a different line width.</para>
<para>Given this design, it's important to avoid unnecessary
reformatting. It creates unnecessary conflicts when merging
branches, updating working copies, and applying patches. It also
can drown you in noise when viewing differences between
revisions.</para>
<para>You can avoid these problems by following clearly-defined
formatting rules. The Subversion project's own
<filename>hacking.html</filename> document (<systemitem
class="url">http://svn.collab.net/repos/svn/trunk/www/hacking.html</systemitem>)
and the Code Conventions for the Java Programming Language
(<systemitem
class="url">http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html</systemitem>),
are good examples.</para>
<para>Tabs are particularly important. Some projects, like
Subversion, do not use tabs at all in the source tree. Others
always use them and define a particular tab size.</para>
<para>It can be very helpful to have an editor smart enough to
help adhere to these rules. For example, <command>vim</command>
can do this on a per-project basis with
<filename>.vimrc</filename> commands like the following:</para>
<screen>
autocmd BufRead,BufNewFile */rapidsvn/*.{cpp,h}
setlocal ts=4 noexpandtab
autocmd BufRead,BufNewFile */subversion/*.[ch]
setlocal sw=2 expandtab cinoptions=>2sn-s{s^-s:s
</screen>
<para>Check your favorite editor's documentation for more
information.</para>
<sect2 id="misc-docs-best_practices-sect-1.1">
<title>When You Have To Reformat</title>
<para>In the real world, we're not always so perfect. Formatting
preferences may change over time, or we may just make
mistakes. There are things you can do to minimize the problems
of reformatting.</para>
<para>These are good guidelines to follow:</para>
<itemizedlist>
<listitem>
<para>If you're making a sweeping reformatting change, do it
in a single commit with no semantic changes. Give precise
directions on duplicating formatting changes.</para>
</listitem>
<listitem>
<para>If you've made semantic changes to some area of code and
see inconsistent formatting in the immediate context, it's
okay to reformat. Causing conflicts is not as great a
concern because your semantic changes are likely to do that
anyway.</para>
</listitem>
</itemizedlist>
<para>Here's an example of a sweeping reformat:</para>
<screen>
$ svn co file:///repo/path/trunk indent_wc
$ indent -gnu indent_wc/src/*.[ch]
$ svn commit -m 'Ran indent -gnu src/*.[ch]' indent_wc
</screen>
<para>This follows all rules: there were no semantic changes mixed
in (no files were changed other than through
<command>indent</command>). The <command>indent</command>
commandline was given, so the changes can be very easily
duplicated. All the reformatting was done in a single
revision.</para>
<para>Let's say these changes occurred to the trunk at revision
26. The head revision is now 42. You created a branch at
revision 13 and now want to merge it back into the
trunk. Ordinarily you'd do this:</para>
<screen>
$ svn co file://repo/path/trunk merge_wc
$ svn merge -r 13:head file://repo/path/branches/mybranch merge_wc
… # resolve conflicts
$ svn commit -m 'Merged branch'
</screen>
<para>But with the reformatting changes, there will be many, many
conflicts. If you follow these rules, you can merge more
easily:</para>
<screen>
$ svn co -r 25 file://repo/path/trunk merge_wc
$ svn merge -r 13:head file://repo/path/branches/mybranch merge_wc
… # resolve conflicts
$ indent -gnu src/*.[ch]
$ svn up
… # resolve conflicts
$ svn commit -m 'Merged branch'
</screen>
<para>In English, the procedure is:</para>
<itemizedlist>
<listitem>
<para> Check out a pre-reformatting trunk working copy.</para>
</listitem>
<listitem>
<para> Merge all branch changes. Fix conflicts.</para>
</listitem>
<listitem>
<para> Reformat in the same manner.</para>
</listitem>
<listitem>
<para> Update to the head revision. Fix conflicts.</para>
</listitem>
<listitem>
<para> Check in the merged working copy.</para>
</listitem>
</itemizedlist>
</sect2>
<sect2 id="misc-docs-best_practices-sect-1.2">
<title>Ignoring Whitespace Differences</title>
<para>When viewing differences between revisions, you can
customize <command>svn diff</command> output to hide whitespace
changes. The <option>-x</option> argument passes arguments
through to GNU diff. Here are some useful arguments:</para>
<table id="misc-docs-best_practices-table-1">
<title>Some useful GNU diff arguments</title>
<tgroup cols="2">
<thead>
<row>
<entry>Option</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><option>-b</option></entry>
<entry>Ignore differences in whitespace only.</entry>
</row>
<row>
<entry><option>-B</option></entry>
<entry>Ignore added/removed blank lines.</entry>
</row>
<row>
<entry><option>-i</option></entry>
<entry>Ignore changes in case.</entry>
</row>
<row>
<entry><option>-t</option></entry>
<entry>Expand tabs to spaces to preserve
alignment.</entry>
</row>
<row>
<entry><option>-T</option></entry>
<entry>Output a tab rather than a space at the beginning
of each line to start on a tab stop.</entry>
</row>
</tbody>
</tgroup>
</table>
<para>The commit emails always show whitespace-only changes.
<filename>commit-email.pl</filename> uses <command>svnlook diff</command> to get
differences, which doesn't support the <option>-x</option>
option.</para>
</sect2>
<sect2 id="misc-docs-best_practices-sect-1.3">
<title>Line Endings</title>
<para>Different platforms (Unix, Windows, Mac OS) have different
conventions for marking the line endings of text files. Simple
editors may rewrite line endings, causing problems with diff and
merge. This is a subset of the formatting problems.</para>
<para>Subversion has built-in support for normalizing line
endings. To enable it, set the <command>svn:eol-style</command>
property to ``native''. See Properties in the Subversion
book for more information.</para>
</sect2>
</sect1>
<!-- ================================================================= -->
<!-- ======================== SECTION 2 ============================== -->
<!-- ================================================================= -->
<sect1 id="misc-docs-best_practices-sect-2">
<title>When you commit</title>
<para>It pays to take some time before you commit to review your
changes and create an appropriate log message. You are
publishing the newly changed project anew every time you
commit. This is true in two senses:</para>
<itemizedlist>
<listitem>
<para> When you commit, you are potentially destabilizing the
head revision. Many projects have a policy that the head
revision is <quote>stable</quote>—it should always
parse/compile, it should always pass unit tests, etc. If you
don't get something right, you may be inconveniencing an
arbitrary number of people until someone commits a fix.</para>
</listitem>
<listitem>
<para> You cannot easily remove revisions. (There is no
equivalent to <command>cvs admin -o</command>.) If you might
not want something to be in the repository, make sure it is
not included in your commit. Check for sensitive
information, autogenerated files, and unnecessary large
files.</para>
</listitem>
</itemizedlist>
<para>If you later don't like your log message, it is possible to
change it. The <command>svnadmin setlog</command> command will
do this locally. You can set up the script <systemitem
class="url">http://svn.collab.net/repos/svn/trunk/tools/cgi/tweak-log.cgi,tweak-log.cgi</systemitem>
to allow the same thing remotely. All the same, creating a good
log message beforehand helps clarify your thoughts and avoid
committing a mistake.</para>
<para>You should run a <command>svn diff</command> before each
commit and ask yourself:</para>
<itemizedlist>
<listitem>
<para> do these changes belong together? It's best that each
revision is a single logical change. It's very easy to
forget that you've started another change.
</para>
</listitem>
<listitem>
<para> do I have a log entry for these changes?
</para>
</listitem>
</itemizedlist>
<para>Defining a log entry policy is also helpful --- the
Subversion <filename>hacking.html</filename> document
<systemitem
class="url">http://svn.collab.net/repos/svn/trunk/www/hacking.html</systemitem>
is a good model. If you always embed filenames, function names,
etc. then you can easily search through the logs with
search-svnlog.pl <systemitem
class="url">http://svn.collab.net/repos/svn/trunk/tools/client-side/search-svnlog.pl</systemitem>.</para>
<para>You may want to write the log entry as you go. It's common
to create a file <filename>changes</filename> with your log
entry in progress. When you commit, use <command>svn ci -F
changes</command>.</para>
<para>If you do not write log entries as you go, you can generate
an initial log entry file using the output of <command>svn
status</command> which contains a list of all modified files and
directories and write a comment for each one.</para>
<sect2 id="misc-docs-best_practices-sect-2.1">
<title>Binary Files</title>
<para>Subversion does not have any way to merge or view
differences of binary files, so it's critical that these have
accurate log messages. Since you can't review your changes
with <command>svn diff</command> immediately before
committing, it's a particularly good idea to write the log
entry as you go.</para>
</sect2>
</sect1>
</chapter>
<!--
local variables:
sgml-parent-document: ("misc-docs.xml" "chapter")
end:
-->
|