How do I revert a big change in CVS?
Asked Answered
L

10

29

One of my colleagues has totally messed up the contents of a directory in our main CVS repository. I need to just revert the whole module to the state it was in at the end of last year. What's the CVS command to do this please?

He has added and removed hundreds of files, so a simple "copy over files from old checkout and commit" isn't enough.

I have RTFM and STFW, and I tried this:

cvs co modulename  # Note no -P option
cvs up -jHEAD -jMAIN:2008-12-30 modulename

But that doesn't work - the new files he created get removed, but the old files and directories don't get resurrected. (I didn't commit it).

I can probably write a shell script for this, but surely this functionality must be in CVS already?

Update: Some clarifications:

  • I can get a local checkout of the module at a specific date. The question is how to get that back into CVS.

  • I do have backups, but the point using of a revision control system like CVS is that it's supposed to be easy to get any historical state. Next time something like this happens I may not be lucky enough to have backups (e.g. backups are daily, so I may lose up to a day's work).

  • I know that CVS is old, and we should move to something newer. But in a large team with a large number of CVS-based tools (checkout & build scripts, nightly build server, etc) the time cost of such a move is considerable. (Evaluation, updating scripts, testing, migration, training, lost developer time, maintaining both systems in parallel as CVS would still be needed for old branches). Hence this has to be planned & scheduled by management.

Update #2: I'm going to start a bounty on this. To qualify for the bounty you have to explain how to revert using normal CVS commands, not with a hacky shell script.

Update #3: The server is CVS 1.12.13. Access is via pserver. I can use the same version of CVS on a Linux PC, or the CVSNT 2.0.51d client on Windows.

Lakeshialakey answered 12/1, 2009 at 13:13 Comment(4)
I do not want to be rude, but you have backups, right?Radnorshire
@Keltia, I don't think this is a backup issue--this is what source control is designed for. Although, I agree that OP should have backups, just not for this.Jazmin
Yes, we do have backups. The thought of doing a restore had occurred to me, too. I'd (foolishly) thought that a CVS revert would be easier.Lakeshialakey
What CVS version would this be on? I think the newer cvs versions with patchset support would be able to do this easily enough...Viquelia
C
28

Actually your initial approach was very close to the solution. The problem is, that joining date-based does not handle removed files and directories correctly. You need to set a tag to the code base you want to join first:

mkdir code_base1 && cd code_base1
cvs co -D "2008-12-30" modulename
cvs tag code_base_2008_12_30

Now do the join tag-based, subtracting all changes between now and 2008-12-30:

cd .. && mkdir code_base2 && cd code_base2
cvs co modulename
cvs update -d -j HEAD -j code_base_2008_12_30  # use -d to resurrect deleted directories

Compare the contents of code_base1 and code_base2. They should be identical except for the CVS meta information. Finally commit the code as it was on 2008-12-30 as new HEAD:

cvs commit -m "Revert all changes this year"

Note that tagging the code you wish to join like this will not work, because rtag also does not handle removed files and directories correctly, when using -D:

cvs rtag -D "2008-12-30" code_base_2008_12_30 modulename
Carr answered 25/6, 2009 at 4:5 Comment(1)
I'm not sure you need to checkout in two different directories. But it won't hurt either.Unisexual
R
4

There are several problems with CVS and you're hitting them with such a problem.

  1. CVS is file-oriented, no concept of a changeset or snasphot. That means that changes such as the one you want to revert are a bit difficult to handle. Commits are atomic within a given directory, not outside.

  2. Directories are not versioned. That means that empty directories will be deleted (if you update with -P) and that you have to specify -d to create them on checkout/update.

So, to answer your question, dates are probably the only way to deal with because you didn't use tags to create some poor man's version of changeset.

My comment about backups is that it may be easier to recover the whole repo from backups than try to correct things that CVS is not really good at.

I would encourage you -- but that is another subject -- to change version control as soon as you can. Trust me, I've been dealing with CVS for a long time within the FreeBSD project and learn very quickly how hateful CVS is... See here for some of my views on version control software.

Radnorshire answered 12/1, 2009 at 21:37 Comment(0)
E
2

I believe your second command should also be a checkout, rather than an update. I can't justify this with logic, since there is no logic in the world of CVS, but it has worked for me. Try this:

cvs co -P modulename
cvs co -P -jHEAD -jMAIN:2008-12-30 modulename

If you're reverting a branch other than HEAD, e.g. X, pass the -rX argument in both commands:

cvs co -P -rX modulename
cvs co -P -rX -jHEAD -jMAIN:2008-12-30 modulename
Enamelware answered 14/1, 2009 at 18:34 Comment(0)
L
1

