UNPKG

epubjs

Version:

Render ePub documents in the browser, across many devices

358 lines (234 loc) 33.4 kB
<?xml version='1.0' encoding='utf-8'?> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Pro Git - professional version control</title> <meta content="http://www.w3.org/1999/xhtml; charset=utf-8" http-equiv="Content-Type"/> <link href="stylesheet.css" type="text/css" rel="stylesheet"/> <style type="text/css"> @page { margin-bottom: 5.000000pt; margin-top: 5.000000pt; }</style> </head> <body class="calibre"> <h2 class="calibre4" id="calibre_pb_45">Maintaining a Project</h2> <p class="calibre3">In addition to knowing how to effectively contribute to a project, you'll likely need to know how to maintain one. This can consist of accepting and applying patches generated via <code class="calibre10">format-patch</code> and e-mailed to you, or integrating changes in remote branches for repositories you've added as remotes to your project. Whether you maintain a canonical repository or want to help by verifying or approving patches, you need to know how to accept work in a way that is clearest for other contributors and sustainable by you over the long run.</p> <h3 class="calibre5">Working in Topic Branches</h3> <p class="calibre3">When you're thinking of integrating new work, it's generally a good idea to try it out in a topic branch - a temporary branch specifically made to try out that new work. This way, it's easy to tweak a patch individually and leave it if it's not working until you have time to come back to it. If you create a simple branch name based on the theme of the work you're going to try, such as <code class="calibre10">ruby_client</code> or something similarly descriptive, you can easily remember it if you have to abandon it for a while and come back later. The maintainer of the Git project tends to namespace these branches as well - such as <code class="calibre10">sc/ruby_client</code>, where <code class="calibre10">sc</code> is short for the person who contributed the work. As you'll remember, you can create the branch based off your master branch like this:</p> <pre class="calibre9"><code class="calibre10">$ git branch sc/ruby_client master </code></pre> <p class="calibre3">Or, if you want to also switch to it immediately, you can use the <code class="calibre10">checkout -b</code> option:</p> <pre class="calibre9"><code class="calibre10">$ git checkout -b sc/ruby_client master </code></pre> <p class="calibre3">Now you're ready to add your contributed work into this topic branch and determine if you want to merge it into your longer-term branches.</p> <h3 class="calibre5">Applying Patches from E-mail</h3> <p class="calibre3">If you receive a patch over e-mail that you need to integrate into your project, you need to apply the patch in your topic branch to evaluate it. There are two ways to apply an e-mailed patch: with <code class="calibre10">git apply</code> or with <code class="calibre10">git am</code>.</p> <h4 class="calibre14">Applying a Patch with apply</h4> <p class="calibre3">If you received the patch from someone who generated it with the <code class="calibre10">git diff</code> or a Unix <code class="calibre10">diff</code> command, you can apply it with the <code class="calibre10">git apply</code> command. Assuming you saved the patch at <code class="calibre10">/tmp/patch-ruby-client.patch</code>, you can apply the patch like this:</p> <pre class="calibre9"><code class="calibre10">$ git apply /tmp/patch-ruby-client.patch </code></pre> <p class="calibre3">This modifies the files in your working directory. It's almost identical to running a <code class="calibre10">patch -p1</code> command to apply the patch, although it's more paranoid and accepts fewer fuzzy matches than patch. It also handles file adds, deletes, and renames if they're described in the <code class="calibre10">git diff</code> format, which <code class="calibre10">patch</code> won't do. Finally, <code class="calibre10">git apply</code> is an "apply all or abort all" model where either everything is applied or nothing is, whereas <code class="calibre10">patch</code> can partially apply patchfiles, leaving your working directory in a weird state. <code class="calibre10">git apply</code> is overall much more paranoid than <code class="calibre10">patch</code>. It won't create a commit for you - after running it, you must stage and commit the changes introduced manually.</p> <p class="calibre3">You can also use git apply to see if a patch applies cleanly before you try actually applying it - you can run <code class="calibre10">git apply --check</code> with the patch:</p> <pre class="calibre9"><code class="calibre10">$ git apply --check 0001-seeing-if-this-helps-the-gem.patch error: patch failed: ticgit.gemspec:1 error: ticgit.gemspec: patch does not apply </code></pre> <p class="calibre3">If there is no output, then the patch should apply cleanly. This command also exits with a non-zero status if the check fails, so you can use it in scripts if you want.</p> <h4 class="calibre14">Applying a Patch with am</h4> <p class="calibre3">If the contributor is a Git user and was good enough to use the <code class="calibre10">format-patch</code> command to generate their patch, then your job is easier because the patch contains author information and a commit message for you. If you can, encourage your contributors to use <code class="calibre10">format-patch</code> instead of <code class="calibre10">diff</code> to generate patches for you. You should only have to use <code class="calibre10">git apply</code> for legacy patches and things like that.</p> <p class="calibre3">To apply a patch generated by <code class="calibre10">format-patch</code>, you use <code class="calibre10">git am</code>. Technically, <code class="calibre10">git am</code> is built to read an mbox file, which is a simple, plain-text format for storing one or more e-mail messages in one text file. It looks something like this:</p> <pre class="calibre9"><code class="calibre10">From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001 From: Jessica Smith &lt;jessica@example.com&gt; Date: Sun, 6 Apr 2008 10:17:23 -0700 Subject: [PATCH 1/2] add limit to log function Limit log functionality to the first 20 </code></pre> <p class="calibre3">This is the beginning of the output of the format-patch command that you saw in the previous section. This is also a valid mbox e-mail format. If someone has e-mailed you the patch properly using git send-email, and you download that into an mbox format, then you can point git am to that mbox file, and it will start applying all the patches it sees. If you run a mail client that can save several e-mails out in mbox format, you can save entire patch series into a file and then use git am to apply them one at a time. </p> <p class="calibre3">However, if someone uploaded a patch file generated via <code class="calibre10">format-patch</code> to a ticketing system or something similar, you can save the file locally and then pass that file saved on your disk to <code class="calibre10">git am</code> to apply it:</p> <pre class="calibre9"><code class="calibre10">$ git am 0001-limit-log-function.patch Applying: add limit to log function </code></pre> <p class="calibre3">You can see that it applied cleanly and automatically created the new commit for you. The author information is taken from the e-mail's <code class="calibre10">From</code> and <code class="calibre10">Date</code> headers, and the message of the commit is taken from the <code class="calibre10">Subject</code> and body (before the patch) of the e-mail. For example, if this patch was applied from the mbox example I just showed, the commit generated would look something like this:</p> <pre class="calibre9"><code class="calibre10">$ git log --pretty=fuller -1 commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0 Author: Jessica Smith &lt;jessica@example.com&gt; AuthorDate: Sun Apr 6 10:17:23 2008 -0700 Commit: Scott Chacon &lt;schacon@gmail.com&gt; CommitDate: Thu Apr 9 09:19:06 2009 -0700 add limit to log function Limit log functionality to the first 20 </code></pre> <p class="calibre3">The <code class="calibre10">Commit</code> information indicates the person who applied the patch and the time it was applied. The <code class="calibre10">Author</code> information is the individual who originally created the patch and when it was originally created. </p> <p class="calibre3">But it's possible that the patch won't apply cleanly. Perhaps your main branch has diverged too far from the branch the patch was built from, or the patch depends on another patch you haven't applied yet. In that case, the <code class="calibre10">git am</code> process will fail and ask you what you want to do:</p> <pre class="calibre9"><code class="calibre10">$ git am 0001-seeing-if-this-helps-the-gem.patch Applying: seeing if this helps the gem error: patch failed: ticgit.gemspec:1 error: ticgit.gemspec: patch does not apply Patch failed at 0001. When you have resolved this problem run "git am --resolved". If you would prefer to skip this patch, instead run "git am --skip". To restore the original branch and stop patching run "git am --abort". </code></pre> <p class="calibre3">This command puts conflict markers in any files it has issues with, much like a conflicted merge or rebase operation. You solve this issue much the same way - edit the file to resolve the conflict, stage the new file, and then run <code class="calibre10">git am --resolved</code> to continue to the next patch:</p> <pre class="calibre9"><code class="calibre10">$ (fix the file) $ git add ticgit.gemspec $ git am --resolved Applying: seeing if this helps the gem </code></pre> <p class="calibre3">If you want Git to try a bit more intelligently to resolve the conflict, you can pass a <code class="calibre10">-3</code> option to it, which makes Git attempt a three-way merge. This option isn't on by default because it doesn't work if the commit the patch says it was based on isn't in your repository. If you do have that commit - if the patch was based on a public commit - then the <code class="calibre10">-3</code> option is generally much smarter about applying a conflicting patch:</p> <pre class="calibre9"><code class="calibre10">$ git am -3 0001-seeing-if-this-helps-the-gem.patch Applying: seeing if this helps the gem error: patch failed: ticgit.gemspec:1 error: ticgit.gemspec: patch does not apply Using index info to reconstruct a base tree... Falling back to patching base and 3-way merge... No changes -- Patch already applied. </code></pre> <p class="calibre3">In this case, I was trying to apply a patch I had already applied. Without the <code class="calibre10">-3</code> option, it looks like a conflict.</p> <p class="calibre3">If you're applying a number of patches from an mbox, you can also run the <code class="calibre10">am</code> command in interactive mode, which stops at each patch it finds and asks if you want to apply it:</p> <pre class="calibre9"><code class="calibre10">$ git am -3 -i mbox Commit Body is: -------------------------- seeing if this helps the gem -------------------------- Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all </code></pre> <p class="calibre3">This is nice if you have a number of patches saved, because you can view the patch first if you don't remember what it is, or not apply the patch if you've already done so.</p> <p class="calibre3">When all the patches for your topic are applied and committed into your branch, you can choose whether and how to integrate them into a longer-running branch.</p> <h3 class="calibre5">Checking Out Remote Branches</h3> <p class="calibre3">If your contribution came from a Git user who set up their own repository, pushed a number of changes into it, and then sent you the URL to the repository and the name of the remote branch the changes are in, you can add them as a remote and do merges locally.</p> <p class="calibre3">For instance, if Jessica sends you an e-mail saying that she has a great new feature in the <code class="calibre10">ruby-client</code> branch of her repository, you can test it by adding the remote and checking out that branch locally:</p> <pre class="calibre9"><code class="calibre10">$ git remote add jessica git://github.com/jessica/myproject.git $ git fetch jessica $ git checkout -b rubyclient jessica/ruby-client </code></pre> <p class="calibre3">If she e-mails you again later with another branch containing another great feature, you can fetch and check out because you already have the remote setup.</p> <p class="calibre3">This is most useful if you're working with a person consistently. If someone only has a single patch to contribute once in a while, then accepting it over e-mail may be less time consuming than requiring everyone to run their own server and having to continually add and remove remotes to get a few patches. You're also unlikely to want to have hundreds of remotes, each for someone who contributes only a patch or two. However, scripts and hosted services may make this easier - it depends largely on how you develop and how your contributors develop.</p> <p class="calibre3">The other advantage of this approach is that you get the history of the commits as well. Although you may have legitimate merge issues, you know where in your history their work is based; a proper three-way merge is the default rather than having to supply a <code class="calibre10">-3</code> and hope the patch was generated off a public commit to which you have access.</p> <p class="calibre3">If you aren't working with a person consistently but still want to pull from them in this way, you can provide the URL of the remote repository to the <code class="calibre10">git pull</code> command. This does a one-time pull and doesn't save the URL as a remote reference:</p> <pre class="calibre9"><code class="calibre10">$ git pull git://github.com/onetimeguy/project.git From git://github.com/onetimeguy/project * branch HEAD -&gt; FETCH_HEAD Merge made by recursive. </code></pre> <h3 class="calibre5">Determining What Is Introduced</h3> <p class="calibre3">Now you have a topic branch that contains contributed work. At this point, you can determine what you'd like to do with it. This section revisits a couple of commands so you can see how you can use them to review exactly what you'll be introducing if you merge this into your main branch.</p> <p class="calibre3">It's often helpful to get a review of all the commits that are in this branch but that aren't in your master branch. You can exclude commits in the master branch by adding the <code class="calibre10">--not</code> option before the branch name. For example, if your contributor sends you two patches and you create a branch called <code class="calibre10">contrib</code> and applied those patches there, you can run this:</p> <pre class="calibre9"><code class="calibre10">$ git log contrib --not master commit 5b6235bd297351589efc4d73316f0a68d484f118 Author: Scott Chacon &lt;schacon@gmail.com&gt; Date: Fri Oct 24 09:53:59 2008 -0700 seeing if this helps the gem commit 7482e0d16d04bea79d0dba8988cc78df655f16a0 Author: Scott Chacon &lt;schacon@gmail.com&gt; Date: Mon Oct 22 19:38:36 2008 -0700 updated the gemspec to hopefully work better </code></pre> <p class="calibre3">To see what changes each commit introduces, remember that you can pass the <code class="calibre10">-p</code> option to <code class="calibre10">git log</code> and it will append the diff introduced to each commit.</p> <p class="calibre3">To see a full diff of what would happen if you were to merge this topic branch with another branch, you may have to use a weird trick to get the correct results. You may think to run this:</p> <pre class="calibre9"><code class="calibre10">$ git diff master </code></pre> <p class="calibre3">This command gives you a diff, but it may be misleading. If your <code class="calibre10">master</code> branch has moved forward since you created the topic branch from it, then you'll get seemingly strange results. This happens because Git directly compares the snapshots of the last commit of the topic branch you're on and the snapshot of the last commit on the <code class="calibre10">master</code> branch. For example, if you've added a line in a file on the <code class="calibre10">master</code> branch, a direct comparison of the snapshots will look like the topic branch is going to remove that line.</p> <p class="calibre3">If <code class="calibre10">master</code> is a direct ancestor of your topic branch, this isn't a problem; but if the two histories have diverged, the diff will look like you're adding all the new stuff in your topic branch and removing everything unique to the <code class="calibre10">master</code> branch.</p> <p class="calibre3">What you really want to see are the changes added to the topic branch - the work you'll introduce if you merge this branch with master. You do that by having Git compare the last commit on your topic branch with the first common ancestor it has with the master branch.</p> <p class="calibre3">Technically, you can do that by explicitly figuring out the common ancestor and then running your diff on it:</p> <pre class="calibre9"><code class="calibre10">$ git merge-base contrib master 36c7dba2c95e6bbb78dfa822519ecfec6e1ca649 $ git diff 36c7db </code></pre> <p class="calibre3">However, that isn't convenient, so Git provides another shorthand for doing the same thing: the triple-dot syntax. In the context of the <code class="calibre10">diff</code> command, you can put three periods after another branch to do a <code class="calibre10">diff</code> between the last commit of the branch you're on and its common ancestor with another branch:</p> <pre class="calibre9"><code class="calibre10">$ git diff master...contrib </code></pre> <p class="calibre3">This command shows you only the work your current topic branch has introduced since its common ancestor with master. That is a very useful syntax to remember.</p> <h3 class="calibre5">Integrating Contributed Work</h3> <p class="calibre3">When all the work in your topic branch is ready to be integrated into a more mainline branch, the question is how to do it. Furthermore, what overall workflow do you want to use to maintain your project? You have a number of choices, so I'll cover a few of them.</p> <h4 class="calibre14">Merging Workflows</h4> <p class="calibre3">One simple workflow merges your work into your <code class="calibre10">master</code> branch. In this scenario, you have a <code class="calibre10">master</code> branch that contains basically stable code. When you have work in a topic branch that you've done or that someone has contributed and you've verified, you merge it into your master branch, delete the topic branch, and then continue the process. If we have a repository with work in two branches named <code class="calibre10">ruby_client</code> and <code class="calibre10">php_client</code> that looks like Figure 5-19 and merge <code class="calibre10">ruby_client</code> first and then <code class="calibre10">php_client</code> next, then your history will end up looking like Figure 5-20.</p> <p class="calibre3"><img src="18333fig0519-tn.png" alt="Figure 5-19. History with several topic branches." title="Figure 5-19. History with several topic branches." class="calibre6"/></p> <p class="calibre3"><img src="18333fig0520-tn.png" alt="Figure 5-20. After a topic branch merge." title="Figure 5-20. After a topic branch merge." class="calibre6"/></p> <p class="calibre3">That is probably the simplest workflow, but it's problematic if you're dealing with larger repositories or projects.</p> <p class="calibre3">If you have more developers or a larger project, you'll probably want to use at least a two-phase merge cycle. In this scenario, you have two long-running branches, <code class="calibre10">master</code> and <code class="calibre10">develop</code>, in which you determine that <code class="calibre10">master</code> is updated only when a very stable release is cut and all new code is integrated into the <code class="calibre10">develop</code> branch. You regularly push both of these branches to the public repository. Each time you have a new topic branch to merge in (Figure 5-21), you merge it into <code class="calibre10">develop</code> (Figure 5-22); then, when you tag a release, you fast-forward <code class="calibre10">master</code> to wherever the now-stable <code class="calibre10">develop</code> branch is (Figure 5-23).</p> <p class="calibre3"><img src="18333fig0521-tn.png" alt="Figure 5-21. Before a topic branch merge." title="Figure 5-21. Before a topic branch merge." class="calibre6"/></p> <p class="calibre3"><img src="18333fig0522-tn.png" alt="Figure 5-22. After a topic branch merge." title="Figure 5-22. After a topic branch merge." class="calibre6"/></p> <p class="calibre3"><img src="18333fig0523-tn.png" alt="Figure 5-23. After a topic branch release." title="Figure 5-23. After a topic branch release." class="calibre6"/></p> <p class="calibre3">This way, when people clone your project's repository, they can either check out master to build the latest stable version and keep up to date on that easily, or they can check out develop, which is the more cutting-edge stuff. You can also continue this concept, having an integrate branch where all the work is merged together. Then, when the codebase on that branch is stable and passes tests, you merge it into a develop branch; and when that has proven itself stable for a while, you fast-forward your master branch.</p> <h4 class="calibre14">Large-Merging Workflows</h4> <p class="calibre3">The Git project has four long-running branches: <code class="calibre10">master</code>, <code class="calibre10">next</code>, and <code class="calibre10">pu</code> (proposed updates) for new work, and <code class="calibre10">maint</code> for maintenance backports. When new work is introduced by contributors, it's collected into topic branches in the maintainer's repository in a manner similar to what I've described (see Figure 5-24). At this point, the topics are evaluated to determine whether they're safe and ready for consumption or whether they need more work. If they're safe, they're merged into <code class="calibre10">next</code>, and that branch is pushed up so everyone can try the topics integrated together.</p> <p class="calibre3"><img src="18333fig0524-tn.png" alt="Figure 5-24. Managing a complex series of parallel contributed topic branches." title="Figure 5-24. Managing a complex series of parallel contributed topic branches." class="calibre6"/></p> <p class="calibre3">If the topics still need work, they're merged into <code class="calibre10">pu</code> instead. When it's determined that they're totally stable, the topics are re-merged into <code class="calibre10">master</code> and are then rebuilt from the topics that were in <code class="calibre10">next</code> but didn't yet graduate to <code class="calibre10">master</code>. This means <code class="calibre10">master</code> almost always moves forward, <code class="calibre10">next</code> is rebased occasionally, and <code class="calibre10">pu</code> is rebased even more often (see Figure 5-25).</p> <p class="calibre3"><img src="18333fig0525-tn.png" alt="Figure 5-25. Merging contributed topic branches into long-term integration branches." title="Figure 5-25. Merging contributed topic branches into long-term integration branches." class="calibre6"/></p> <p class="calibre3">When a topic branch has finally been merged into <code class="calibre10">master</code>, it's removed from the repository. The Git project also has a <code class="calibre10">maint</code> branch that is forked off from the last release to provide backported patches in case a maintenance release is required. Thus, when you clone the Git repository, you have four branches that you can check out to evaluate the project in different stages of development, depending on how cutting edge you want to be or how you want to contribute; and the maintainer has a structured workflow to help them vet new contributions.</p> <h4 class="calibre14">Rebasing and Cherry Picking Workflows</h4> <p class="calibre3">Other maintainers prefer to rebase or cherry-pick contributed work on top of their master branch, rather than merging it in, to keep a mostly linear history. When you have work in a topic branch and have determined that you want to integrate it, you move to that branch and run the rebase command to rebuild the changes on top of your current master (or <code class="calibre10">develop</code>, and so on) branch. If that works well, you can fast-forward your <code class="calibre10">master</code> branch, and you'll end up with a linear project history.</p> <p class="calibre3">The other way to move introduced work from one branch to another is to cherry-pick it. A cherry-pick in Git is like a rebase for a single commit. It takes the patch that was introduced in a commit and tries to reapply it on the branch you're currently on. This is useful if you have a number of commits on a topic branch and you want to integrate only one of them, or if you only have one commit on a topic branch and you'd prefer to cherry-pick it rather than run rebase. For example, suppose you have a project that looks like Figure 5-26.</p> <p class="calibre3"><img src="18333fig0526-tn.png" alt="Figure 5-26. Example history before a cherry pick." title="Figure 5-26. Example history before a cherry pick." class="calibre6"/></p> <p class="calibre3">If you want to pull commit <code class="calibre10">e43a6</code> into your master branch, you can run</p> <pre class="calibre9"><code class="calibre10">$ git cherry-pick e43a6fd3e94888d76779ad79fb568ed180e5fcdf Finished one cherry-pick. [master]: created a0a41a9: "More friendly message when locking the index fails." 3 files changed, 17 insertions(+), 3 deletions(-) </code></pre> <p class="calibre3">This pulls the same change introduced in <code class="calibre10">e43a6</code>, but you get a new commit SHA-1 value, because the date applied is different. Now your history looks like Figure 5-27.</p> <p class="calibre3"><img src="18333fig0527-tn.png" alt="Figure 5-27. History after cherry-picking a commit on a topic branch." title="Figure 5-27. History after cherry-picking a commit on a topic branch." class="calibre6"/></p> <p class="calibre3">Now you can remove your topic branch and drop the commits you didn't want to pull in.</p> <h3 class="calibre5">Tagging Your Releases</h3> <p class="calibre3">When you've decided to cut a release, you'll probably want to drop a tag so you can re-create that release at any point going forward. You can create a new tag as I discussed in Chapter 2. If you decide to sign the tag as the maintainer, the tagging may look something like this:</p> <pre class="calibre9"><code class="calibre10">$ git tag -s v1.5 -m 'my signed 1.5 tag' You need a passphrase to unlock the secret key for user: "Scott Chacon &lt;schacon@gmail.com&gt;" 1024-bit DSA key, ID F721C45A, created 2009-02-09 </code></pre> <p class="calibre3">If you do sign your tags, you may have the problem of distributing the public PGP key used to sign your tags. The maintainer of the Git project has solved this issue by including their public key as a blob in the repository and then adding a tag that points directly to that content. To do this, you can figure out which key you want by running <code class="calibre10">gpg --list-keys</code>:</p> <pre class="calibre9"><code class="calibre10">$ gpg --list-keys /Users/schacon/.gnupg/pubring.gpg --------------------------------- pub 1024D/F721C45A 2009-02-09 [expires: 2010-02-09] uid Scott Chacon &lt;schacon@gmail.com&gt; sub 2048g/45D02282 2009-02-09 [expires: 2010-02-09] </code></pre> <p class="calibre3">Then, you can directly import the key into the Git database by exporting it and piping that through <code class="calibre10">git hash-object</code>, which writes a new blob with those contents into Git and gives you back the SHA-1 of the blob:</p> <pre class="calibre9"><code class="calibre10">$ gpg -a --export F721C45A | git hash-object -w --stdin 659ef797d181633c87ec71ac3f9ba29fe5775b92 </code></pre> <p class="calibre3">Now that you have the contents of your key in Git, you can create a tag that points directly to it by specifying the new SHA-1 value that the <code class="calibre10">hash-object</code> command gave you:</p> <pre class="calibre9"><code class="calibre10">$ git tag -a maintainer-pgp-pub 659ef797d181633c87ec71ac3f9ba29fe5775b92 </code></pre> <p class="calibre3">If you run <code class="calibre10">git push --tags</code>, the <code class="calibre10">maintainer-pgp-pub</code> tag will be shared with everyone. If anyone wants to verify a tag, they can directly import your PGP key by pulling the blob directly out of the database and importing it into GPG:</p> <pre class="calibre9"><code class="calibre10">$ git show maintainer-pgp-pub | gpg --import </code></pre> <p class="calibre3">They can use that key to verify all your signed tags. Also, if you include instructions in the tag message, running <code class="calibre10">git show &lt;tag&gt;</code> will let you give the end user more specific instructions about tag verification.</p> <h3 class="calibre5">Generating a Build Number</h3> <p class="calibre3">Because Git doesn't have monotonically increasing numbers like 'v123' or the equivalent to go with each commit, if you want to have a human-readable name to go with a commit, you can run <code class="calibre10">git describe</code> on that commit. Git gives you the name of the nearest tag with the number of commits on top of that tag and a partial SHA-1 value of the commit you're describing:</p> <pre class="calibre9"><code class="calibre10">$ git describe master v1.6.2-rc1-20-g8c5b85c </code></pre> <p class="calibre3">This way, you can export a snapshot or build and name it something understandable to people. In fact, if you build Git from source code cloned from the Git repository, <code class="calibre10">git --version</code> gives you something that looks like this. If you're describing a commit that you have directly tagged, it gives you the tag name.</p> <p class="calibre3">The <code class="calibre10">git describe</code> command favors annotated tags (tags created with the <code class="calibre10">-a</code> or <code class="calibre10">-s</code> flag), so release tags should be created this way if you're using <code class="calibre10">git describe</code>, to ensure the commit is named properly when described. You can also use this string as the target of a checkout or show command, although it relies on the abbreviated SHA-1 value at the end, so it may not be valid forever. For instance, the Linux kernel recently jumped from 8 to 10 characters to ensure SHA-1 object uniqueness, so older <code class="calibre10">git describe</code> output names were invalidated.</p> <h3 class="calibre5">Preparing a Release</h3> <p class="calibre3">Now you want to release a build. One of the things you'll want to do is create an archive of the latest snapshot of your code for those poor souls who don't use Git. The command to do this is <code class="calibre10">git archive</code>:</p> <pre class="calibre9"><code class="calibre10">$ git archive master --prefix='project/' | gzip &gt; `git describe master`.tar.gz $ ls *.tar.gz v1.6.2-rc1-20-g8c5b85c.tar.gz </code></pre> <p class="calibre3">If someone opens that tarball, they get the latest snapshot of your project under a project directory. You can also create a zip archive in much the same way, but by passing the <code class="calibre10">--format=zip</code> option to <code class="calibre10">git archive</code>:</p> <pre class="calibre9"><code class="calibre10">$ git archive master --prefix='project/' --format=zip &gt; `git describe master`.zip </code></pre> <p class="calibre3">You now have a nice tarball and a zip archive of your project release that you can upload to your website or e-mail to people.</p> <h3 class="calibre5">The Shortlog</h3> <p class="calibre3">It's time to e-mail your mailing list of people who want to know what's happening in your project. A nice way of quickly getting a sort of changelog of what has been added to your project since your last release or e-mail is to use the <code class="calibre10">git shortlog</code> command. It summarizes all the commits in the range you give it; for example, the following gives you a summary of all the commits since your last release, if your last release was named v1.0.1:</p> <pre class="calibre9"><code class="calibre10">$ git shortlog --no-merges master --not v1.0.1 Chris Wanstrath (8): Add support for annotated tags to Grit::Tag Add packed-refs annotated tag support. Add Grit::Commit#to_patch Update version and History.txt Remove stray `puts` Make ls_tree ignore nils Tom Preston-Werner (4): fix dates in history dynamic version method Version bump to 1.0.2 Regenerated gemspec for version 1.0.2 </code></pre> <p class="calibre3">You get a clean summary of all the commits since v1.0.1, grouped by author, that you can e-mail to your list.</p> </body> </html>