for Cold Turkey Quitters 2007
This is not a git manual. There is a lot of decent documentation available on the web, and all git commands are very well documented, so there is no need to repeat them here. This document explains step-by-step how to move your local changes in your CVS repositories to the newly created git repository. It assumes that you have a recent version of git installed on your system, and that you have familiarised your self with the basic git operations by reading the available git documentation at http://git.or.cz.
The first part of this guide is intended for repository administrators, and documents step-by-step how to freeze the CVS archive and convert it to a git archive.
The second part is intended for the individual developer, documenting how to set up git for personal use, and how to safely migrate all uncommitted changes from CVS to git.
Finally, I'm sure there are errors and/or semi correct statements in this document since I'm not a true git expert (yet). All suggestions and corrections are welcome!
The very nature of git makes it much more powerful than CVS. Git is allows for a lot more flexibility, leading to a different workflow than under CVS. Changing two both the source code revision software and the working paradigm at the same time is a recipe for trouble. It's not git itself that is causing the confusion, rather the new way how to use it really efficiently! To avoid having grumpy and confused users it's better to do one thing at a time. Start by changing from CVS to git, but keep the same working paradigm as before. When the users have become comfortable using git, you can implement more advanced things like restricted branches and multiple remotes.
The central master server is set up with a master branch, which is supposed to contain the latest, greatest, bleeding edge working development version of the code. This is the branch from which the developers should pull regularly, and only the repository maintainers have right to push to this branch. There will also be an experimental branch to which people can push code they want others to test. Furthermore there will be a testing branch which acts as a staging area for code to be included in the master branch. It's simple to have the code in the testing branch pulled once per night and run through a test suite. If all tests pass, the repository maintainers can merge with the master branch.
In addition to these standard branches, every user has the right to create and update their own branch on the master server. One ``official'' branch per developer is probably enough for most users. This simple setup allows users to regularly fetch all remote branches and communicate code sideways if they need to. If one branch is not enough, or if a developer has special requirements, each developer is allowed to have his/hers own repository on the server. This gives maximum flexibility to the users without losing too much transparency. The git web interface shows all repositories in a directory tree, so that everyone can follow the development cycle in all repositories.
$ mkdir /path/to/git $ chgrp gits /path/to/git $ chmod 775 /path/to/git $ chmod +s /path/to/git $ chmod +t /path/to/git
Then change to your CVSROOT and make the module(s) read-only. Make sure that the lock and history files remain in writable locations. These are configured in $CVSROOT/CVSROOT/config.
$ cd $CVSROOT $ chmod -R ugo-w myprojNow CVS commits are disabled and the repository frozen.
$ cd /path/to/git $ git-cvsimport -k -i -d /path/to/CVSROOT -C myproj myprojTo create a bare, shared repository we need to rename the git directory, fix the permissions and edit the git config file
$ mv myproj/.git myproj.git
$ rmdir myproj
$ chgrp -R gitusers myproj.git
$ chmod -R ug+w myproj.git
$ chmod +s myproj.git
# the hooks/ and info/ dirs are special since the control repository access
$ cd myproj.git; chgrp -R gitadmin hooks info config description
$ find -type d -exec chmod +s {} \;
Then edit the config file:
[core] repositoryformatversion = 0 filemode = true bare = true sharedrepository = 1 [receive] denyNonFastforwards = trueFinally edit the description file, adding a one line comment comment describing your project for gitweb.
$ crontab -e
1 1 * * * cd /path/to/git; find -name "*.git" -type d -exec \
/path/to/git/git-optimize.sh {} \;
1 3 1 * * cd /path/to/git; find -name "*.git" -type d -exec \
/path/to/git/git-optimize.sh {} \;
As a last point, it's a good idea to make sure that all users have a umask of at least 027 to make sure that the files and directories are accessible by all other developers.
$ git config --global user.name "Your Name" $ git config --global user.email "my@email.com"These options are written in /.gitconfig, and are used by git to ensure that your commit messages are sensible, since user names and mail addresses are not necessarily set properly on all machines. In addition you might want to enable the following options as well:
$ git config --global color.branch auto $ git config --global color.status auto $ git config --global color.diff falseIf you want to use some external (graphical) merge tool to resolve conflicts:
$ git config --global merge.tool meldMeld is a fantastic merge tool, and I strongly suggest you have a look at it. Other valid possibilities are kdiff3 and xxdiff (amongst others).
$ cvs updateThis makes sure that your files are in the right state. Files with local modifications will not be overwritten nor updated. If you have modified files that meanwhile have been modified on the master you will probably have a number of conflicts. You need to resolve these conflicts before continuing. Edit the files with conflicts and pick the correct pieces of code between the markers.
$ cvs diff -u -N >migration.diffThis file contains all the differences between the master and your working copy.
$ git clone <myserver>:/path/to/repos/myrepo.git $ cd aces2Optionally you can also specify the name of the new repository.
$ git branch migration CVS_migration_HEAD $ git checkout migration
$ patch -p0 < /path/to/myproj/migration.diffThe patch should apply without a hitch if you have done everything correctly. You can now run
$ git statusto show the status of your repository (i.e. modified files, deleted files, files that needs to be added, etc.).
Congratulations! Your repository is now in the same state as before the switch!
Before you add changes (and files) to commit you should probably think closely about how changes are logically related and how they could be grouped into sets, instead of committing everything as one big blob. It's better to have many, many small commits than a few big ones, since this helps picking out particular patch sets and applying them to other branches (this is known as cherry picking). There is a convenient tool to pick, group and commit your changes
$ git citoolIf you rather do everything by hand, repeatedly run
$ git add file(s) $ git committo group changes into sets, until all changes have been committed. This is also how you add untracked files to git.
Finally run
$ git statusto check that you have not missed anything.
You can also just run
$ git commit -ato do what you probably should not, i.e. commit everything as one huge commit.
[ cvs checkout -d :ext:mylogin@myserver:/path/to/CVSROOT myproj ] $ git clone mylogin@myserver:/path/to/repository/myproj.git
[ cvs update ] $ git pull origin master
[ cvs update ] [ cvs checkin file1 file2 ...] $ git pull origin $ git commit file1 file2 ... $ git push origin master
This is where git differs from CVS substantially, committing a file commits it to your local repository, and pushing it transfers it to the master. Note that before you can push anything it has to be committed! A more useful way of working is:
... work $ git commit file1 file2 ... ... work $ git commit fileN dir2 ... ... work $ git commit -a # (commits everything) ... ok, we are ready with a new feature $ git pull origin master $ git push origin master
If you get a conflict when you pull, please consult the Nutshell guide how to resolve it.
[ cvs add file1 ... ] $ git add file1 ...
[ cvs remove file1 ] $ git rm file1
[ CVS cannot do this ] $ git mv file1 file2
[ cvs update ] $ git checkout file1
It's important to understand that a commit under git is exactly the same thing as under CVS, even if it's local to you until you push. When you push git will update the remote branch (master in this case) to be in the exact same state as your repository. This means that if you have 12 commits which are not on the remote server, then all those 12 commits will be communicated! Under CVS you would have made 12 commits directly to the server, under git you can commit any number of times, and when you think it's appropriate you push them all in one batch to the server.
In the old, comfy realm of CVS you would work, work, work and never commit until you had something you really needed or had to commit, like a bugfix in one file in some module. You can do exactly the same with git! Never commit anything until you have something you really need to commit. Then you do the following:
# first make sure we are up-to-date $ git pull origin master # commit CVS style $ git commit file1 file2 ... fileN $ git push origin master
This is identical behaviour compared to CVS, except for the fact that you explicitly need to communicate the changes to your 'origin'.
Of course, now I will argue that working like in the old CVS world is not very good. For one thing, it's important to commit often, since this way you get a much better work log/history and it's makes tracking down bugs a lot easier. Furthermore, when you accidentally delete a file, you can always get a reasonably new version back (if you have no daily backups of your work area).
Now the problem arises; you need to publish a small fix, but you have a lot of commits, and the code is not ready for the public yet. This is one place where branches are very convenient:
$ git push origin migration:refs/heads/<muid>where <myuid> is your login on the server.
$ git fetch originThis downloads all remote branches and stores them in your remote tracking branches (use git branch -r to see them). git fetch does not merge any changes into your working branches. After the git fetch you can examine the changes to the remote branches using e.g. gitk -all or git [log/diff] origin/master. To update your current branch with e.g. the remote master branch do
$ git merge origin/master
$ git pull origin masterThis command automatically fetches the master branch from the server and merges it with your current branch. This is more or less the git equivalent of cvs update. Note that this only fetches the specified branch from the server. If you want all changes on all branches use the fetch command above.
$ git push origin somebranch:<myuid>For more details see the git push man page. Note that for a push to work, it must result in a so called fast-forward-merge. Fast-forward means that the files you want to merge on another branch, must have the files in the other branch as parents. Simply stated, the files on the other branch have not changed since the last pull. If the push fails because it's not fast-forward, you must first do a git pull origin branch and resolve any conflicts before (re)doing the push.
$ git push --tags originThis will push all your tags to the main server. Note that you are not allowed by the server to create tags unless they are prefixed with your user id, e.g. <myuid>/mytag. If you don't want to push all tags, you can use the special syntax
$ git push origin tag <myuid>/<mytag>To delete a tag on the master you need to explicitly push an empty tag
$ git push origin :refs/tags/<myuid>/<mytag>
$ git push origin :<branch>
Since branches come and go, both in origin and in your own remote(s) it's a good idea to clean up the remotes once in a while by doing a
$ git remote prune origin
To set up your own sub-master repository follow these steps:
$ ssh <myserver> $ cd /path/to/repos $ mkdir $USER $ git clone -s -l --bare proj.git $USER/proj.git $ cd $USER/proj.git $ git branch -a # remove any branches and tags you do not care about $ git branch -D <branch branch...> $ git tag -d <tag tag...> # edit description (for gitweb) to your liking $ vi descriptionThe -s and -l options to git clone will cause git to set up the repository to use the master's object database as much as possible, so that it will take up very little space.
Now on your local machine, cd to your development repository and register the new remote:
$ git remote add <name> <myserver>:/path/to/repos/<myuid>/proj.gitNow when you fetch, pull and push use your new remote-tag <name> instead of origin to the corresponding command. The new development cycle will typically be something like
$ git pull origin master # do some work $ git push myremote mybranch:mybranch # or to push all branches automatically $ git push --all myremote
This document was generated using the LaTeX2HTML translator Version 2002-2-1 (1.71)
Copyright © 1993, 1994, 1995, 1996,
Nikos Drakos,
Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999,
Ross Moore,
Mathematics Department, Macquarie University, Sydney.
The command line arguments were:
latex2html -local_icons -dir HTML/CVS2git -split 0 CVS2git.tex
The translation was initiated by Jonas Juselius on 2008-03-14