]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/cvs/src/classify.c
Optimize the CVS bug #17168 fix by relaxing the conditions for update
[FreeBSD/FreeBSD.git] / contrib / cvs / src / classify.c
1 /*
2  * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
3  *
4  * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5  *                                  and others.
6  *
7  * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8  * Portions Copyright (C) 1989-1992, Brian Berliner
9  * 
10  * You may distribute under the terms of the GNU General Public License as
11  * specified in the README file that comes with the CVS source distribution.
12  * 
13  */
14
15 #include "cvs.h"
16
17 static void sticky_ck PROTO ((struct file_info *finfo, int aflag,
18                               Vers_TS * vers));
19
20
21
22 static inline int keywords_may_change PROTO ((int aflag, Vers_TS * vers));
23 static inline int
24 keywords_may_change (aflag, vers)
25     int aflag;
26     Vers_TS * vers;
27 {
28     int retval;
29
30     if (/* Options are different...  */
31         strcmp (vers->entdata->options, vers->options)
32         /* ...or...  */
33         || (/* ...clearing stickies...  */
34             aflag
35             /* ...and...  */
36             && (/* ...there used to be a tag which subs in Name keys...  */
37                 (vers->entdata->tag && !isdigit (vers->entdata->tag[0])
38                     && vers->tag && !isdigit (vers->tag[0])
39                     && strcmp (vers->entdata->tag, vers->tag))
40                 /* ...or there used to be a keyword mode which may be
41                  * changed by -A...
42                  */
43                 || (strlen (vers->entdata->options)
44                     && strcmp (vers->entdata->options, vers->options)
45                     && strcmp (vers->entdata->options, "-kkv")
46                     && strcmp (vers->entdata->options, "-kb"))))
47         /* ...or...  */
48         || (/* ...this is not commit...  */
49             strcmp (cvs_cmd_name, "commit")
50             /* ...and...  */
51             && (/* ...the tag is changing in a way that affects Name keys...  */
52                 (vers->entdata->tag && vers->tag
53                  && strcmp (vers->entdata->tag, vers->tag)
54                  && !(isdigit (vers->entdata->tag[0])
55                       && isdigit (vers->entdata->tag[0])))
56                 || (!vers->entdata->tag && vers->tag
57                     && !isdigit (vers->tag[0])))))
58         retval = 1;
59     else
60         retval = 0;
61
62     return retval;
63 }
64
65
66
67 /*
68  * Classify the state of a file
69  */
70 Ctype
71 Classify_File (finfo, tag, date, options, force_tag_match, aflag, versp,
72                pipeout)
73     struct file_info *finfo;
74     char *tag;
75     char *date;
76
77     /* Keyword expansion options.  Can be either NULL or "" to
78        indicate none are specified here.  */
79     char *options;
80
81     int force_tag_match;
82     int aflag;
83     Vers_TS **versp;
84     int pipeout;
85 {
86     Vers_TS *vers;
87     Ctype ret;
88
89     /* get all kinds of good data about the file */
90     vers = Version_TS (finfo, options, tag, date,
91                        force_tag_match, 0);
92
93     if (vers->vn_user == NULL)
94     {
95         /* No entry available, ts_rcs is invalid */
96         if (vers->vn_rcs == NULL)
97         {
98             /* there is no RCS file either */
99             if (vers->ts_user == NULL)
100             {
101                 /* there is no user file */
102                 /* FIXME: Why do we skip this message if vers->tag or
103                    vers->date is set?  It causes "cvs update -r tag98 foo"
104                    to silently do nothing, which is seriously confusing
105                    behavior.  "cvs update foo" gives this message, which
106                    is what I would expect.  */
107                 if (!force_tag_match || !(vers->tag || vers->date))
108                     if (!really_quiet)
109                         error (0, 0, "nothing known about %s", finfo->fullname);
110                 ret = T_UNKNOWN;
111             }
112             else
113             {
114                 /* there is a user file */
115                 /* FIXME: Why do we skip this message if vers->tag or
116                    vers->date is set?  It causes "cvs update -r tag98 foo"
117                    to silently do nothing, which is seriously confusing
118                    behavior.  "cvs update foo" gives this message, which
119                    is what I would expect.  */
120                 if (!force_tag_match || !(vers->tag || vers->date))
121                     if (!really_quiet)
122                         error (0, 0, "use `%s add' to create an entry for %s",
123                                program_name, finfo->fullname);
124                 ret = T_UNKNOWN;
125             }
126         }
127         else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
128         {
129             /* there is an RCS file, but it's dead */
130             if (vers->ts_user == NULL)
131                 ret = T_UPTODATE;
132             else
133             {
134                 error (0, 0, "use `%s add' to create an entry for %s",
135                        program_name, finfo->fullname);
136                 ret = T_UNKNOWN;
137             }
138         }
139         else if (!pipeout && vers->ts_user && No_Difference (finfo, vers))
140         {
141             /* the files were different so it is a conflict */
142             if (!really_quiet)
143                 error (0, 0, "move away %s; it is in the way",
144                        finfo->fullname);
145             ret = T_CONFLICT;
146         }
147         else
148             /* no user file or no difference, just checkout */
149             ret = T_CHECKOUT;
150     }
151     else if (strcmp (vers->vn_user, "0") == 0)
152     {
153         /* An entry for a new-born file; ts_rcs is dummy */
154
155         if (vers->ts_user == NULL)
156         {
157             if (pipeout)
158             {
159                 ret = T_CHECKOUT;
160             }
161             else
162             {
163                 /*
164                  * There is no user file, but there should be one; remove the
165                  * entry
166                  */
167                 if (!really_quiet)
168                     error (0, 0, "warning: new-born %s has disappeared",
169                            finfo->fullname);
170                 ret = T_REMOVE_ENTRY;
171             }
172         }
173         else if (vers->vn_rcs == NULL ||
174                  RCS_isdead (vers->srcfile, vers->vn_rcs))
175             /* No RCS file or RCS file revision is dead  */
176             ret = T_ADDED;
177         else
178         {
179             if (pipeout)
180             {
181                 ret = T_CHECKOUT;
182             }
183             else
184             {
185                 if (vers->srcfile->flags & INATTIC
186                     && vers->srcfile->flags & VALID)
187                 {
188                     /* This file has been added on some branch other than
189                        the one we are looking at.  In the branch we are
190                        looking at, the file was already valid.  */
191                     if (!really_quiet)
192                         error (0, 0,
193                            "conflict: %s has been added, but already exists",
194                                finfo->fullname);
195                 }
196                 else
197                 {
198                     /*
199                      * There is an RCS file, so someone else must have checked
200                      * one in behind our back; conflict
201                      */
202                     if (!really_quiet)
203                         error (0, 0,
204                            "conflict: %s created independently by second party",
205                                finfo->fullname);
206                 }
207                 ret = T_CONFLICT;
208             }
209         }
210     }
211     else if (vers->vn_user[0] == '-')
212     {
213         /* An entry for a removed file, ts_rcs is invalid */
214
215         if (vers->ts_user == NULL)
216         {
217             /* There is no user file (as it should be) */
218
219             if (vers->vn_rcs == NULL
220                 || RCS_isdead (vers->srcfile, vers->vn_rcs))
221             {
222
223                 /*
224                  * There is no RCS file; this is all-right, but it has been
225                  * removed independently by a second party; remove the entry
226                  */
227                 ret = T_REMOVE_ENTRY;
228             }
229             else if (strcmp (vers->vn_rcs, vers->vn_user + 1) == 0)
230                 /*
231                  * The RCS file is the same version as the user file was, and
232                  * that's OK; remove it
233                  */
234                 ret = T_REMOVED;
235             else if (pipeout)
236                 /*
237                  * The RCS file doesn't match the user's file, but it doesn't
238                  * matter in this case
239                  */
240                 ret = T_NEEDS_MERGE;
241             else
242             {
243
244                 /*
245                  * The RCS file is a newer version than the removed user file
246                  * and this is definitely not OK; make it a conflict.
247                  */
248                 if (!really_quiet)
249                     error (0, 0,
250                            "conflict: removed %s was modified by second party",
251                            finfo->fullname);
252                 ret = T_CONFLICT;
253             }
254         }
255         else
256         {
257             /* The user file shouldn't be there */
258             if (!really_quiet)
259                 error (0, 0, "%s should be removed and is still there",
260                        finfo->fullname);
261             ret = T_REMOVED;
262         }
263     }
264     else
265     {
266         /* A normal entry, TS_Rcs is valid */
267         if (vers->vn_rcs == NULL || RCS_isdead (vers->srcfile, vers->vn_rcs))
268         {
269             /* There is no RCS file */
270
271             if (vers->ts_user == NULL)
272             {
273                 /* There is no user file, so just remove the entry */
274                 if (!really_quiet)
275                     error (0, 0, "warning: %s is not (any longer) pertinent",
276                            finfo->fullname);
277                 ret = T_REMOVE_ENTRY;
278             }
279             else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
280             {
281
282                 /*
283                  * The user file is still unmodified, so just remove it from
284                  * the entry list
285                  */
286                 if (!really_quiet)
287                     error (0, 0, "%s is no longer in the repository",
288                            finfo->fullname);
289                 ret = T_REMOVE_ENTRY;
290             }
291             else if (No_Difference (finfo, vers))
292             {
293                 /* they are different -> conflict */
294                 if (!really_quiet)
295                     error (0, 0,
296                "conflict: %s is modified but no longer in the repository",
297                            finfo->fullname);
298                 ret = T_CONFLICT;
299             }
300             else
301             {
302                 /* they weren't really different */
303                 if (!really_quiet)
304                     error (0, 0,
305                            "warning: %s is not (any longer) pertinent",
306                            finfo->fullname);
307                 ret = T_REMOVE_ENTRY;
308             }
309         }
310         else if (strcmp (vers->vn_rcs, vers->vn_user) == 0)
311         {
312             /* The RCS file is the same version as the user file */
313
314             if (vers->ts_user == NULL)
315             {
316
317                 /*
318                  * There is no user file, so note that it was lost and
319                  * extract a new version
320                  */
321                 /* Comparing the cvs_cmd_name against "update", in
322                    addition to being an ugly way to operate, means
323                    that this message does not get printed by the
324                    server.  That might be considered just a straight
325                    bug, although there is one subtlety: that case also
326                    gets hit when a patch fails and the client fetches
327                    a file.  I'm not sure there is currently any way
328                    for the server to distinguish those two cases.  */
329                 if (strcmp (cvs_cmd_name, "update") == 0)
330                     if (!really_quiet)
331                         error (0, 0, "warning: %s was lost", finfo->fullname);
332                 ret = T_CHECKOUT;
333             }
334             else if (!strcmp (vers->ts_user,
335                               vers->ts_conflict
336                               ? vers->ts_conflict : vers->ts_rcs))
337             {
338
339                 /*
340                  * The user file is still unmodified, so nothing special at
341                  * all to do -- no lists updated, unless the sticky -k option
342                  * has changed.  If the sticky tag has changed, we just need
343                  * to re-register the entry
344                  */
345                 /* TODO: decide whether we need to check file permissions
346                    for a mismatch, and return T_CONFLICT if so. */
347                 if (keywords_may_change (aflag, vers))
348                     ret = T_PATCH;
349                 else if (vers->ts_conflict)
350                     ret = T_CONFLICT;
351                 else
352                 {
353                     ret = T_UPTODATE;
354                     sticky_ck (finfo, aflag, vers);
355                 }
356             }
357             else if (No_Difference (finfo, vers))
358             {
359
360                 /*
361                  * they really are different; modified if we aren't
362                  * changing any sticky -k options, else needs merge
363                  */
364 #ifdef XXX_FIXME_WHEN_RCSMERGE_IS_FIXED
365                 if (strcmp (vers->entdata->options ?
366                        vers->entdata->options : "", vers->options) == 0)
367                     ret = T_MODIFIED;
368                 else
369                     ret = T_NEEDS_MERGE;
370 #else
371                 /* Files with conflict markers and new timestamps fall through
372                  * here, but they need to.  T_CONFLICT is an error in
373                  * commit_fileproc, whereas T_CONFLICT with conflict markers
374                  * is caught but only warned about.  Similarly, update_fileproc
375                  * currently reregisters a file that was conflicted but lost
376                  * its markers.
377                  */
378                 ret = T_MODIFIED;
379                 sticky_ck (finfo, aflag, vers);
380 #endif
381             }
382             else if (strcmp (vers->entdata->options ?
383                        vers->entdata->options : "", vers->options) != 0)
384             {
385                 /* file has not changed; check out if -k changed */
386                 ret = T_CHECKOUT;
387             }
388             else
389             {
390
391                 /*
392                  * else -> note that No_Difference will Register the
393                  * file already for us, using the new tag/date. This
394                  * is the desired behaviour
395                  */
396                 ret = T_UPTODATE;
397             }
398         }
399         else
400         {
401             /* The RCS file is a newer version than the user file */
402
403             if (vers->ts_user == NULL)
404             {
405                 /* There is no user file, so just get it */
406
407                 /* See comment at other "update" compare, for more
408                    thoughts on this comparison.  */
409                 if (strcmp (cvs_cmd_name, "update") == 0)
410                     if (!really_quiet)
411                         error (0, 0, "warning: %s was lost", finfo->fullname);
412                 ret = T_CHECKOUT;
413             }
414             else if (strcmp (vers->ts_user, vers->ts_rcs) == 0)
415
416                 /*
417                  * The user file is still unmodified, so just get it as well
418                  */
419                 ret = T_PATCH;
420             else if (No_Difference (finfo, vers))
421                 /* really modified, needs to merge */
422                 ret = T_NEEDS_MERGE;
423             else
424                 ret = T_PATCH;
425         }
426     }
427
428     /* free up the vers struct, or just return it */
429     if (versp != (Vers_TS **) NULL)
430         *versp = vers;
431     else
432         freevers_ts (&vers);
433
434     /* return the status of the file */
435     return (ret);
436 }
437
438 static void
439 sticky_ck (finfo, aflag, vers)
440     struct file_info *finfo;
441     int aflag;
442     Vers_TS *vers;
443 {
444     if (aflag || vers->tag || vers->date)
445     {
446         char *enttag = vers->entdata->tag;
447         char *entdate = vers->entdata->date;
448
449         if ((enttag && vers->tag && strcmp (enttag, vers->tag)) ||
450             ((enttag && !vers->tag) || (!enttag && vers->tag)) ||
451             (entdate && vers->date && strcmp (entdate, vers->date)) ||
452             ((entdate && !vers->date) || (!entdate && vers->date)))
453         {
454             Register (finfo->entries, finfo->file, vers->vn_user, vers->ts_rcs,
455                       vers->options, vers->tag, vers->date, vers->ts_conflict);
456
457 #ifdef SERVER_SUPPORT
458             if (server_active)
459             {
460                 /* We need to update the entries line on the client side.
461                    It is possible we will later update it again via
462                    server_updated or some such, but that is OK.  */
463                 server_update_entries
464                   (finfo->file, finfo->update_dir, finfo->repository,
465                    strcmp (vers->ts_rcs, vers->ts_user) == 0 ?
466                    SERVER_UPDATED : SERVER_MERGED);
467             }
468 #endif
469         }
470     }
471 }