1git for CVS users 2================= 3 4So you're a CVS user. That's OK, it's a treatable condition. The job of 5this document is to put you on the road to recovery, by helping you 6convert an existing cvs repository to git, and by showing you how to use a 7git repository in a cvs-like fashion. 8 9Some basic familiarity with git is required. This 10link:tutorial.html[tutorial introduction to git] should be sufficient. 11 12First, note some ways that git differs from CVS: 13 14 * Commits are atomic and project-wide, not per-file as in CVS. 15 16 * Offline work is supported: you can make multiple commits locally, 17 then submit them when you're ready. 18 19 * Branching is fast and easy. 20 21 * Every working tree contains a repository with a full copy of the 22 project history, and no repository is inherently more important than 23 any other. However, you can emulate the CVS model by designating a 24 single shared repository which people can synchronize with; see below 25 for details. 26 27 * Since every working tree contains a repository, a commit in your 28 private repository will not publish your changes; it will only create 29 a revision. You have to "push" your changes to a public repository to 30 make them visible to others. 31 32Importing a CVS archive 33----------------------- 34 35First, install version 2.1 or higher of cvsps from 36link:http://www.cobite.com/cvsps/[http://www.cobite.com/cvsps/] and make 37sure it is in your path. The magic command line is then 38 39------------------------------------------- 40$ git cvsimport -v -d <cvsroot> -C <destination> <module> 41------------------------------------------- 42 43This puts a git archive of the named CVS module in the directory 44<destination>, which will be created if necessary. The -v option makes 45the conversion script very chatty. 46 47The import checks out from CVS every revision of every file. Reportedly 48cvsimport can average some twenty revisions per second, so for a 49medium-sized project this should not take more than a couple of minutes. 50Larger projects or remote repositories may take longer. 51 52The main trunk is stored in the git branch named `origin`, and additional 53CVS branches are stored in git branches with the same names. The most 54recent version of the main trunk is also left checked out on the `master` 55branch, so you can start adding your own changes right away. 56 57The import is incremental, so if you call it again next month it will 58fetch any CVS updates that have been made in the meantime. For this to 59work, you must not modify the imported branches; instead, create new 60branches for your own changes, and merge in the imported branches as 61necessary. 62 63Development Models 64------------------ 65 66CVS users are accustomed to giving a group of developers commit access to 67a common repository. In the next section we'll explain how to do this 68with git. However, the distributed nature of git allows other development 69models, and you may want to first consider whether one of them might be a 70better fit for your project. 71 72For example, you can choose a single person to maintain the project's 73primary public repository. Other developers then clone this repository 74and each work in their own clone. When they have a series of changes that 75they're happy with, they ask the maintainer to pull from the branch 76containing the changes. The maintainer reviews their changes and pulls 77them into the primary repository, which other developers pull from as 78necessary to stay coordinated. The Linux kernel and other projects use 79variants of this model. 80 81With a small group, developers may just pull changes from each other's 82repositories without the need for a central maintainer. 83 84Emulating the CVS Development Model 85----------------------------------- 86 87Start with an ordinary git working directory containing the project, and 88remove the checked-out files, keeping just the bare .git directory: 89 90------------------------------------------------ 91$ mv project/.git /pub/repo.git 92$ rm -r project/ 93------------------------------------------------ 94 95Next, give every team member read/write access to this repository. One 96easy way to do this is to give all the team members ssh access to the 97machine where the repository is hosted. If you don't want to give them a 98full shell on the machine, there is a restricted shell which only allows 99users to do git pushes and pulls; see gitlink:git-shell[1]. 100 101Put all the committers in the same group, and make the repository 102writable by that group: 103 104------------------------------------------------ 105$ chgrp -R $group repo.git 106$ find repo.git -mindepth 1 -type d |xargs chmod ug+rwx,g+s 107$ GIT_DIR=repo.git git repo-config core.sharedrepository true 108------------------------------------------------ 109 110Make sure committers have a umask of at most 027, so that the directories 111they create are writable and searchable by other group members. 112 113Suppose this repository is now set up in /pub/repo.git on the host 114foo.com. Then as an individual committer you can clone the shared 115repository: 116 117------------------------------------------------ 118$ git clone foo.com:/pub/repo.git/ my-project 119$ cd my-project 120------------------------------------------------ 121 122and hack away. The equivalent of `cvs update` is 123 124------------------------------------------------ 125$ git pull origin 126------------------------------------------------ 127 128which merges in any work that others might have done since the clone 129operation. 130 131[NOTE] 132================================ 133The first `git clone` places the following in the 134`my-project/.git/remotes/origin` file, and that's why the previous step 135and the next step both work. 136------------ 137URL: foo.com:/pub/project.git/ my-project 138Pull: master:origin 139------------ 140================================ 141 142You can update the shared repository with your changes using: 143 144------------------------------------------------ 145$ git push origin master 146------------------------------------------------ 147 148If someone else has updated the repository more recently, `git push`, like 149`cvs commit`, will complain, in which case you must pull any changes 150before attempting the push again. 151 152In the `git push` command above we specify the name of the remote branch 153to update (`master`). If we leave that out, `git push` tries to update 154any branches in the remote repository that have the same name as a branch 155in the local repository. So the last `push` can be done with either of: 156 157------------ 158$ git push origin 159$ git push repo.shared.xz:/pub/scm/project.git/ 160------------ 161 162as long as the shared repository does not have any branches 163other than `master`. 164 165[NOTE] 166============ 167Because of this behavior, if the shared repository and the developer's 168repository both have branches named `origin`, then a push like the above 169attempts to update the `origin` branch in the shared repository from the 170developer's `origin` branch. The results may be unexpected, so it's 171usually best to remove any branch named `origin` from the shared 172repository. 173============ 174 175Advanced Shared Repository Management 176------------------------------------- 177 178Git allows you to specify scripts called "hooks" to be run at certain 179points. You can use these, for example, to send all commits to the shared 180repository to a mailing list. See link:hooks.html[Hooks used by git]. 181 182You can enforce finer grained permissions using update hooks. See 183link:howto/update-hook-example.txt[Controlling access to branches using 184update hooks]. 185 186CVS annotate 187------------ 188 189So, something has gone wrong, and you don't know whom to blame, and 190you're an ex-CVS user and used to do "cvs annotate" to see who caused 191the breakage. You're looking for the "git annotate", and it's just 192claiming not to find such a script. You're annoyed. 193 194Yes, that's right. Core git doesn't do "annotate", although it's 195technically possible, and there are at least two specialized scripts out 196there that can be used to get equivalent information (see the git 197mailing list archives for details). 198 199git has a couple of alternatives, though, that you may find sufficient 200or even superior depending on your use. One is called "git-whatchanged" 201(for obvious reasons) and the other one is called "pickaxe" ("a tool for 202the software archaeologist"). 203 204The "git-whatchanged" script is a truly trivial script that can give you 205a good overview of what has changed in a file or a directory (or an 206arbitrary list of files or directories). The "pickaxe" support is an 207additional layer that can be used to further specify exactly what you're 208looking for, if you already know the specific area that changed. 209 210Let's step back a bit and think about the reason why you would 211want to do "cvs annotate a-file.c" to begin with. 212 213You would use "cvs annotate" on a file when you have trouble 214with a function (or even a single "if" statement in a function) 215that happens to be defined in the file, which does not do what 216you want it to do. And you would want to find out why it was 217written that way, because you are about to modify it to suit 218your needs, and at the same time you do not want to break its 219current callers. For that, you are trying to find out why the 220original author did things that way in the original context. 221 222Many times, it may be enough to see the commit log messages of 223commits that touch the file in question, possibly along with the 224patches themselves, like this: 225 226 $ git-whatchanged -p a-file.c 227 228This will show log messages and patches for each commit that 229touches a-file. 230 231This, however, may not be very useful when this file has many 232modifications that are not related to the piece of code you are 233interested in. You would see many log messages and patches that 234do not have anything to do with the piece of code you are 235interested in. As an example, assuming that you have this piece 236of code that you are interested in in the HEAD version: 237 238 if (frotz) { 239 nitfol(); 240 } 241 242you would use git-rev-list and git-diff-tree like this: 243 244 $ git-rev-list HEAD | 245 git-diff-tree --stdin -v -p -S'if (frotz) { 246 nitfol(); 247 }' 248 249We have already talked about the "\--stdin" form of git-diff-tree 250command that reads the list of commits and compares each commit 251with its parents (otherwise you should go back and read the tutorial). 252The git-whatchanged command internally runs 253the equivalent of the above command, and can be used like this: 254 255 $ git-whatchanged -p -S'if (frotz) { 256 nitfol(); 257 }' 258 259When the -S option is used, git-diff-tree command outputs 260differences between two commits only if one tree has the 261specified string in a file and the corresponding file in the 262other tree does not. The above example looks for a commit that 263has the "if" statement in it in a file, but its parent commit 264does not have it in the same shape in the corresponding file (or 265the other way around, where the parent has it and the commit 266does not), and the differences between them are shown, along 267with the commit message (thanks to the -v flag). It does not 268show anything for commits that do not touch this "if" statement. 269 270Also, in the original context, the same statement might have 271appeared at first in a different file and later the file was 272renamed to "a-file.c". CVS annotate would not help you to go 273back across such a rename, but git would still help you in such 274a situation. For that, you can give the -C flag to 275git-diff-tree, like this: 276 277 $ git-whatchanged -p -C -S'if (frotz) { 278 nitfol(); 279 }' 280 281When the -C flag is used, file renames and copies are followed. 282So if the "if" statement in question happens to be in "a-file.c" 283in the current HEAD commit, even if the file was originally 284called "o-file.c" and then renamed in an earlier commit, or if 285the file was created by copying an existing "o-file.c" in an 286earlier commit, you will not lose track. If the "if" statement 287did not change across such a rename or copy, then the commit that 288does rename or copy would not show in the output, and if the 289"if" statement was modified while the file was still called 290"o-file.c", it would find the commit that changed the statement 291when it was in "o-file.c". 292 293NOTE: The current version of "git-diff-tree -C" is not eager 294 enough to find copies, and it will miss the fact that a-file.c 295 was created by copying o-file.c unless o-file.c was somehow 296 changed in the same commit. 297 298You can use the --pickaxe-all flag in addition to the -S flag. 299This causes the differences from all the files contained in 300those two commits, not just the differences between the files 301that contain this changed "if" statement: 302 303 $ git-whatchanged -p -C -S'if (frotz) { 304 nitfol(); 305 }' --pickaxe-all 306 307NOTE: This option is called "--pickaxe-all" because -S 308 option is internally called "pickaxe", a tool for software 309 archaeologists.