Git : Home | Documentation | Wiki | Download Site | Git's Gitweb

Cogito - CVS Crash Course

Welcome to the Git version control system! Here we will briefly introduce you to Git usage based on your current CVS knowledge. You will need the latest Git and Cogito packages installed; Cogito is a user interface for Git which extends it for easier use, while you can still use directly Git for more advanced operations; you can also perform all of the below using Git commands, but the core Git interface is more elaborate and complicated. There is also a more technical CVS to Git migration HOWTO in the Git documentation which uses Git commands only, but covers only some specific differences.

If you are just after tracking someone else's project, this will get you started quickly:

cg clone url
cg update
cvs -droot checkout module
cvs update -dP

How to Read Me

In those small tables, at the left we always list the Cogito/Git commands for the task, while at the right the corresponding CVS commands you would use for the job are listed. If you are in hurry, just skimming over them should give you a good idea about the Cogito usage basics.

Before running any command the first time, it's recommended that you at least quickly skim through its manual page. Many of the commands have very useful and interesting features (that we won't list here) and sometimes there are some extra notes you might want to know. There's a quick usage help available for the Cogito commands if you pass them the --help switch.

Things You Should Know

There are couple important concepts it is good to know when starting with Git. If you are in hurry though, you can skip this section and only get back to it when you get seriously confused; it should be possible to pick up with just using your intuition.

With CVS, for each project there is a single repository at some detached central place where all the history is and which you checkout and commit into. Git works differently, each copy of the project tree (we call that the working copy) carries its own repository around (in the .git subdirectory in the project tree root). You can also have a so-called bare repository which is not attached to a working copy; that is useful especially when you want to publish your repository. We will get to that.

CVS has the repositories partitioned to modules; there is no such thing in Git, you just create several separate repositories.

CVS identifies revisions with the RCS ids - few numbers delimited with dots. That is impractical in distributed systems like Git. Git identifies revisions with SHA1 ids, which are long 160-bit numbers written in hexadecimal. It may look scary at first, but in practice it is not a big hurdle - you can refer to the latest revision by HEAD and its parent as HEAD^ (you can go on adding carrets), cut'n'paste helps a lot and you can write only the few leading digits of a revision - as long as it is unique, Git will guess the rest. (You can do even more advanced stuff with revision specifiers, see the git-rev-parse manpage for details.)

Each commit has an author and a committer field, which record who and when created the change and who committed it (Git is designed to work well with patches coming by mail - in that case, the author and the committer will be different). Git will try to guess your realname and email, but especially with email it is likely to get it wrong. You can check it using git-var -l; see cg-commit manpage on various ways of overriding it.

The Cogito commands are in the form cg command, the Git commands take the form of git command. In both cases, you can interchangeably use the cg-command and git-command form as well.

Cogito can produce colorful output with some commands; since some people hate colors way more than the rest likes them, by default the colors are turned off. If you would like to have colors in your output, create ~/.cgrc and put this inside:

cg-diff -c
cg-help -c
cg-log -c
The format and purpose is the same as of the ~/.cvsrc file; you can add other commands and switches there later if you wish (my tip is -f for cg-log).

Also, you may find it convenient to watch your repository using the gitk repository as you go.

Your Very Own

For the first introduction, let's make your project tracked by Git and see how we get around to do daily development in it. Let's cd to the directory with your project and initialize a brand new Git repository with it:

cg init cvs -drepo init
cvs import FOO BAR

As we already mentioned, there are no modules; due to superior branching support in Git. There is no vendor branch in Git either (we will cover how to do vendor tracking in Git at a later time in the StGit Crash Course). Thus there is no need for two separate commands and cg init will both initialize the repository and create the initial import.

Now your tree is officially tracked by Git. You can explore the .git subdirectory a bit if you want, or don't if you don't care. Do some random changes to your tree now - poke into few files or such. Let's check what we've done:

cg diffcvs diff -u | less

That's it. This is one of the more powerful commands; just like with CVS, you can pass -rs, limit the diff to specific directories or files and so on. Git embeds special information in the diffs about adds, removals and mode changes:

cg patchpatch -p0

That will apply the patch while telling Git about and performing those "meta-changes".

There is a more concise representation of changes available:

cg status(cvs -n update)

cg status will not update your working copy, though, only show the concise changes summary as well as list any files that you haven't either ignored or told Git about. In addition, it will also show some cryptic line at the top (with "master" inside) - ignore it for now, we'll learn more about it later.

