6bad1902f5
Signed-off-by: Junio C Hamano <junkio@cox.net>
249 lines
10 KiB
Plaintext
249 lines
10 KiB
Plaintext
git for CVS users
|
|
=================
|
|
|
|
Ok, so you're a CVS user. That's ok, it's a treatable condition, and the
|
|
first step to recovery is admitting you have a problem. The fact that
|
|
you are reading this file means that you may be well on that path
|
|
already.
|
|
|
|
The thing about CVS is that it absolutely sucks as a source control
|
|
manager, and you'll thus be happy with almost anything else. git,
|
|
however, may be a bit 'too' different (read: "good") for your taste, and
|
|
does a lot of things differently.
|
|
|
|
One particular suckage of CVS is very hard to work around: CVS is
|
|
basically a tool for tracking 'file' history, while git is a tool for
|
|
tracking 'project' history. This sometimes causes problems if you are
|
|
used to doing very strange things in CVS, in particular if you're doing
|
|
things like making branches of just a subset of the project. git can't
|
|
track that, since git never tracks things on the level of an individual
|
|
file, only on the whole project level.
|
|
|
|
The good news is that most people don't do that, and in fact most sane
|
|
people think it's a bug in CVS that makes it tag (and check in changes)
|
|
one file at a time. So most projects you'll ever see will use CVS
|
|
'as if' it was sane. In which case you'll find it very easy indeed to
|
|
move over to git.
|
|
|
|
First off: this is not a git tutorial. See
|
|
link:tutorial.html[Documentation/tutorial.txt] for how git
|
|
actually works. This is more of a random collection of gotcha's
|
|
and notes on converting from CVS to git.
|
|
|
|
Second: CVS has the notion of a "repository" as opposed to the thing
|
|
that you're actually working in (your working directory, or your
|
|
"checked out tree"). git does not have that notion at all, and all git
|
|
working directories 'are' the repositories. However, you can easily
|
|
emulate the CVS model by having one special "global repository", which
|
|
people can synchronize with. See details later, but in the meantime
|
|
just keep in mind that with git, every checked out working tree will
|
|
have a full revision control history of its own.
|
|
|
|
|
|
Importing a CVS archive
|
|
-----------------------
|
|
|
|
Ok, you have an old project, and you want to at least give git a chance
|
|
to see how it performs. The first thing you want to do (after you've
|
|
gone through the git tutorial, and generally familiarized yourself with
|
|
how to commit stuff etc in git) is to create a git'ified version of your
|
|
CVS archive.
|
|
|
|
Happily, that's very easy indeed. git will do it for you, although git
|
|
will need the help of a program called "cvsps":
|
|
|
|
http://www.cobite.com/cvsps/
|
|
|
|
which is not actually related to git at all, but which makes CVS usage
|
|
look almost sane (ie you almost certainly want to have it even if you
|
|
decide to stay with CVS). However, git will want 'at least' version 2.1
|
|
of cvsps (available at the address above), and in fact will currently
|
|
refuse to work with anything else.
|
|
|
|
Once you've gotten (and installed) cvsps, you may or may not want to get
|
|
any more familiar with it, but make sure it is in your path. After that,
|
|
the magic command line is
|
|
|
|
git cvsimport -v -d <cvsroot> -C <destination> <module>
|
|
|
|
which will do exactly what you'd think it does: it will create a git
|
|
archive of the named CVS module. The new archive will be created in the
|
|
subdirectory named <destination>; it'll be created if it doesn't exist.
|
|
Default is the local directory.
|
|
|
|
It can take some time to actually do the conversion for a large archive
|
|
since it involves checking out from CVS every revision of every file,
|
|
and the conversion script is reasonably chatty unless you omit the '-v'
|
|
option, but on some not very scientific tests it averaged about twenty
|
|
revisions per second, so a medium-sized project should not take more
|
|
than a couple of minutes. For larger projects or remote repositories,
|
|
the process may take longer.
|
|
|
|
After the (initial) import is done, the CVS archive's current head
|
|
revision will be checked out -- thus, you can start adding your own
|
|
changes right away.
|
|
|
|
The import is incremental, i.e. if you call it again next month it'll
|
|
fetch any CVS updates that have been happening in the meantime. The
|
|
cut-off is date-based, so don't change the branches that were imported
|
|
from CVS.
|
|
|
|
You can merge those updates (or, in fact, a different CVS branch) into
|
|
your main branch:
|
|
|
|
git resolve HEAD origin "merge with current CVS HEAD"
|
|
|
|
The HEAD revision from CVS is named "origin", not "HEAD", because git
|
|
already uses "HEAD". (If you don't like 'origin', use cvsimport's
|
|
'-o' option to change it.)
|
|
|
|
|
|
Emulating CVS behaviour
|
|
-----------------------
|
|
|
|
|
|
So, by now you are convinced you absolutely want to work with git, but
|
|
at the same time you absolutely have to have a central repository.
|
|
Step back and think again. Okay, you still need a single central
|
|
repository? There are several ways to go about that:
|
|
|
|
1. Designate a person responsible to pull all branches. Make the
|
|
repository of this person public, and make every team member
|
|
pull regularly from it.
|
|
|
|
2. Set up a public repository with read/write access for every team
|
|
member. Use "git pull/push" as you used "cvs update/commit". Be
|
|
sure that your repository is up to date before pushing, just
|
|
like you used to do with "cvs commit"; your push will fail if
|
|
what you are pushing is not up to date.
|
|
|
|
3. Make the repository of every team member public. It is the
|
|
responsibility of each single member to pull from every other
|
|
team member.
|
|
|
|
|
|
CVS annotate
|
|
------------
|
|
|
|
So, something has gone wrong, and you don't know whom to blame, and
|
|
you're an ex-CVS user and used to do "cvs annotate" to see who caused
|
|
the breakage. You're looking for the "git annotate", and it's just
|
|
claiming not to find such a script. You're annoyed.
|
|
|
|
Yes, that's right. Core git doesn't do "annotate", although it's
|
|
technically possible, and there are at least two specialized scripts out
|
|
there that can be used to get equivalent information (see the git
|
|
mailing list archives for details).
|
|
|
|
git has a couple of alternatives, though, that you may find sufficient
|
|
or even superior depending on your use. One is called "git-whatchanged"
|
|
(for obvious reasons) and the other one is called "pickaxe" ("a tool for
|
|
the software archeologist").
|
|
|
|
The "git-whatchanged" script is a truly trivial script that can give you
|
|
a good overview of what has changed in a file or a directory (or an
|
|
arbitrary list of files or directories). The "pickaxe" support is an
|
|
additional layer that can be used to further specify exactly what you're
|
|
looking for, if you already know the specific area that changed.
|
|
|
|
Let's step back a bit and think about the reason why you would
|
|
want to do "cvs annotate a-file.c" to begin with.
|
|
|
|
You would use "cvs annotate" on a file when you have trouble
|
|
with a function (or even a single "if" statement in a function)
|
|
that happens to be defined in the file, which does not do what
|
|
you want it to do. And you would want to find out why it was
|
|
written that way, because you are about to modify it to suit
|
|
your needs, and at the same time you do not want to break its
|
|
current callers. For that, you are trying to find out why the
|
|
original author did things that way in the original context.
|
|
|
|
Many times, it may be enough to see the commit log messages of
|
|
commits that touch the file in question, possibly along with the
|
|
patches themselves, like this:
|
|
|
|
$ git-whatchanged -p a-file.c
|
|
|
|
This will show log messages and patches for each commit that
|
|
touches a-file.
|
|
|
|
This, however, may not be very useful when this file has many
|
|
modifications that are not related to the piece of code you are
|
|
interested in. You would see many log messages and patches that
|
|
do not have anything to do with the piece of code you are
|
|
interested in. As an example, assuming that you have this piece
|
|
of code that you are interested in in the HEAD version:
|
|
|
|
if (frotz) {
|
|
nitfol();
|
|
}
|
|
|
|
you would use git-rev-list and git-diff-tree like this:
|
|
|
|
$ git-rev-list HEAD |
|
|
git-diff-tree --stdin -v -p -S'if (frotz) {
|
|
nitfol();
|
|
}'
|
|
|
|
We have already talked about the "\--stdin" form of git-diff-tree
|
|
command that reads the list of commits and compares each commit
|
|
with its parents (otherwise you should go back and read the tutorial).
|
|
The git-whatchanged command internally runs
|
|
the equivalent of the above command, and can be used like this:
|
|
|
|
$ git-whatchanged -p -S'if (frotz) {
|
|
nitfol();
|
|
}'
|
|
|
|
When the -S option is used, git-diff-tree command outputs
|
|
differences between two commits only if one tree has the
|
|
specified string in a file and the corresponding file in the
|
|
other tree does not. The above example looks for a commit that
|
|
has the "if" statement in it in a file, but its parent commit
|
|
does not have it in the same shape in the corresponding file (or
|
|
the other way around, where the parent has it and the commit
|
|
does not), and the differences between them are shown, along
|
|
with the commit message (thanks to the -v flag). It does not
|
|
show anything for commits that do not touch this "if" statement.
|
|
|
|
Also, in the original context, the same statement might have
|
|
appeared at first in a different file and later the file was
|
|
renamed to "a-file.c". CVS annotate would not help you to go
|
|
back across such a rename, but git would still help you in such
|
|
a situation. For that, you can give the -C flag to
|
|
git-diff-tree, like this:
|
|
|
|
$ git-whatchanged -p -C -S'if (frotz) {
|
|
nitfol();
|
|
}'
|
|
|
|
When the -C flag is used, file renames and copies are followed.
|
|
So if the "if" statement in question happens to be in "a-file.c"
|
|
in the current HEAD commit, even if the file was originally
|
|
called "o-file.c" and then renamed in an earlier commit, or if
|
|
the file was created by copying an existing "o-file.c" in an
|
|
earlier commit, you will not lose track. If the "if" statement
|
|
did not change across such a rename or copy, then the commit that
|
|
does rename or copy would not show in the output, and if the
|
|
"if" statement was modified while the file was still called
|
|
"o-file.c", it would find the commit that changed the statement
|
|
when it was in "o-file.c".
|
|
|
|
NOTE: The current version of "git-diff-tree -C" is not eager
|
|
enough to find copies, and it will miss the fact that a-file.c
|
|
was created by copying o-file.c unless o-file.c was somehow
|
|
changed in the same commit.
|
|
|
|
You can use the --pickaxe-all flag in addition to the -S flag.
|
|
This causes the differences from all the files contained in
|
|
those two commits, not just the differences between the files
|
|
that contain this changed "if" statement:
|
|
|
|
$ git-whatchanged -p -C -S'if (frotz) {
|
|
nitfol();
|
|
}' --pickaxe-all
|
|
|
|
NOTE: This option is called "--pickaxe-all" because -S
|
|
option is internally called "pickaxe", a tool for software
|
|
archaeologists.
|