I'm still interested to know if there's an easier way. (There must surely be an easier way). What I ended up doing was, on a Linux PC using bash:

# Get woking copy we're going to change
cd ~/work
rm -rf modulename
cvs up -dP modulename
cd modulename

# Remove all files
find . -name CVS -prune -o -type f -print | xargs cvs rm -f

# Get the old revision
cd ~
mkdir scratch
cd scratch
cvs -q co -D 2008-12-31 modulename
cd modulename

# Copy everything to the working dir and do "cvs add" on it
find . -name CVS -prune -o -type f -print | \
    xargs tar c | \
    (cd ~/work/modulename && tar xv | \
    xargs cvs add)

# Check everything is OK before we commit
cd ~/work/modulename
cvs -nq up

# it gave me an error on readme.txt because I'd deleted and then added it, so:
mv readme.txt x # save good rev
cvs add readme.txt # resurrect the bad rev
mv x readme.txt # clobber file with good rev

# Commit it
cvs commit -m "Revert all changes this year"

# Delete now-empty directories
cvs -q up -dP

# Double-check everything is back how it was
diff -ur -xCVS ~/scratch/modulename ~/work/modulename

Then I discovered that there were still differences - my colleague had added filenames containing spaces, which weren't deleted by the above process. I had to delete those separately. (I should have used find ... -print0 rather than -print, and passed the -0 argument to xargs. I just didn't realise there were files with spaces.)

Lakeshialakey answered 12/1, 2009 at 13:59 Comment(0)
P
1

You could look into cvsps. Google it.

Also, with quilt (or Andrew Morton's patchscripts, which is what quilt started out as) and cvsps, a very close approximation of changesets can be had.

see http://geocities.com/smcameron/cvs_changesets.html

Petersen answered 28/6, 2009 at 2:18 Comment(0)
A
0

Have you tried using the -d option? (build subdirectories)

As far as I can remember, it's implied for cvs co, but not for cvs up.

Anteater answered 12/1, 2009 at 13:15 Comment(1)
I've just tried adding -d to the cvs up part; unfortunately I get the same resultLakeshialakey
S
0

According to http://www.astro.ku.dk/~aake/MHD/docs/CVS.html, the following is what you need:

cvs update -D "30 Dec 2008 23:59"
Sadism answered 12/1, 2009 at 13:29 Comment(2)
That gets the old version into my local working copy, but I then need to commit that into the repository (to get rid of the changes). Unfortunately, committing it is hard - CVS won't let you commit if you're using a dated checkout.Lakeshialakey
You could check out an additional copy from head, swap out the directory you want with the one from the dated checkout, and then commit over the top.Maricruzmaridel
O
0

If you have a backup of your repository (the actual RCS files on the server, e.g. on tape) you could just restore that folder on the CVS server to the state it was before. Don't forget to stop the CVS server before doing this (and restart it afterwards).

Obovoid answered 12/1, 2009 at 22:40 Comment(0)
W
0

Big problem, don't have full answer, just a tip on your scripting to deal with spaces in file names.

Instead of

find ... | xargs tar c - | ...

try putting

find ... | perl -e '@names = <>;' -e 'chomp @names;' -e 'system( "tar", "c", "-", @names);' | ...

that way, your archive creation (or similar operations) won't suffer from spaces in the names, the shell argv parsing gets skipped before tar is called.

One more thing, on the off chance it actually works: if there is a CVS to SVN utility, use it (I am assuming such a utility would pull deleted files from the "CVS attic"), and if it saves each moment in time as a project level checkpoint (since SVN does that, unlike CVS), use SVN to fetch the right moment in time. Lot of ifs...

Wallack answered 14/1, 2009 at 1:56 Comment(1)
"find ... -print0 | xargs -0 ..." would work too. However, spaces in filenames are so rare, I didn't realise I needed it until too late.Lakeshialakey
C
0

If you or a colleague are comfortable with git, you could use git cvsimport to create a git repository mirroring the CVS repository. Reverting a commit/changeset in git is trivial (using git revert). You could then use git cvsexportcommit to send the revert commit to CVS.

This might all sound overly complicated, but in my experience git cvsimport and git cvsexportcommit work really well once you've got everything set up. You end up with all the power of git personally even though the project is still using CVS.

Convertiplane answered 19/8, 2011 at 17:34 Comment(1)
careful with this, you can lose data if you're using cvsnt without realizing it. a lot of shops use cvsnt for ldap integrationInquisitorial

© 2022 - 2024 — McMap. All rights reserved.