add summary script so i can see progress and authorship
[progit-mk.git] / pt-br / 08-git-and-other-scms / 01-chapter8.markdown
blob9919de19eb93ab88aa5420381b9f2c9c2aa7b27a
1 # Git and Other Systems #
3 The world isn’t perfect. Usually, you can’t immediately switch every project you come in contact with to Git. Sometimes you’re stuck on a project using another VCS, and many times that system is Subversion. You’ll spend the first part of this chapter learning about `git svn`, the bidirectional Subversion gateway tool in Git.
5 At some point, you may want to convert your existing project to Git. The second part of this chapter covers how to migrate your project into Git: first from Subversion, then from Perforce, and finally via a custom import script for a nonstandard importing case. 
7 ## Git and Subversion ##
9 Currently, the majority of open source development projects and a large number of corporate projects use Subversion to manage their source code. It’s the most popular open source VCS and has been around for nearly a decade. It’s also very similar in many ways to CVS, which was the big boy of the source-control world before that.
11 One of Git’s great features is a bidirectional bridge to Subversion called `git svn`. This tool allows you to use Git as a valid client to a Subversion server, so you can use all the local features of Git and then push to a Subversion server as if you were using Subversion locally. This means you can do local branching and merging, use the staging area, use rebasing and cherry-picking, and so on, while your collaborators continue to work in their dark and ancient ways. It’s a good way to sneak Git into the corporate environment and help your fellow developers become more efficient while you lobby to get the infrastructure changed to support Git fully. The Subversion bridge is the gateway drug to the DVCS world.
13 ### git svn ###
15 The base command in Git for all the Subversion bridging commands is `git svn`. You preface everything with that. It takes quite a few commands, so you’ll learn about the common ones while going through a few small workflows.
17 It’s important to note that when you’re using `git svn`, you’re interacting with Subversion, which is a system that is far less sophisticated than Git. Although you can easily do local branching and merging, it’s generally best to keep your history as linear as possible by rebasing your work and avoiding doing things like simultaneously interacting with a Git remote repository.
19 Don’t rewrite your history and try to push again, and don’t push to a parallel Git repository to collaborate with fellow Git developers at the same time. Subversion can have only a single linear history, and confusing it is very easy. If you’re working with a team, and some are using SVN and others are using Git, make sure everyone is using the SVN server to collaborate — doing so will make your life easier.
21 ### Setting Up ###
23 To demonstrate this functionality, you need a typical SVN repository that you have write access to. If you want to copy these examples, you’ll have to make a writeable copy of my test repository. In order to do that easily, you can use a tool called `svnsync` that comes with more recent versions of Subversion — it should be distributed with at least 1.4. For these tests, I created a new Subversion repository on Google code that was a partial copy of the `protobuf` project, which is a tool that encodes structured data for network transmission. 
25 To follow along, you first need to create a new local Subversion repository:
27         $ mkdir /tmp/test-svn
28         $ svnadmin create /tmp/test-svn
30 Then, enable all users to change revprops — the easy way is to add a pre-revprop-change script that always exits 0:
32         $ cat /tmp/test-svn/hooks/pre-revprop-change 
33         #!/bin/sh
34         exit 0;
35         $ chmod +x /tmp/test-svn/hooks/pre-revprop-change
37 You can now sync this project to your local machine by calling `svnsync init` with the to and from repositories.
39         $ svnsync init file:///tmp/test-svn http://progit-example.googlecode.com/svn/ 
41 This sets up the properties to run the sync. You can then clone the code by running
43         $ svnsync sync file:///tmp/test-svn
44         Committed revision 1.
45         Copied properties for revision 1.
46         Committed revision 2.
47         Copied properties for revision 2.
48         Committed revision 3.
49         ...
51 Although this operation may take only a few minutes, if you try to copy the original repository to another remote repository instead of a local one, the process will take nearly an hour, even though there are fewer than 100 commits. Subversion has to clone one revision at a time and then push it back into another repository — it’s ridiculously inefficient, but it’s the only easy way to do this.
53 ### Getting Started ###
55 Now that you have a Subversion repository to which you have write access, you can go through a typical workflow. You’ll start with the `git svn clone` command, which imports an entire Subversion repository into a local Git repository. Remember that if you’re importing from a real hosted Subversion repository, you should replace the `file:///tmp/test-svn` here with the URL of your Subversion repository:
57         $ git svn clone file:///tmp/test-svn -T trunk -b branches -t tags
58         Initialized empty Git repository in /Users/schacon/projects/testsvnsync/svn/.git/
59         r1 = b4e387bc68740b5af56c2a5faf4003ae42bd135c (trunk)
60               A    m4/acx_pthread.m4
61               A    m4/stl_hash.m4
62         ...
63         r75 = d1957f3b307922124eec6314e15bcda59e3d9610 (trunk)
64         Found possible branch point: file:///tmp/test-svn/trunk => \
65             file:///tmp/test-svn /branches/my-calc-branch, 75
66         Found branch parent: (my-calc-branch) d1957f3b307922124eec6314e15bcda59e3d9610
67         Following parent with do_switch
68         Successfully followed parent
69         r76 = 8624824ecc0badd73f40ea2f01fce51894189b01 (my-calc-branch)
70         Checked out HEAD:
71          file:///tmp/test-svn/branches/my-calc-branch r76
73 This runs the equivalent of two commands — `git svn init` followed by `git svn fetch` — on the URL you provide. This can take a while. The test project has only about 75 commits and the codebase isn’t that big, so it takes just a few minutes. However, Git has to check out each version, one at a time, and commit it individually. For a project with hundreds or thousands of commits, this can literally take hours or even days to finish.
75 The `-T trunk -b branches -t tags` part tells Git that this Subversion repository follows the basic branching and tagging conventions. If you name your trunk, branches, or tags differently, you can change these options. Because this is so common, you can replace this entire part with `-s`, which means standard layout and implies all those options. The following command is equivalent:
77         $ git svn clone file:///tmp/test-svn -s
79 At this point, you should have a valid Git repository that has imported your branches and tags:
81         $ git branch -a
82         * master
83           my-calc-branch
84           tags/2.0.2
85           tags/release-2.0.1
86           tags/release-2.0.2
87           tags/release-2.0.2rc1
88           trunk
90 It’s important to note how this tool namespaces your remote references differently. When you’re cloning a normal Git repository, you get all the branches on that remote server available locally as something like `origin/[branch]` - namespaced by the name of the remote. However, `git svn` assumes that you won’t have multiple remotes and saves all its references to points on the remote server with no namespacing. You can use the Git plumbing command `show-ref` to look at all your full reference names:
92         $ git show-ref
93         1cbd4904d9982f386d87f88fce1c24ad7c0f0471 refs/heads/master
94         aee1ecc26318164f355a883f5d99cff0c852d3c4 refs/remotes/my-calc-branch
95         03d09b0e2aad427e34a6d50ff147128e76c0e0f5 refs/remotes/tags/2.0.2
96         50d02cc0adc9da4319eeba0900430ba219b9c376 refs/remotes/tags/release-2.0.1
97         4caaa711a50c77879a91b8b90380060f672745cb refs/remotes/tags/release-2.0.2
98         1c4cb508144c513ff1214c3488abe66dcb92916f refs/remotes/tags/release-2.0.2rc1
99         1cbd4904d9982f386d87f88fce1c24ad7c0f0471 refs/remotes/trunk
101 A normal Git repository looks more like this:
103         $ git show-ref
104         83e38c7a0af325a9722f2fdc56b10188806d83a1 refs/heads/master
105         3e15e38c198baac84223acfc6224bb8b99ff2281 refs/remotes/gitserver/master
106         0a30dd3b0c795b80212ae723640d4e5d48cabdff refs/remotes/origin/master
107         25812380387fdd55f916652be4881c6f11600d6f refs/remotes/origin/testing
109 You have two remote servers: one named `gitserver` with a `master` branch; and another named `origin` with two branches, `master` and `testing`. 
111 Notice how in the example of remote references imported from `git svn`, tags are added as remote branches, not as real Git tags. Your Subversion import looks like it has a remote named tags with branches under it.
113 ### Committing Back to Subversion ###
115 Now that you have a working repository, you can do some work on the project and push your commits back upstream, using Git effectively as a SVN client. If you edit one of the files and commit it, you have a commit that exists in Git locally that doesn’t exist on the Subversion server:
117         $ git commit -am 'Adding git-svn instructions to the README'
118         [master 97031e5] Adding git-svn instructions to the README
119          1 files changed, 1 insertions(+), 1 deletions(-)
121 Next, you need to push your change upstream. Notice how this changes the way you work with Subversion — you can do several commits offline and then push them all at once to the Subversion server. To push to a Subversion server, you run the `git svn dcommit` command:
123         $ git svn dcommit
124         Committing to file:///tmp/test-svn/trunk ...
125                M      README.txt
126         Committed r79
127                M      README.txt
128         r79 = 938b1a547c2cc92033b74d32030e86468294a5c8 (trunk)
129         No changes between current HEAD and refs/remotes/trunk
130         Resetting to the latest refs/remotes/trunk
132 This takes all the commits you’ve made on top of the Subversion server code, does a Subversion commit for each, and then rewrites your local Git commit to include a unique identifier. This is important because it means that all the SHA-1 checksums for your commits change. Partly for this reason, working with Git-based remote versions of your projects concurrently with a Subversion server isn’t a good idea. If you look at the last commit, you can see the new `git-svn-id` that was added:
134         $ git log -1
135         commit 938b1a547c2cc92033b74d32030e86468294a5c8
136         Author: schacon <schacon@4c93b258-373f-11de-be05-5f7a86268029>
137         Date:   Sat May 2 22:06:44 2009 +0000
139             Adding git-svn instructions to the README
141             git-svn-id: file:///tmp/test-svn/trunk@79 4c93b258-373f-11de-be05-5f7a86268029
143 Notice that the SHA checksum that originally started with `97031e5` when you committed now begins with `938b1a5`. If you want to push to both a Git server and a Subversion server, you have to push (`dcommit`) to the Subversion server first, because that action changes your commit data.
145 ### Pulling in New Changes ###
147 If you’re working with other developers, then at some point one of you will push, and then the other one will try to push a change that conflicts. That change will be rejected until you merge in their work. In `git svn`, it looks like this:
149         $ git svn dcommit
150         Committing to file:///tmp/test-svn/trunk ...
151         Merge conflict during commit: Your file or directory 'README.txt' is probably \
152         out-of-date: resource out of date; try updating at /Users/schacon/libexec/git-\
153         core/git-svn line 482
155 To resolve this situation, you can run `git svn rebase`, which pulls down any changes on the server that you don’t have yet and rebases any work you have on top of what is on the server:
157         $ git svn rebase
158                M      README.txt
159         r80 = ff829ab914e8775c7c025d741beb3d523ee30bc4 (trunk)
160         First, rewinding head to replay your work on top of it...
161         Applying: first user change
163 Now, all your work is on top of what is on the Subversion server, so you can successfully `dcommit`:
165         $ git svn dcommit
166         Committing to file:///tmp/test-svn/trunk ...
167                M      README.txt
168         Committed r81
169                M      README.txt
170         r81 = 456cbe6337abe49154db70106d1836bc1332deed (trunk)
171         No changes between current HEAD and refs/remotes/trunk
172         Resetting to the latest refs/remotes/trunk
174 It’s important to remember that unlike Git, which requires you to merge upstream work you don’t yet have locally before you can push, `git svn` makes you do that only if the changes conflict. If someone else pushes a change to one file and then you push a change to another file, your `dcommit` will work fine:
176         $ git svn dcommit
177         Committing to file:///tmp/test-svn/trunk ...
178                M      configure.ac
179         Committed r84
180                M      autogen.sh
181         r83 = 8aa54a74d452f82eee10076ab2584c1fc424853b (trunk)
182                M      configure.ac
183         r84 = cdbac939211ccb18aa744e581e46563af5d962d0 (trunk)
184         W: d2f23b80f67aaaa1f6f5aaef48fce3263ac71a92 and refs/remotes/trunk differ, \
185           using rebase:
186         :100755 100755 efa5a59965fbbb5b2b0a12890f1b351bb5493c18 \
187           015e4c98c482f0fa71e4d5434338014530b37fa6 M   autogen.sh
188         First, rewinding head to replay your work on top of it...
189         Nothing to do.
191 This is important to remember, because the outcome is a project state that didn’t exist on either of your computers when you pushed. If the changes are incompatible but don’t conflict, you may get issues that are difficult to diagnose. This is different than using a Git server — in Git, you can fully test the state on your client system before publishing it, whereas in SVN, you can’t ever be certain that the states immediately before commit and after commit are identical.
193 You should also run this command to pull in changes from the Subversion server, even if you’re not ready to commit yourself. You can run `git svn fetch` to grab the new data, but `git svn rebase` does the fetch and then updates your local commits.
195         $ git svn rebase
196                M      generate_descriptor_proto.sh
197         r82 = bd16df9173e424c6f52c337ab6efa7f7643282f1 (trunk)
198         First, rewinding head to replay your work on top of it...
199         Fast-forwarded master to refs/remotes/trunk.
201 Running `git svn rebase` every once in a while makes sure your code is always up to date. You need to be sure your working directory is clean when you run this, though. If you have local changes, you must either stash your work or temporarily commit it before running `git svn rebase` — otherwise, the command will stop if it sees that the rebase will result in a merge conflict.
203 ### Git Branching Issues ###
205 When you’ve become comfortable with a Git workflow, you’ll likely create topic branches, do work on them, and then merge them in. If you’re pushing to a Subversion server via git svn, you may want to rebase your work onto a single branch each time instead of merging branches together. The reason to prefer rebasing is that Subversion has a linear history and doesn’t deal with merges like Git does, so git svn follows only the first parent when converting the snapshots into Subversion commits.
207 Suppose your history looks like the following: you created an `experiment` branch, did two commits, and then merged them back into `master`. When you `dcommit`, you see output like this:
209         $ git svn dcommit
210         Committing to file:///tmp/test-svn/trunk ...
211                M      CHANGES.txt
212         Committed r85
213                M      CHANGES.txt
214         r85 = 4bfebeec434d156c36f2bcd18f4e3d97dc3269a2 (trunk)
215         No changes between current HEAD and refs/remotes/trunk
216         Resetting to the latest refs/remotes/trunk
217         COPYING.txt: locally modified
218         INSTALL.txt: locally modified
219                M      COPYING.txt
220                M      INSTALL.txt
221         Committed r86
222                M      INSTALL.txt
223                M      COPYING.txt
224         r86 = 2647f6b86ccfcaad4ec58c520e369ec81f7c283c (trunk)
225         No changes between current HEAD and refs/remotes/trunk
226         Resetting to the latest refs/remotes/trunk
228 Running `dcommit` on a branch with merged history works fine, except that when you look at your Git project history, it hasn’t rewritten either of the commits you made on the `experiment` branch — instead, all those changes appear in the SVN version of the single merge commit.
230 When someone else clones that work, all they see is the merge commit with all the work squashed into it; they don’t see the commit data about where it came from or when it was committed.
232 ### Subversion Branching ###
234 Branching in Subversion isn’t the same as branching in Git; if you can avoid using it much, that’s probably best. However, you can create and commit to branches in Subversion using git svn.
236 #### Creating a New SVN Branch ####
238 To create a new branch in Subversion, you run `git svn branch [branchname]`:
240         $ git svn branch opera
241         Copying file:///tmp/test-svn/trunk at r87 to file:///tmp/test-svn/branches/opera...
242         Found possible branch point: file:///tmp/test-svn/trunk => \
243           file:///tmp/test-svn/branches/opera, 87
244         Found branch parent: (opera) 1f6bfe471083cbca06ac8d4176f7ad4de0d62e5f
245         Following parent with do_switch
246         Successfully followed parent
247         r89 = 9b6fe0b90c5c9adf9165f700897518dbc54a7cbf (opera)
249 This does the equivalent of the `svn copy trunk branches/opera` command in Subversion and operates on the Subversion server. It’s important to note that it doesn’t check you out into that branch; if you commit at this point, that commit will go to `trunk` on the server, not `opera`.
251 ### Switching Active Branches ###
253 Git figures out what branch your dcommits go to by looking for the tip of any of your Subversion branches in your history — you should have only one, and it should be the last one with a `git-svn-id` in your current branch history. 
255 If you want to work on more than one branch simultaneously, you can set up local branches to `dcommit` to specific Subversion branches by starting them at the imported Subversion commit for that branch. If you want an `opera` branch that you can work on separately, you can run
257         $ git branch opera remotes/opera
259 Now, if you want to merge your `opera` branch into `trunk` (your `master` branch), you can do so with a normal `git merge`. But you need to provide a descriptive commit message (via `-m`), or the merge will say "Merge branch opera" instead of something useful.
261 Remember that although you’re using `git merge` to do this operation, and the merge likely will be much easier than it would be in Subversion (because Git will automatically detect the appropriate merge base for you), this isn’t a normal Git merge commit. You have to push this data back to a Subversion server that can’t handle a commit that tracks more than one parent; so, after you push it up, it will look like a single commit that squashed in all the work of another branch under a single commit. After you merge one branch into another, you can’t easily go back and continue working on that branch, as you normally can in Git. The `dcommit` command that you run erases any information that says what branch was merged in, so subsequent merge-base calculations will be wrong — the dcommit makes your `git merge` result look like you ran `git merge --squash`. Unfortunately, there’s no good way to avoid this situation — Subversion can’t store this information, so you’ll always be crippled by its limitations while you’re using it as your server. To avoid issues, you should delete the local branch (in this case, `opera`) after you merge it into trunk.
263 ### Subversion Commands ###
265 The `git svn` toolset provides a number of commands to help ease the transition to Git by providing some functionality that’s similar to what you had in Subversion. Here are a few commands that give you what Subversion used to.
267 #### SVN Style History ####
269 If you’re used to Subversion and want to see your history in SVN output style, you can run `git svn log` to view your commit history in SVN formatting:
271         $ git svn log
272         ------------------------------------------------------------------------
273         r87 | schacon | 2009-05-02 16:07:37 -0700 (Sat, 02 May 2009) | 2 lines
275         autogen change
277         ------------------------------------------------------------------------
278         r86 | schacon | 2009-05-02 16:00:21 -0700 (Sat, 02 May 2009) | 2 lines
280         Merge branch 'experiment'
282         ------------------------------------------------------------------------
283         r85 | schacon | 2009-05-02 16:00:09 -0700 (Sat, 02 May 2009) | 2 lines
284         
285         updated the changelog
287 You should know two important things about `git svn log`. First, it works offline, unlike the real `svn log` command, which asks the Subversion server for the data. Second, it only shows you commits that have been committed up to the Subversion server. Local Git commits that you haven’t dcommited don’t show up; neither do commits that people have made to the Subversion server in the meantime. It’s more like the last known state of the commits on the Subversion server.
289 #### SVN Annotation ####
291 Much as the `git svn log` command simulates the `svn log` command offline, you can get the equivalent of `svn annotate` by running `git svn blame [FILE]`. The output looks like this:
293         $ git svn blame README.txt 
294          2   temporal Protocol Buffers - Google's data interchange format
295          2   temporal Copyright 2008 Google Inc.
296          2   temporal http://code.google.com/apis/protocolbuffers/
297          2   temporal 
298         22   temporal C++ Installation - Unix
299         22   temporal =======================
300          2   temporal 
301         79    schacon Committing in git-svn.
302         78    schacon 
303          2   temporal To build and install the C++ Protocol Buffer runtime and the Protocol
304          2   temporal Buffer compiler (protoc) execute the following:
305          2   temporal 
307 Again, it doesn’t show commits that you did locally in Git or that have been pushed to Subversion in the meantime.
309 #### SVN Server Information ####
311 You can also get the same sort of information that `svn info` gives you by running `git svn info`:
313         $ git svn info
314         Path: .
315         URL: https://schacon-test.googlecode.com/svn/trunk
316         Repository Root: https://schacon-test.googlecode.com/svn
317         Repository UUID: 4c93b258-373f-11de-be05-5f7a86268029
318         Revision: 87
319         Node Kind: directory
320         Schedule: normal
321         Last Changed Author: schacon
322         Last Changed Rev: 87
323         Last Changed Date: 2009-05-02 16:07:37 -0700 (Sat, 02 May 2009)
325 This is like `blame` and `log` in that it runs offline and is up to date only as of the last time you communicated with the Subversion server.
327 #### Ignoring What Subversion Ignores ####
329 If you clone a Subversion repository that has `svn:ignore` properties set anywhere, you’ll likely want to set corresponding `.gitignore` files so you don’t accidentally commit files that you shouldn’t. `git svn` has two commands to help with this issue. The first is `git svn create-ignore`, which automatically creates corresponding `.gitignore` files for you so your next commit can include them.
331 The second command is `git svn show-ignore`, which prints to stdout the lines you need to put in a `.gitignore` file so you can redirect the output into your project exclude file:
333         $ git svn show-ignore > .git/info/exclude
335 That way, you don’t litter the project with `.gitignore` files. This is a good option if you’re the only Git user on a Subversion team, and your teammates don’t want `.gitignore` files in the project.
337 ### Git-Svn Summary ###
339 The `git svn` tools are useful if you’re stuck with a Subversion server for now or are otherwise in a development environment that necessitates running a Subversion server. You should consider it crippled Git, however, or you’ll hit issues in translation that may confuse you and your collaborators. To stay out of trouble, try to follow these guidelines:
341 * Keep a linear Git history that doesn’t contain merge commits made by `git merge`. Rebase any work you do outside of your mainline branch back onto it; don’t merge it in.
342 * Don’t set up and collaborate on a separate Git server. Possibly have one to speed up clones for new developers, but don’t push anything to it that doesn’t have a `git-svn-id` entry. You may even want to add a `pre-receive` hook that checks each commit message for a `git-svn-id` and rejects pushes that contain commits without it.
344 If you follow those guidelines, working with a Subversion server can be more bearable. However, if it’s possible to move to a real Git server, doing so can gain your team a lot more.
346 ## Migrating to Git ##
348 If you have an existing codebase in another VCS but you’ve decided to start using Git, you must migrate your project one way or another. This section goes over some importers that are included with Git for common systems and then demonstrates how to develop your own custom importer.
350 ### Importing ###
352 You’ll learn how to import data from two of the bigger professionally used SCM systems — Subversion and Perforce — both because they make up the majority of users I hear of who are currently switching, and because high-quality tools for both systems are distributed with Git.
354 ### Subversion ###
356 If you read the previous section about using `git svn`, you can easily use those instructions to `git svn clone` a repository; then, stop using the Subversion server, push to a new Git server, and start using that. If you want the history, you can accomplish that as quickly as you can pull the data out of the Subversion server (which may take a while).
358 However, the import isn’t perfect; and because it will take so long, you may as well do it right. The first problem is the author information. In Subversion, each person committing has a user on the system who is recorded in the commit information. The examples in the previous section show `schacon` in some places, such as the `blame` output and the `git svn log`. If you want to map this to better Git author data, you need a mapping from the Subversion users to the Git authors. Create a file called `users.txt` that has this mapping in a format like this:
360         schacon = Scott Chacon <schacon@geemail.com>
361         selse = Someo Nelse <selse@geemail.com>
363 To get a list of the author names that SVN uses, you can run this:
365         $ svn log --xml | grep author | sort -u | perl -pe 's/.>(.?)<./$1 = /'
367 That gives you the log output in XML format — you can look for the authors, create a unique list, and then strip out the XML. (Obviously this only works on a machine with `grep`, `sort`, and `perl` installed.) Then, redirect that output into your users.txt file so you can add the equivalent Git user data next to each entry.
369 You can provide this file to `git svn` to help it map the author data more accurately. You can also tell `git svn` not to include the metadata that Subversion normally imports, by passing `--no-metadata` to the `clone` or `init` command. This makes your `import` command look like this:
371         $ git-svn clone http://my-project.googlecode.com/svn/ \
372               --authors-file=users.txt --no-metadata -s my_project
374 Now you should have a nicer Subversion import in your `my_project` directory. Instead of commits that look like this
376         commit 37efa680e8473b615de980fa935944215428a35a
377         Author: schacon <schacon@4c93b258-373f-11de-be05-5f7a86268029>
378         Date:   Sun May 3 00:12:22 2009 +0000
380             fixed install - go to trunk
382             git-svn-id: https://my-project.googlecode.com/svn/trunk@94 4c93b258-373f-11de-
383             be05-5f7a86268029
384 they look like this:
386         commit 03a8785f44c8ea5cdb0e8834b7c8e6c469be2ff2
387         Author: Scott Chacon <schacon@geemail.com>
388         Date:   Sun May 3 00:12:22 2009 +0000
390             fixed install - go to trunk
392 Not only does the Author field look a lot better, but the `git-svn-id` is no longer there, either.
394 You need to do a bit of `post-import` cleanup. For one thing, you should clean up the weird references that `git svn` set up. First you’ll move the tags so they’re actual tags rather than strange remote branches, and then you’ll move the rest of the branches so they’re local.
396 To move the tags to be proper Git tags, run
398         $ cp -Rf .git/refs/remotes/tags/* .git/refs/tags/
399         $ rm -Rf .git/refs/remotes/tags
401 This takes the references that were remote branches that started with `tag/` and makes them real (lightweight) tags.
403 Next, move the rest of the references under `refs/remotes` to be local branches:
405         $ cp -Rf .git/refs/remotes/* .git/refs/heads/
406         $ rm -Rf .git/refs/remotes
408 Now all the old branches are real Git branches and all the old tags are real Git tags. The last thing to do is add your new Git server as a remote and push to it. Because you want all your branches and tags to go up, you can run this:
410         $ git push origin --all
412 All your branches and tags should be on your new Git server in a nice, clean import.
414 ### Perforce ###
416 The next system you’ll look at importing from is Perforce. A Perforce importer is also distributed with Git, but only in the `contrib` section of the source code — it isn’t available by default like `git svn`. To run it, you must get the Git source code, which you can download from git.kernel.org:
418         $ git clone git://git.kernel.org/pub/scm/git/git.git
419         $ cd git/contrib/fast-import
421 In this `fast-import` directory, you should find an executable Python script named `git-p4`. You must have Python and the `p4` tool installed on your machine for this import to work. For example, you’ll import the Jam project from the Perforce Public Depot. To set up your client, you must export the P4PORT environment variable to point to the Perforce depot:
423         $ export P4PORT=public.perforce.com:1666
425 Run the `git-p4 clone` command to import the Jam project from the Perforce server, supplying the depot and project path and the path into which you want to import the project:
427         $ git-p4 clone //public/jam/src@all /opt/p4import
428         Importing from //public/jam/src@all into /opt/p4import
429         Reinitialized existing Git repository in /opt/p4import/.git/
430         Import destination: refs/remotes/p4/master
431         Importing revision 4409 (100%)
433 If you go to the `/opt/p4import` directory and run `git log`, you can see your imported work:
435         $ git log -2
436         commit 1fd4ec126171790efd2db83548b85b1bbbc07dc2
437         Author: Perforce staff <support@perforce.com>
438         Date:   Thu Aug 19 10:18:45 2004 -0800
440             Drop 'rc3' moniker of jam-2.5.  Folded rc2 and rc3 RELNOTES into
441             the main part of the document.  Built new tar/zip balls.
443             Only 16 months later.
445             [git-p4: depot-paths = "//public/jam/src/": change = 4409]
447         commit ca8870db541a23ed867f38847eda65bf4363371d
448         Author: Richard Geiger <rmg@perforce.com>
449         Date:   Tue Apr 22 20:51:34 2003 -0800
451             Update derived jamgram.c
453             [git-p4: depot-paths = "//public/jam/src/": change = 3108]
455 You can see the `git-p4` identifier in each commit. It’s fine to keep that identifier there, in case you need to reference the Perforce change number later. However, if you’d like to remove the identifier, now is the time to do so — before you start doing work on the new repository. You can use `git filter-branch` to remove the identifier strings en masse:
457         $ git filter-branch --msg-filter '
458                 sed -e "/^\[git-p4:/d"
459         '
460         Rewrite 1fd4ec126171790efd2db83548b85b1bbbc07dc2 (123/123)
461         Ref 'refs/heads/master' was rewritten
463 If you run `git log`, you can see that all the SHA-1 checksums for the commits have changed, but the `git-p4` strings are no longer in the commit messages:
465         $ git log -2
466         commit 10a16d60cffca14d454a15c6164378f4082bc5b0
467         Author: Perforce staff <support@perforce.com>
468         Date:   Thu Aug 19 10:18:45 2004 -0800
470             Drop 'rc3' moniker of jam-2.5.  Folded rc2 and rc3 RELNOTES into
471             the main part of the document.  Built new tar/zip balls.
473             Only 16 months later.
475         commit 2b6c6db311dd76c34c66ec1c40a49405e6b527b2
476         Author: Richard Geiger <rmg@perforce.com>
477         Date:   Tue Apr 22 20:51:34 2003 -0800
479             Update derived jamgram.c
481 Your import is ready to push up to your new Git server.
483 ### A Custom Importer ###
485 If your system isn’t Subversion or Perforce, you should look for an importer online — quality importers are available for CVS, Clear Case, Visual Source Safe, even a directory of archives. If none of these tools works for you, you have a rarer tool, or you otherwise need a more custom importing process, you should use `git fast-import`. This command reads simple instructions from stdin to write specific Git data. It’s much easier to create Git objects this way than to run the raw Git commands or try to write the raw objects (see Chapter 9 for more information). This way, you can write an import script that reads the necessary information out of the system you’re importing from and prints straightforward instructions to stdout. You can then run this program and pipe its output through `git fast-import`.
487 To quickly demonstrate, you’ll write a simple importer. Suppose you work in current, you back up your project by occasionally copying the directory into a time-stamped `back_YYYY_MM_DD` backup directory, and you want to import this into Git. Your directory structure looks like this:
489         $ ls /opt/import_from
490         back_2009_01_02
491         back_2009_01_04
492         back_2009_01_14
493         back_2009_02_03
494         current
496 In order to import a Git directory, you need to review how Git stores its data. As you may remember, Git is fundamentally a linked list of commit objects that point to a snapshot of content. All you have to do is tell `fast-import` what the content snapshots are, what commit data points to them, and the order they go in. Your strategy will be to go through the snapshots one at a time and create commits with the contents of each directory, linking each commit back to the previous one.
498 As you did in the "An Example Git Enforced Policy" section of Chapter 7, we’ll write this in Ruby, because it’s what I generally work with and it tends to be easy to read. You can write this example pretty easily in anything you’re familiar with — it just needs to print the appropriate information to stdout.
500 To begin, you’ll change into the target directory and identify every subdirectory, each of which is a snapshot that you want to import as a commit. You’ll change into each subdirectory and print the commands necessary to export it. Your basic main loop looks like this:
502         last_mark = nil
504         # loop through the directories
505         Dir.chdir(ARGV[0]) do
506           Dir.glob("*").each do |dir|
507             next if File.file?(dir)
509             # move into the target directory
510             Dir.chdir(dir) do 
511               last_mark = print_export(dir, last_mark)
512             end
513           end
514         end
516 You run `print_export` inside each directory, which takes the manifest and mark of the previous snapshot and returns the manifest and mark of this one; that way, you can link them properly. "Mark" is the `fast-import` term for an identifier you give to a commit; as you create commits, you give each one a mark that you can use to link to it from other commits. So, the first thing to do in your `print_export` method is generate a mark from the directory name:
518         mark = convert_dir_to_mark(dir)
520 You’ll do this by creating an array of directories and using the index value as the mark, because a mark must be an integer. Your method looks like this:
522         $marks = []
523         def convert_dir_to_mark(dir)
524           if !$marks.include?(dir)
525             $marks << dir
526           end
527           ($marks.index(dir) + 1).to_s
528         end
530 Now that you have an integer representation of your commit, you need a date for the commit metadata. Because the date is expressed in the name of the directory, you’ll parse it out. The next line in your `print_export` file is
532         date = convert_dir_to_date(dir)
534 where `convert_dir_to_date` is defined as
536         def convert_dir_to_date(dir)
537           if dir == 'current'
538             return Time.now().to_i
539           else
540             dir = dir.gsub('back_', '')
541             (year, month, day) = dir.split('_')
542             return Time.local(year, month, day).to_i
543           end
544         end
546 That returns an integer value for the date of each directory. The last piece of meta-information you need for each commit is the committer data, which you hardcode in a global variable:
548         $author = 'Scott Chacon <schacon@example.com>'
550 Now you’re ready to begin printing out the commit data for your importer. The initial information states that you’re defining a commit object and what branch it’s on, followed by the mark you’ve generated, the committer information and commit message, and then the previous commit, if any. The code looks like this:
552         # print the import information
553         puts 'commit refs/heads/master'
554         puts 'mark :' + mark
555         puts "committer #{$author} #{date} -0700"
556         export_data('imported from ' + dir)
557         puts 'from :' + last_mark if last_mark
559 You hardcode the time zone (-0700) because doing so is easy. If you’re importing from another system, you must specify the time zone as an offset. 
560 The commit message must be expressed in a special format:
562         data (size)\n(contents)
564 The format consists of the word data, the size of the data to be read, a newline, and finally the data. Because you need to use the same format to specify the file contents later, you create a helper method, `export_data`:
566         def export_data(string)
567           print "data #{string.size}\n#{string}"
568         end
570 All that’s left is to specify the file contents for each snapshot. This is easy, because you have each one in a directory — you can print out the `deleteall` command followed by the contents of each file in the directory. Git will then record each snapshot appropriately:
572         puts 'deleteall'
573         Dir.glob("**/*").each do |file|
574           next if !File.file?(file)
575           inline_data(file)
576         end
578 Note:   Because many systems think of their revisions as changes from one commit to another, fast-import can also take commands with each commit to specify which files have been added, removed, or modified and what the new contents are. You could calculate the differences between snapshots and provide only this data, but doing so is more complex — you may as well give Git all the data and let it figure it out. If this is better suited to your data, check the `fast-import` man page for details about how to provide your data in this manner.
580 The format for listing the new file contents or specifying a modified file with the new contents is as follows:
582         M 644 inline path/to/file
583         data (size)
584         (file contents)
586 Here, 644 is the mode (if you have executable files, you need to detect and specify 755 instead), and inline says you’ll list the contents immediately after this line. Your `inline_data` method looks like this:
588         def inline_data(file, code = 'M', mode = '644')
589           content = File.read(file)
590           puts "#{code} #{mode} inline #{file}"
591           export_data(content)
592         end
594 You reuse the `export_data` method you defined earlier, because it’s the same as the way you specified your commit message data. 
596 The last thing you need to do is to return the current mark so it can be passed to the next iteration:
598         return mark
600 That’s it. If you run this script, you’ll get content that looks something like this:
602         $ ruby import.rb /opt/import_from 
603         commit refs/heads/master
604         mark :1
605         committer Scott Chacon <schacon@geemail.com> 1230883200 -0700
606         data 29
607         imported from back_2009_01_02deleteall
608         M 644 inline file.rb
609         data 12
610         version two
611         commit refs/heads/master
612         mark :2
613         committer Scott Chacon <schacon@geemail.com> 1231056000 -0700
614         data 29
615         imported from back_2009_01_04from :1
616         deleteall
617         M 644 inline file.rb
618         data 14
619         version three
620         M 644 inline new.rb
621         data 16
622         new version one
623         (...)
625 To run the importer, pipe this output through `git fast-import` while in the Git directory you want to import into. You can create a new directory and then run `git init` in it for a starting point, and then run your script:
627         $ git init
628         Initialized empty Git repository in /opt/import_to/.git/
629         $ ruby import.rb /opt/import_from | git fast-import
630         git-fast-import statistics:
631         ---------------------------------------------------------------------
632         Alloc'd objects:       5000
633         Total objects:           18 (         1 duplicates                  )
634               blobs  :            7 (         1 duplicates          0 deltas)
635               trees  :            6 (         0 duplicates          1 deltas)
636               commits:            5 (         0 duplicates          0 deltas)
637               tags   :            0 (         0 duplicates          0 deltas)
638         Total branches:           1 (         1 loads     )
639               marks:           1024 (         5 unique    )
640               atoms:              3
641         Memory total:          2255 KiB
642                pools:          2098 KiB
643              objects:           156 KiB
644         ---------------------------------------------------------------------
645         pack_report: getpagesize()            =       4096
646         pack_report: core.packedGitWindowSize =   33554432
647         pack_report: core.packedGitLimit      =  268435456
648         pack_report: pack_used_ctr            =          9
649         pack_report: pack_mmap_calls          =          5
650         pack_report: pack_open_windows        =          1 /          1
651         pack_report: pack_mapped              =       1356 /       1356
652         ---------------------------------------------------------------------
654 As you can see, when it completes successfully, it gives you a bunch of statistics about what it accomplished. In this case, you imported 18 objects total for 5 commits into 1 branch. Now, you can run `git log` to see your new history:
656         $ git log -2
657         commit 10bfe7d22ce15ee25b60a824c8982157ca593d41
658         Author: Scott Chacon <schacon@example.com>
659         Date:   Sun May 3 12:57:39 2009 -0700
661             imported from current
663         commit 7e519590de754d079dd73b44d695a42c9d2df452
664         Author: Scott Chacon <schacon@example.com>
665         Date:   Tue Feb 3 01:00:00 2009 -0700
667             imported from back_2009_02_03
669 There you go — a nice, clean Git repository. It’s important to note that nothing is checked out — you don’t have any files in your working directory at first. To get them, you must reset your branch to where `master` is now:
671         $ ls
672         $ git reset --hard master
673         HEAD is now at 10bfe7d imported from current
674         $ ls
675         file.rb  lib
677 You can do a lot more with the `fast-import` tool — handle different modes, binary data, multiple branches and merging, tags, progress indicators, and more. A number of examples of more complex scenarios are available in the `contrib/fast-import` directory of the Git source code; one of the better ones is the `git-p4` script I just covered.
679 ## Summary ##
681 You should feel comfortable using Git with Subversion or importing nearly any existing repository into a new Git one without losing data. The next chapter will cover the raw internals of Git so you can craft every single byte, if need be.