|
|
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:
|
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.
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 -cThe 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.
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 diff | cvs 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 patch | patch -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 commit | cvs 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').
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 name | cvs 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 branch | cvs 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.
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 url | cvs -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 branch | cvs 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 git@vger.kernel.org. We hope you enjoy using Git and Cogito!