]>
Commit | Line | Data |
---|---|---|
72e9340c | 1 | git for CVS users |
fcbfd5a6 LT |
2 | ================= |
3 | ||
4 | Ok, so you're a CVS user. That's ok, it's a treatable condition, and the | |
5 | first step to recovery is admitting you have a problem. The fact that | |
6 | you are reading this file means that you may be well on that path | |
7 | already. | |
8 | ||
9 | The thing about CVS is that it absolutely sucks as a source control | |
72e9340c | 10 | manager, and you'll thus be happy with almost anything else. git, |
8db9307c | 11 | however, may be a bit 'too' different (read: "good") for your taste, and |
fcbfd5a6 LT |
12 | does a lot of things differently. |
13 | ||
14 | One particular suckage of CVS is very hard to work around: CVS is | |
8db9307c JH |
15 | basically a tool for tracking 'file' history, while git is a tool for |
16 | tracking 'project' history. This sometimes causes problems if you are | |
1cc92ff6 | 17 | used to doing very strange things in CVS, in particular if you're doing |
72e9340c | 18 | things like making branches of just a subset of the project. git can't |
fcbfd5a6 LT |
19 | track that, since git never tracks things on the level of an individual |
20 | file, only on the whole project level. | |
21 | ||
22 | The good news is that most people don't do that, and in fact most sane | |
23 | people think it's a bug in CVS that makes it tag (and check in changes) | |
24 | one file at a time. So most projects you'll ever see will use CVS | |
8db9307c | 25 | 'as if' it was sane. In which case you'll find it very easy indeed to |
f73ae1fc | 26 | move over to git. |
fcbfd5a6 | 27 | |
8db9307c JH |
28 | First off: this is not a git tutorial. See |
29 | link:tutorial.html[Documentation/tutorial.txt] for how git | |
30 | actually works. This is more of a random collection of gotcha's | |
31 | and notes on converting from CVS to git. | |
fcbfd5a6 LT |
32 | |
33 | Second: CVS has the notion of a "repository" as opposed to the thing | |
34 | that you're actually working in (your working directory, or your | |
72e9340c | 35 | "checked out tree"). git does not have that notion at all, and all git |
8db9307c | 36 | working directories 'are' the repositories. However, you can easily |
fcbfd5a6 LT |
37 | emulate the CVS model by having one special "global repository", which |
38 | people can synchronize with. See details later, but in the meantime | |
1cc92ff6 TM |
39 | just keep in mind that with git, every checked out working tree will |
40 | have a full revision control history of its own. | |
fcbfd5a6 LT |
41 | |
42 | ||
43 | Importing a CVS archive | |
44 | ----------------------- | |
45 | ||
46 | Ok, you have an old project, and you want to at least give git a chance | |
47 | to see how it performs. The first thing you want to do (after you've | |
48 | gone through the git tutorial, and generally familiarized yourself with | |
49 | how to commit stuff etc in git) is to create a git'ified version of your | |
50 | CVS archive. | |
51 | ||
72e9340c | 52 | Happily, that's very easy indeed. git will do it for you, although git |
fcbfd5a6 LT |
53 | will need the help of a program called "cvsps": |
54 | ||
55 | http://www.cobite.com/cvsps/ | |
56 | ||
57 | which is not actually related to git at all, but which makes CVS usage | |
58 | look almost sane (ie you almost certainly want to have it even if you | |
8db9307c | 59 | decide to stay with CVS). However, git will want 'at least' version 2.1 |
fcbfd5a6 LT |
60 | of cvsps (available at the address above), and in fact will currently |
61 | refuse to work with anything else. | |
62 | ||
63 | Once you've gotten (and installed) cvsps, you may or may not want to get | |
64 | any more familiar with it, but make sure it is in your path. After that, | |
65 | the magic command line is | |
66 | ||
2c38fe4c | 67 | git cvsimport -v -d <cvsroot> -C <destination> <module> |
fcbfd5a6 LT |
68 | |
69 | which will do exactly what you'd think it does: it will create a git | |
e694dbab MU |
70 | archive of the named CVS module. The new archive will be created in the |
71 | subdirectory named <destination>; it'll be created if it doesn't exist. | |
72 | Default is the local directory. | |
fcbfd5a6 | 73 | |
1cc92ff6 TM |
74 | It can take some time to actually do the conversion for a large archive |
75 | since it involves checking out from CVS every revision of every file, | |
6c9a0dc2 MU |
76 | and the conversion script is reasonably chatty unless you omit the '-v' |
77 | option, but on some not very scientific tests it averaged about twenty | |
78 | revisions per second, so a medium-sized project should not take more | |
79 | than a couple of minutes. For larger projects or remote repositories, | |
80 | the process may take longer. | |
fcbfd5a6 | 81 | |
6c9a0dc2 MU |
82 | After the (initial) import is done, the CVS archive's current head |
83 | revision will be checked out -- thus, you can start adding your own | |
84 | changes right away. | |
e694dbab MU |
85 | |
86 | The import is incremental, i.e. if you call it again next month it'll | |
6c9a0dc2 MU |
87 | fetch any CVS updates that have been happening in the meantime. The |
88 | cut-off is date-based, so don't change the branches that were imported | |
89 | from CVS. | |
90 | ||
91 | You can merge those updates (or, in fact, a different CVS branch) into | |
92 | your main branch: | |
e694dbab | 93 | |
7da71deb | 94 | git resolve HEAD origin "merge with current CVS HEAD" |
e694dbab | 95 | |
6c9a0dc2 MU |
96 | The HEAD revision from CVS is named "origin", not "HEAD", because git |
97 | already uses "HEAD". (If you don't like 'origin', use cvsimport's | |
98 | '-o' option to change it.) | |
99 | ||
fcbfd5a6 LT |
100 | |
101 | Emulating CVS behaviour | |
102 | ----------------------- | |
103 | ||
104 | ||
7da71deb JS |
105 | So, by now you are convinced you absolutely want to work with git, but |
106 | at the same time you absolutely have to have a central repository. | |
107 | Step back and think again. Okay, you still need a single central | |
108 | repository? There are several ways to go about that: | |
fcbfd5a6 | 109 | |
7da71deb JS |
110 | 1. Designate a person responsible to pull all branches. Make the |
111 | repository of this person public, and make every team member | |
112 | pull regularly from it. | |
113 | ||
114 | 2. Set up a public repository with read/write access for every team | |
3206014a JH |
115 | member. Use "git pull/push" as you used "cvs update/commit". Be |
116 | sure that your repository is up to date before pushing, just | |
117 | like you used to do with "cvs commit"; your push will fail if | |
118 | what you are pushing is not up to date. | |
7da71deb JS |
119 | |
120 | 3. Make the repository of every team member public. It is the | |
121 | responsibility of each single member to pull from every other | |
122 | team member. | |
123 | ||
fcbfd5a6 LT |
124 | |
125 | CVS annotate | |
126 | ------------ | |
b0bf8f24 | 127 | |
3c65eb18 LT |
128 | So, something has gone wrong, and you don't know whom to blame, and |
129 | you're an ex-CVS user and used to do "cvs annotate" to see who caused | |
130 | the breakage. You're looking for the "git annotate", and it's just | |
131 | claiming not to find such a script. You're annoyed. | |
132 | ||
133 | Yes, that's right. Core git doesn't do "annotate", although it's | |
134 | technically possible, and there are at least two specialized scripts out | |
135 | there that can be used to get equivalent information (see the git | |
136 | mailing list archives for details). | |
137 | ||
72e9340c | 138 | git has a couple of alternatives, though, that you may find sufficient |
3c65eb18 LT |
139 | or even superior depending on your use. One is called "git-whatchanged" |
140 | (for obvious reasons) and the other one is called "pickaxe" ("a tool for | |
141 | the software archeologist"). | |
142 | ||
143 | The "git-whatchanged" script is a truly trivial script that can give you | |
144 | a good overview of what has changed in a file or a directory (or an | |
145 | arbitrary list of files or directories). The "pickaxe" support is an | |
146 | additional layer that can be used to further specify exactly what you're | |
147 | looking for, if you already know the specific area that changed. | |
b0bf8f24 JH |
148 | |
149 | Let's step back a bit and think about the reason why you would | |
150 | want to do "cvs annotate a-file.c" to begin with. | |
151 | ||
152 | You would use "cvs annotate" on a file when you have trouble | |
153 | with a function (or even a single "if" statement in a function) | |
154 | that happens to be defined in the file, which does not do what | |
155 | you want it to do. And you would want to find out why it was | |
156 | written that way, because you are about to modify it to suit | |
157 | your needs, and at the same time you do not want to break its | |
158 | current callers. For that, you are trying to find out why the | |
159 | original author did things that way in the original context. | |
160 | ||
161 | Many times, it may be enough to see the commit log messages of | |
162 | commits that touch the file in question, possibly along with the | |
163 | patches themselves, like this: | |
164 | ||
165 | $ git-whatchanged -p a-file.c | |
166 | ||
167 | This will show log messages and patches for each commit that | |
168 | touches a-file. | |
169 | ||
170 | This, however, may not be very useful when this file has many | |
171 | modifications that are not related to the piece of code you are | |
172 | interested in. You would see many log messages and patches that | |
173 | do not have anything to do with the piece of code you are | |
174 | interested in. As an example, assuming that you have this piece | |
7da71deb | 175 | of code that you are interested in in the HEAD version: |
b0bf8f24 JH |
176 | |
177 | if (frotz) { | |
178 | nitfol(); | |
179 | } | |
180 | ||
181 | you would use git-rev-list and git-diff-tree like this: | |
182 | ||
183 | $ git-rev-list HEAD | | |
184 | git-diff-tree --stdin -v -p -S'if (frotz) { | |
185 | nitfol(); | |
186 | }' | |
187 | ||
e1ccf53a | 188 | We have already talked about the "\--stdin" form of git-diff-tree |
b0bf8f24 | 189 | command that reads the list of commits and compares each commit |
6bad1902 JH |
190 | with its parents (otherwise you should go back and read the tutorial). |
191 | The git-whatchanged command internally runs | |
b0bf8f24 JH |
192 | the equivalent of the above command, and can be used like this: |
193 | ||
194 | $ git-whatchanged -p -S'if (frotz) { | |
195 | nitfol(); | |
196 | }' | |
197 | ||
198 | When the -S option is used, git-diff-tree command outputs | |
199 | differences between two commits only if one tree has the | |
200 | specified string in a file and the corresponding file in the | |
201 | other tree does not. The above example looks for a commit that | |
202 | has the "if" statement in it in a file, but its parent commit | |
203 | does not have it in the same shape in the corresponding file (or | |
204 | the other way around, where the parent has it and the commit | |
205 | does not), and the differences between them are shown, along | |
206 | with the commit message (thanks to the -v flag). It does not | |
207 | show anything for commits that do not touch this "if" statement. | |
208 | ||
209 | Also, in the original context, the same statement might have | |
210 | appeared at first in a different file and later the file was | |
211 | renamed to "a-file.c". CVS annotate would not help you to go | |
72e9340c | 212 | back across such a rename, but git would still help you in such |
b0bf8f24 JH |
213 | a situation. For that, you can give the -C flag to |
214 | git-diff-tree, like this: | |
215 | ||
216 | $ git-whatchanged -p -C -S'if (frotz) { | |
217 | nitfol(); | |
218 | }' | |
219 | ||
220 | When the -C flag is used, file renames and copies are followed. | |
221 | So if the "if" statement in question happens to be in "a-file.c" | |
222 | in the current HEAD commit, even if the file was originally | |
223 | called "o-file.c" and then renamed in an earlier commit, or if | |
224 | the file was created by copying an existing "o-file.c" in an | |
225 | earlier commit, you will not lose track. If the "if" statement | |
7da71deb | 226 | did not change across such a rename or copy, then the commit that |
b0bf8f24 JH |
227 | does rename or copy would not show in the output, and if the |
228 | "if" statement was modified while the file was still called | |
229 | "o-file.c", it would find the commit that changed the statement | |
230 | when it was in "o-file.c". | |
231 | ||
f73ae1fc | 232 | NOTE: The current version of "git-diff-tree -C" is not eager |
b0bf8f24 JH |
233 | enough to find copies, and it will miss the fact that a-file.c |
234 | was created by copying o-file.c unless o-file.c was somehow | |
df8baa42 | 235 | changed in the same commit. |
b0bf8f24 JH |
236 | |
237 | You can use the --pickaxe-all flag in addition to the -S flag. | |
238 | This causes the differences from all the files contained in | |
239 | those two commits, not just the differences between the files | |
240 | that contain this changed "if" statement: | |
241 | ||
242 | $ git-whatchanged -p -C -S'if (frotz) { | |
243 | nitfol(); | |
244 | }' --pickaxe-all | |
245 | ||
df8baa42 | 246 | NOTE: This option is called "--pickaxe-all" because -S |
b0bf8f24 | 247 | option is internally called "pickaxe", a tool for software |
df8baa42 | 248 | archaeologists. |