While we are at the status command, over time plenty of the "? lines" will get in there, denoting files not tracked by Git. Wait a moment if you want to add them, run cg clean if you want to get rid of all of them, or add them to the .gitignore file if you want to keep them around untracked (works the same as .cvsignore).

Another thing cvs update would do is restoring files you've accidentally removed. The command for that in Cogito is cg restore; you can restore everything, just specified files, force it to overwrite missing files.

So, just like in CVS, you need to tell Git when you add or remove any files. Additionaly, you can tell it when you move files around:

cg add file
cg rm file
cg mv file
cvs add file
cvs remove

Cogito is more promising than CVS here; especially, you can remove files that are yet present in the working directory (cg rm can remove them for you if you pass it the -f switch) - saves a lot of annoyance when removing files matching to a wildcard. You can also recursively add/remove whole directories and so on; Cogito's cool!

So, it's about time we committed our changes. Big surprise about the command:

cg commitcvs commit

As with CVS, you can limit the commit only to specified files and so on. A few words on the commit message: it is customary to have a short commit summary as the first line of the message, because various tools listing commits frequently show only the first line of the message You can specify the commit message using the -m parameter as you are used, but there're two differences - the text gets autoformatted to paragraphs and you can pass several -m arguments and they will create separate paragraphs in the commit message:

cg commit -m"Short one-line description" -m"And here can come \
your longer description of the commit, and it gets split out \
and reflown to wrapped paragraphs just right."

If you don't pass any -m parameter or pass the -e parameter, your favorite $EDITOR will get run and you can compose your commit message there, just as with CVS. In addition, the list of files to be committed is shown and you can actually delete lines with the files you don't want to commit and they won't be. You can also adjust the authorship information in the editor.

And as a bonus, if you pass it the -p parameter it will show the whole patch being committed in the editor so that you can do a quick last-time review. And you can manually modify the patch and your changes will get propagated to the commit (and your working tree).

By the way, if you screwed up committing, there's not much you can do with CVS, except using some enigmatic cvs admin subcommands. Git does it better - you can amend your latest commit (re-edit the metadata as well as update the tree) using cg commit --amend, or toss your latest commit away completely using cg admin-uncommit.

Now that we have committed some stuff, you might want to review your history:

cg log
cg seek rev
git blame file
cvs log
cvs checkout -r tag
cvs annotate file

The log command works quite similar in CVS and Git; again, cg log is quite powerful, please look through its options to see some of the stuff it can do.

To move your tree to some older revision, use the seek command (and pass it no arguments to go back to your latest revision_. Note that this is only for "temporary excursions" - if you want to just reset your history and make a given commit your new head, think again and if you are sure, cg switch -f -r newrev master (this will be perhaps made simpler in the future, we will review the switch command in more detail in the future).

Git now has an annotation command akin to CVS', but there is a big chance that you probably want to do something different! Usually, when using annotate you are looking for the origin of some piece of code, and the so-called pickaxe of Git is much more comfortable tool for that job (detailed discussion; you may want to use cg log -S string instead of git log -Sstring, tho').

Tagging and branching

