summaryrefslogtreecommitdiff
path: root/gnu/usr.bin/cvs/doc/cvsclient.texi
blob: 9c8f326bd186569d6e0c2973f41679c7111ce294 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
\input texinfo

@setfilename cvsclient.info

@node Top
@top CVS Client/Server

This manual describes the client/server protocol used by CVS.  It does
not describe how to use or administer client/server CVS; see the
regular CVS manual for that.

@menu
* Goals::             Basic design decisions, requirements, scope, etc.
* Notes::             Notes on the current implementation
* How To::            How to remote your favorite CVS command
* Protocol Notes::    Possible enhancements, limitations, etc. of the protocol
* Protocol::          Complete description of the protocol
@end menu

@node Goals
@chapter Goals

@itemize @bullet
@item
Do not assume any access to the repository other than via this protocol.
It does not depend on NFS, rdist, etc.

@item
Providing a reliable transport is outside this protocol.  It is expected
that it runs over TCP, UUCP, etc.

@item
Security and authentication are handled outside this protocol (but see
below about @samp{cvs kserver}).

@item
This might be a first step towards adding transactions to CVS (i.e. a
set of operations is either executed atomically or none of them is
executed), improving the locking, or other features.  The current server
implementation is a long way from being able to do any of these
things.  The protocol, however, is not known to contain any defects
which would preclude them.

@item
The server never has to have any CVS locks in place while it is waiting
for communication with the client.  This makes things robust in the face
of flaky networks.

@item
Data is transferred in large chunks, which is necessary for good
performance.  In fact, currently the client uploads all the data
(without waiting for server responses), and then waits for one server
response (which consists of a massive download of all the data).  There
may be cases in which it is better to have a richer interraction, but
the need for the server to release all locks whenever it waits for the
client makes it complicated.
@end itemize

@node Notes
@chapter Notes on the Current Implementation

The client is built in to the normal @code{cvs} program, triggered by a
@code{CVSROOT} variable containing a colon, for example
@code{cygnus.com:/rel/cvsfiles}. 

The client stores what is stored in checked-out directories (including
@file{CVS}).  The way these are stored is totally compatible with
standard CVS.  The server requires no storage other than the repository,
which also is totally compatible with standard CVS.

The server is started by @code{cvs server}.  There is no particularly
compelling reason for this rather than making it a separate program
which shares a lot of sources with cvs.

The server can also be started by @code{cvs kserver}, in which case it
does an initial Kerberos authentication on stdin.  If the authentication
succeeds, it subsequently runs identically to @code{cvs server}.

The current server implementation can use up huge amounts of memory
when transmitting a lot of data over a slow link (i.e. the network is
slower than the server can generate the data).  Avoiding this is
tricky because of the goal of not having the server block on the
network when it has locks open (this could lock the repository for
hours if things are running smoothly or longer if not).  Several
solutions are possible.  The two-pass design would involve first
noting what versions of everything we need (with locks in place) and
then sending the data, blocking on the network, with no locks needed.
The lather-rinse-repeat design would involve doing things as it does
now until a certain amount of server memory is being used (10M?), then
releasing locks, and trying the whole update again (some of it is
presumably already done).  One problem with this is getting merges to
work right.  The two-pass design appears to be the more elegant of the
two (it actually reduces the amount of time that locks need to be in
place), but people have expressed concerns about whether it would be
slower (because it traverses the repository twice).  It is not clear
whether this is a real problem (looking for whether a file needs to be
updated and actually checking it out are done separately already), but
I don't think anyone has investigated carefully.  One hybrid approach
which avoids the problem with merges would be to start out in one-pass
mode and switch to two-pass mode if data is backing up--but this
complicates the code and should be undertaken only if the pure
two-pass design is shown to be flawed.

@node How To
@chapter How to add more remote commands

It's the usual simple twelve step process.  Let's say you're making
the existing @code{cvs fix} command work remotely.