CVS marks certain checkpoints in history with tags. Git obviously can do that to, but not only are Git tags much faster to make (and actually reliable), but much more powerful. The Git tag can have an arbitrary description attached (the first line is special as in the commit case), some people actually store the whole release announcements in the tag descriptions. The identity of the person who tagged is stored (again following the same rules as identity of the committer). You can tag other objects than commits (but that is conceptually rather low-level operation). And the tag can be cryptographically PGP signed to verify the identity (by Git's nature of working, that signature also confirms the validity of the associated revision, its ihstory and tree). So, let's do it:

cg tag namecvs tag name

To list tags in CVS, you run cvs log on some random file; kinda weird if you think about it, isn't it? In Git, you can find out using cg tag-ls and show details (author, description, PGP signature verification, ...) using cg tag-show.

Like CVS, Git can do branches, but Git's branches are again much faster and cheaper; in fact, Git encourages you to branch often and use branches extensively during regular development (with so-called topic branches dedicated to development of a particular feature). In CVS, you create a branch tag; in Git, there is a special command for working with branches (although funnily enough, there is certain architectural similarity between branchs/tags relation in CVS and Git).

cg switch -c branch
cg switch branch
cvs tag -b branch
cvs checkout -r branchname

The first command creates a branch, the second command switches your tree to a certain branch. You can pass an -r argument to switch to base your new branch on a different revision than the latest one.

You can list your branches conveniently using the aforementioned cg-status command - the cryptic listing at the top is just the listing of branches. The current one is denoted by an "arrow".

Git supports merging between branches much better than CVS - history of both branches is preserved over the merges and repeated merges of the same branches are supported out-of-the-box. Make sure you are on one of the to-be-merged branches and merge the other one now:

cg merge branchcvs update -j branch

If changes were made on only one of the branches since the last merge, they are simply replayed on your other branch (so-called fast-forward merge). If changes were made on both branches, they are merged intelligently (so-called three-way merge): if any changes conflicted, cg merge will report them and let you resolve them, updating the rest of the tree already to the result state; you can cg commit when you resolve the conflicts. If no changes conflicted, cg commit is invoked automatically, letting you edit the commit message (or you can do cg merge -n branch to review the merge result and then do the commit yourself).

Sometimes, you do want to throw away the history of one of the branches, e.g. when you did a fine-tracked development of a topic and now want to squash it to a single conceptual change commit - just pass merge the --squash switch. Also, sometimes you may want to join together two unrelated branches (coming from different projects; hold on, we'll get to that in a minute): -j will take care of that.

Aside from merging, sometimes you want to just pick one commit from a different branch. We have already mentioned the command cg patch. It can apply patches, but also autocommit them and extract them from given commits. The command cg patch -C rev combines both functionality and will cherry pick a given commit to your branch.

Going Remote

So far, we have neglected that Git is a distributed version control system. It is time for us to set the record straight - let's grab some stuff from remote sites.

If you are working on someone else's project, you usually want to clone its repository instead of starting your own. We've already mentioned that at the top of this document:

cg clone urlcvs -droot checkout module

Now you have got your master branch as when initializing a new repository, but in addition you got an origin remote branch. Remote branch, you ask? Well, so far we have worked only with local branches. Remote branches are a mirror image of branches in remote repositories and you don't ever switch to them directly or write to them. Let me repeat - you never mess with remote branches. If you want to switch to a remote branch, you need to create a corresponding local branch which will "track" the remote branch. In clone's default setup, the master local branch tracks the origin remote branch, which represents the remote repository.

You can add more remote branches to a cloned repository, as well as just an initialized one, using cg branch-add branch url. The command cg branch-ls lists all the branches.

Now, how do you get any new changes from a remote repository to your local branch? You fetch them: cg fetch branch. At this point they are in your local branch and you can examine them using cg log -r branch (cg log -r HEAD..branch to see just the changes you don't have in your branch), diff them, and obviously, merge them - just do cg merge branch. Note that if you don't specify a branch to fetch or merge, it will conveniently default to origin.

Since you frequently just fetch + merge, there is a command to automate that:

cg update branchcvs update -dP

Again, it will default to origin if no branch was specified. It is recommended to use update instead of fetch + merge since it does the first thing even if the remote branch' history got altered (which is also a reason why altering history is discouraged at the moment you publish it).

So we can get updates from the remote side (pull changes). Can we do the opposite as well? Push our changes? Yes! We do cg push branch which will push our current branch to the given remote branch - note that this works generally only over SSH (or HTTP but with special webserver setup). It is highly recommended to setup a SSH key and an SSH agent mechanism so that you don't have to type in a password all the time.

One important thing is that you should push only to remote branches that are not currently checked out on the other side (for the same reasons you never switch to a remote branch locally)! Otherwise the working copy at the remote branch will get out of date and confusion will ensue. The best way to avoid that is to push only to remote repositories with no working copy at all - so called bare repositories which are commonly used for public access or developers' meeting point - just for exchange of history where a checked out copy would be a waste of space anyway. You can create such a repository using cg admin-setuprepo path - you can add additional options to make it shared for a UNIX group of users.

Git can work with the same workflow as CVS, with a group of developers using a single repository for exchange of their work. The only change is that their changes aren't submitted automatically but they have to push (however, you can setup a post-commit hook that will push for you every time you commit; that loses the flexibility to fix up a screwed commit, though). The developers must have either an entry in htaccess (for HTTP DAV) or a UNIX account (for SSH). You can restrict their shell account only to Git pushing/fetching by using the git-shell login shell.

You can also exchange patches by mail. Git has very good support for patches incoming by mail. You can apply them by feeding mailboxes with patch mails to cg patch -m. If you want to send patches (or a third-party changes to an upstream repository with no commit access in general), it is best to use the StGIT tool (see the StGIT Crash Course).

If you have any questions or problems which are not obvious from the documentation, please contact us at the Git mailing list at We hope you enjoy using Git and Cogito!