@itemize @bullet
@item
Add a declaration for the @code{fix} function, which already implements
the @code{cvs fix} command, to @file{server.c}.
@item
Now, the client side.
Add a function @code{client_fix} to @file{client.c}, which calls 
@code{parse_cvsroot} and then calls the usual @code{fix} function.
@item
Add a declaration for @code{client_fix} to @file{client.h}.
@item
Add @code{client_fix} to the "fix" entry in the table of commands in
@file{main.c}.
@item
Now for the server side.
Add the @code{serve_fix} routine to @file{server.c}; make it do:
@example @code
static void
serve_fix (arg)
    char *arg;
@{
    do_cvs_command (fix);
@}
@end example
@item
Add the server command @code{"fix"} to the table of requests in @file{server.c}.
@item
The @code{fix} function can now be entered in three different situations:
local (the old situation), client, and server.  On the server side it probably
will not need any changes to cope.
Modify the @code{fix} function so that if it is run when the variable
@code{client_active} is set, it starts the server, sends over parsed 
arguments and possibly files, sends a "fix" command to the server,
and handles responses from the server.  Sample code:
@example @code
    if (!client_active) @{
        /* Do whatever you used to do */
    @} else @{
        /* We're the local client.  Fire up the remote server.  */
        start_server ();

        if (local)
            if (fprintf (to_server, "Argument -l\n") == EOF)
                error (1, errno, "writing to server");
        send_option_string (options);

        send_files (argc, argv, local);

        if (fprintf (to_server, "fix\n") == EOF)
            error (1, errno, "writing to server");
        err = get_responses_and_close ();
    @}
@end example
@item
Build it locally.  Copy the new version into somewhere on the 
remote system, in your path so that @code{rsh host cvs} finds it.
Now you can test it.
@item
You may want to set the environment variable @code{CVS_CLIENT_PORT} to
-1 to prevent the client from contacting the server via a direct TCP
link.  That will force the client to fall back to using @code{rsh},
which will run your new binary.
@item
Set the environment variable @code{CVS_CLIENT_LOG} to a filename prefix
such as @file{/tmp/cvslog}.  Whenever you run a remote CVS command,
the commands and responses sent across the client/server connection
will be logged in @file{/tmp/cvslog.in} and @file{/tmp/cvslog.out}.
Examine them for problems while you're testing.
@end itemize

This should produce a good first cut at a working remote @code{cvs fix}
command.  You may have to change exactly how arguments are passed,
whether files or just their names are sent, and how some of the deeper
infrastructure of your command copes with remoteness.

@node Protocol Notes
@chapter Notes on the Protocol

A number of enhancements are possible:

@itemize @bullet
@item
The @code{Modified} request could be speeded up by sending diffs rather
than entire files.  The client would need some way to keep the version
of the file which was originally checked out, which would double client
disk space requirements or require coordination with editors (e.g. maybe
it could use emacs numbered backups).  This would also allow local
operation of @code{cvs diff} without arguments.

@item
Have the client keep a copy of some part of the repository.  This allows
all of @code{cvs diff} and large parts of @code{cvs update} and
@code{cvs ci} to be local.  The local copy could be made consistent with
the master copy at night (but if the master copy has been updated since
the latest nightly re-sync, then it would read what it needs to from the
master).

@item
Provide encryption using kerberos.

@item
The current procedure for @code{cvs update} is highly sub-optimal if
there are many modified files.  One possible alternative would be to
have the client send a first request without the contents of every
modified file, then have the server tell it what files it needs.  Note
the server needs to do the what-needs-to-be-updated check twice (or
more, if changes in the repository mean it has to ask the client for
more files), because it can't keep locks open while waiting for the
network.  Perhaps this whole thing is irrelevant if client-side
repositories are implemented, and the rcsmerge is done by the client.
@end itemize

@node Protocol
@chapter The CVS client/server protocol

@menu
* Entries Lines::               
* Modes::                       
* Requests::                    
* Responses::                   
* Example::                     
@end menu

@node Entries Lines
@section Entries Lines

Entries lines are transmitted as:

@example
/ @var{name} / @var{version} / @var{conflict} / @var{options} / @var{tag_or_date}
@end example

@var{tag_or_date} is either @samp{T} @var{tag} or @samp{D} @var{date}
or empty.  If it is followed by a slash, anything after the slash
shall be silently ignored.

@var{version} can be empty, or start with @samp{0} or @samp{-}, for no
user file, new user file, or user file to be removed, respectively.

@var{conflict}, if it starts with @samp{+}, indicates that the file had
conflicts in it.  The rest of @var{conflict} is @samp{=} if the
timestamp matches the file, or anything else if it doesn't.  If
@var{conflict} does not start with a @samp{+}, it is silently ignored.

@node Modes
@section Modes

A mode is any number of repetitions of

@example
@var{mode-type} = @var{data}
@end example

separated by @samp{,}.

@var{mode-type} is an identifier composed of alphanumeric characters.
Currently specified: @samp{u} for user, @samp{g} for group, @samp{o} for
other, as specified in POSIX.  If at all possible, give these their
POSIX meaning and use other mode-types for other behaviors.  For
example, on VMS it shouldn't be hard to make the groups behave like
POSIX, but you would need to use ACLs for some cases.

@var{data} consists of any data not containing @samp{,}, @samp{\0} or
@samp{\n}.  For @samp{u}, @samp{g}, and @samp{o} mode types, data
consists of alphanumeric characters, where @samp{r} means read, @samp{w}
means write, @samp{x} means execute, and unrecognized letters are
silently ignored.

@node Requests
@section Requests

File contents (noted below as @var{file transmission}) can be sent in
one of two forms.  The simpler form is a number of bytes, followed by a
newline, followed by the specified number of bytes of file contents.
These are the entire contents of the specified file.  Second, if both
client and server support @samp{gzip-file-contents}, a @samp{z} may
precede the length, and the `file contents' sent are actually compressed
with @samp{gzip}.  The length specified is that of the compressed
version of the file.

In neither case are the file content followed by any additional data.
The transmission of a file will end with a newline iff that file (or its
compressed form) ends with a newline.

@table @code
@item Root @var{pathname} \n
Response expected: no.
Tell the server which @code{CVSROOT} to use.

@item Valid-responses @var{request-list} \n
Response expected: no.
Tell the server what responses the client will accept.
request-list is a space separated list of tokens.

@item valid-requests \n
Response expected: yes.
Ask the server to send back a @code{Valid-requests} response.

@item Repository @var{repository} \n
Response expected: no.  Tell the server what repository to use.  This
should be a directory name from a previous server response.  Note that
this both gives a default for @code{Entry } and @code{Modified } and
also for @code{ci} and the other commands; normal usage is to send a
@code{Repository } for each directory in which there will be an
@code{Entry } or @code{Modified }, and then a final @code{Repository }
for the original directory, then the command.

@item Directory @var{local-directory} \n
Additional data: @var{repository} \n.  This is like @code{Repository},
but the local name of the directory may differ from the repository name.
If the client uses this request, it affects the way the server returns
pathnames; see @ref{Responses}.  @var{local-directory} is relative to
the top level at which the command is occurring (i.e. the last
@code{Directory} or @code{Repository} which is sent before the command).

@item Max-dotdot @var{level} \n
Tell the server that @var{level} levels of directories above the
directory which @code{Directory} requests are relative to will be
needed.  For example, if the client is planning to use a
@code{Directory} request for @file{../../foo}, it must send a
@code{Max-dotdot} request with a @var{level} of at least 2.
@code{Max-dotdot} must be sent before the first @code{Directory}
request.

@item Static-directory \n
Response expected: no.  Tell the server that the directory most recently
specified with @code{Repository} or @code{Directory} should not have
additional files checked out unless explicitly requested.  The client
sends this if the @code{Entries.Static} flag is set, which is controlled
by the @code{Set-static-directory} and @code{Clear-static-directory}
responses.

@item Sticky @var{tagspec} \n
Response expected: no.  Tell the server that the directory most recently
specified with @code{Repository} has a sticky tag or date @var{tagspec}.
The first character of @var{tagspec} is @samp{T} for a tag, or @samp{D}
for a date.  The remainder of @var{tagspec} contains the actual tag or
date.

@item Checkin-prog @var{program} \n
Response expected: no.  Tell the server that the directory most recently
specified with @code{Directory} has a checkin program @var{program}.
Such a program would have been previously set with the
@code{Set-checkin-prog} response.

@item Update-prog @var{program} \n
Response expected: no.  Tell the server that the directory most recently
specified with @code{Directory} has an update program @var{program}.
Such a program would have been previously set with the
@code{Set-update-prog} response.

@item Entry @var{entry-line} \n
Response expected: no.  Tell the server what version of a file is on the
local machine.  The name in @var{entry-line} is a name relative to the
directory most recently specified with @code{Repository}.  If the user
is operating on only some files in a directory, @code{Entry} requests
for only those files need be included.  If an @code{Entry} request is
sent without @code{Modified}, @code{Unchanged}, or @code{Lost} for that
file the meaning depends on whether @code{UseUnchanged} has been sent;
if it has been it means the file is lost, if not it means the file is
unchanged.

@item Modified @var{filename} \n
Response expected: no.  Additional data: mode, \n, file transmission.
Send the server a copy of one locally modified file.  @var{filename} is
relative to the most recent repository sent with @code{Repository}.  If
the user is operating on only some files in a directory, only those
files need to be included.  This can also be sent without @code{Entry},
if there is no entry for the file.

@item Lost @var{filename} \n
Response expected: no.  Tell the server that @var{filename} no longer
exists.  The name is relative to the most recent repository sent with
@code{Repository}.  This is used for any case in which @code{Entry} is
being sent but the file no longer exists.  If the client has issued the
@code{UseUnchanged} request, then this request is not used.

@item Unchanged @var{filename} \n
Response expected: no.  Tell the server that @var{filename} has not been
modified in the checked out directory.  The name is relative to the most
recent repository sent with @code{Repository}.  This request can only be
issued if @code{UseUnchanged} has been sent.

@item UseUnchanged \n
Response expected: no.  Tell the server that the client will be
indicating unmodified files with @code{Unchanged}, and that files for
which no information is sent are nonexistent on the client side, not
unchanged.  This is necessary for correct behavior since only the server
knows what possible files may exist, and thus what files are
nonexistent.

@item Argument @var{text} \n
Response expected: no.
Save argument for use in a subsequent command.  Arguments
accumulate until an argument-using command is given, at which point
they are forgotten.

@item Argumentx @var{text} \n
Response expected: no.  Append \n followed by text to the current
argument being saved.

@item Global_option @var{option} \n
Transmit one of the global options @samp{-q}, @samp{-Q}, @samp{-l},
@samp{-t}, @samp{-r}, or @samp{-n}.  @var{option} must be one of those
strings, no variations (such as combining of options) are allowed.  For
graceful handling of @code{valid-requests}, it is probably better to
make new global options separate requests, rather than trying to add
them to this request.

@item expand-modules \n
Response expected: yes.  Expand the modules which are specified in the
arguments.  Returns the data in @code{Module-expansion} responses.  Note
that the server can assume that this is checkout or export, not rtag or
rdiff; the latter do not access the working directory and thus have no
need to expand modules on the client side.

@item co \n
@itemx update \n
@itemx ci \n
@itemx diff \n
@itemx tag \n
@itemx status \n
@itemx log \n
@itemx add \n
@itemx remove \n
@itemx rdiff \n
@itemx rtag \n
@itemx import \n
@itemx admin \n
@itemx export \n
@itemx history \n
@itemx release \n
Response expected: yes.  Actually do a cvs command.  This uses any
previous @code{Argument}, @code{Repository}, @code{Entry},
@code{Modified}, or @code{Lost} requests, if they have been sent.  The
last @code{Repository} sent specifies the working directory at the time
of the operation.  No provision is made for any input from the user.
This means that @code{ci} must use a @code{-m} argument if it wants to
specify a log message.

@item update-patches \n
This request does not actually do anything.  It is used as a signal that
the server is able to generate patches when given an @code{update}
request.  The client must issue the @code{-u} argument to @code{update}
in order to receive patches.

@item gzip-file-contents @var{level} \n
This request asks the server to filter files it sends to the client
through the @samp{gzip} program, using the specified level of
compression.  If this request is not made, the server must not do any
compression.

This is only a hint to the server.  It may still decide (for example, in
the case of very small files, or files that already appear to be
compressed) not to do the compression.  Compression is indicated by a
@samp{z} preceding the file length.

Availability of this request in the server indicates to the client that
it may compress files sent to the server, regardless of whether the
client actually uses this request.

@item @var{other-request} @var{text} \n
Response expected: yes.
Any unrecognized request expects a response, and does not
contain any additional data.  The response will normally be something like
@samp{error  unrecognized request}, but it could be a different error if
a previous command which doesn't expect a response produced an error.
@end table

When the client is done, it drops the connection.

@node Responses
@section Responses

After a command which expects a response, the server sends however many
of the following responses are appropriate.  Pathnames are of the actual
files operated on (i.e. they do not contain @samp{,v} endings), and are
suitable for use in a subsequent @code{Repository} request.  However, if
the client has used the @code{Directory} request, then it is instead a
local directory name relative to the directory in which the command was
given (i.e. the last @code{Directory} before the command).  Then a
newline and a repository name (the pathname which is sent if
@code{Directory} is not used).  Then the slash and the filename.  For
example, for a file @file{i386.mh} which is in the local directory
@file{gas.clean/config} and for which the repository is
@file{/rel/cvsfiles/devo/gas/config}:

@example
gas.clean/config/
/rel/cvsfiles/devo/gas/config/i386.mh
@end example

Any response always ends with @samp{error} or @samp{ok}.  This indicates
that the response is over.

@table @code
@item Valid-requests @var{request-list} \n
Indicate what requests the server will accept.  @var{request-list}
is a space separated list of tokens.  If the server supports sending
patches, it will include @samp{update-patches} in this list.  The
@samp{update-patches} request does not actually do anything.

@item Checked-in @var{pathname} \n
Additional data: New Entries line, \n.  This means a file @var{pathname}
has been successfully operated on (checked in, added, etc.).  name in
the Entries line is the same as the last component of @var{pathname}.

@item New-entry @var{pathname} \n
Additional data: New Entries line, \n.  Like @code{Checked-in}, but the
file is not up to date.

@item Updated @var{pathname} \n
Additional data: New Entries line, \n, mode, \n, file transmission.  A
new copy of the file is enclosed.  This is used for a new revision of an
existing file, or for a new file, or for any other case in which the
local (client-side) copy of the file needs to be updated, and after
being updated it will be up to date.  If any directory in pathname does
not exist, create it.

@item Merged @var{pathname} \n
This is just like @code{Updated} and takes the same additional data,
with the one difference that after the new copy of the file is enclosed,
it will still not be up to date.  Used for the results of a merge, with
or without conflicts.

@item Patched @var{pathname} \n
This is just like @code{Updated} and takes the same additional data,
with the one difference that instead of sending a new copy of the file,
the server sends a patch produced by @samp{diff -u}.  This client must
apply this patch, using the @samp{patch} program, to the existing file.
This will only be used when the client has an exact copy of an earlier
revision of a file.  This response is only used if the @code{update}
command is given the @samp{-u} argument.

@item Checksum @var{checksum}\n
The @var{checksum} applies to the next file sent over via
@code{Updated}, @code{Merged}, or @code{Patched}.  In the case of
@code{Patched}, the checksum applies to the file after being patched,
not to the patch itself.  The client should compute the checksum itself,
after receiving the file or patch, and signal an error if the checksums
do not match.  The checksum is the 128 bit MD5 checksum represented as
32 hex digits.  This response is optional, and is only used if the
client supports it (as judged by the @code{Valid-responses} request).

@item Copy-file @var{pathname} \n
Additional data: @var{newname} \n.  Copy file @var{pathname} to
@var{newname} in the same directory where it already is.  This does not
affect @code{CVS/Entries}.

@item Removed @var{pathname} \n
The file has been removed from the repository (this is the case where
cvs prints @samp{file foobar.c is no longer pertinent}).

@item Remove-entry @var{pathname} \n
The file needs its entry removed from @code{CVS/Entries}, but the file
itself is already gone (this happens in response to a @code{ci} request
which involves committing the removal of a file).

@item Set-static-directory @var{pathname} \n
This instructs the client to set the @code{Entries.Static} flag, which
it should then send back to the server in a @code{Static-directory}
request whenever the directory is operated on.  @var{pathname} ends in a
slash; its purpose is to specify a directory, not a file within a
directory.

@item Clear-static-directory @var{pathname} \n
Like @code{Set-static-directory}, but clear, not set, the flag.

@item Set-sticky @var{pathname} \n
Additional data: @var{tagspec} \n.  Tell the client to set a sticky tag
or date, which should be supplied with the @code{Sticky} request for
future operations.  @var{pathname} ends in a slash; its purpose is to
specify a directory, not a file within a directory.  The first character
of @var{tagspec} is @samp{T} for a tag, or @samp{D} for a date.  The
remainder of @var{tagspec} contains the actual tag or date.

@item Clear-sticky @var{pathname} \n
Clear any sticky tag or date set by @code{Set-sticky}.

@item Set-checkin-prog @var{dir} \n
Additional data: @var{prog} \n.  Tell the client to set a checkin
program, which should be supplied with the @code{Checkin-prog} request
for future operations.

@item Set-update-prog @var{dir} \n
Additional data: @var{prog} \n.  Tell the client to set an update
program, which should be supplied with the @code{Update-prog} request
for future operations.

@item Module-expansion @var{pathname} \n
Return a file or directory which is included in a particular module.
@var{pathname} is relative to cvsroot, unlike most pathnames in
responses.

@item M @var{text} \n
A one-line message for the user.

@item E @var{text} \n
Same as @code{M} but send to stderr not stdout.

@item error @var{errno-code} @samp{ } @var{text} \n
The command completed with an error.  @var{errno-code} is a symbolic
error code (e.g. @code{ENOENT}); if the server doesn't support this
feature, or if it's not appropriate for this particular message, it just
omits the errno-code (in that case there are two spaces after
@samp{error}).  Text is an error message such as that provided by
strerror(), or any other message the server wants to use.

@item ok \n
The command completed successfully.
@end table

@node Example
@section Example

Lines beginning with @samp{c>} are sent by the client; lines beginning
with @samp{s>} are sent by the server; lines beginning with @samp{#} are
not part of the actual exchange.

@example
c> Root /rel/cvsfiles
# In actual practice the lists of valid responses and requests would
# be longer
c> Valid-responses Updated Checked-in M ok error
c> valid-requests
s> Valid-requests Root co Modified Entry Repository ci Argument Argumentx
s> ok
# cvs co devo/foo
c> Argument devo/foo
c> co
s> Updated /rel/cvsfiles/devo/foo/foo.c
s> /foo.c/1.4/Mon Apr 19 15:36:47 1993 Mon Apr 19 15:36:47 1993//
s> 26
s> int mein () @{ abort (); @}
s> Updated /rel/cvsfiles/devo/foo/Makefile
s> /Makefile/1.2/Mon Apr 19 15:36:47 1993 Mon Apr 19 15:36:47 1993//
s> 28
s> foo: foo.c
s>         $(CC) -o foo $<
s> ok
# In actual practice the next part would be a separate connection.
# Here it is shown as part of the same one.
c> Repository /rel/cvsfiles/devo/foo
# foo.c relative to devo/foo just set as Repository.
c> Entry /foo.c/1.4/Mon Apr 19 15:36:47 1993 Mon Apr 19 15:36:47 1993//
c> Entry /Makefile/1.2/Mon Apr 19 15:36:47 1993 Mon Apr 19 15:36:47 1993//
c> Modified foo.c
c> 26
c> int main () @{ abort (); @}
# cvs ci -m <log message> foo.c
c> Argument -m
c> Argument Well, you see, it took me hours and hours to find this typo and I
c> Argumentx searched and searched and eventually had to ask John for help.
c> Argument foo.c
c> ci
s> Checked-in /rel/cvsfiles/devo/foo/foo.c
s> /foo.c/1.5/ Mon Apr 19 15:54:22 CDT 1993//
s> M Checking in foo.c;
s> M /cygint/rel/cvsfiles/devo/foo/foo.c,v  <--  foo.c
s> M new revision: 1.5; previous revision: 1.4
s> M done
s> ok
@end example
@bye