[This bug would appear to have been fixed by the following change. I don't know if anyone has reproduced the bug and verified this. 1998-06-21 Ian Lance Taylor * update.c (merge_files): Revert changes of 1998-06-19. Instead, register a merged file with a dummy time stamp. Only set last_register_time if we need to. (join_file): Likewise. Always register a merged file, not just when the merge fails. -kingdon, Jul 1998 CVS knows whether the file was merged. Can't it just put a bogus timestamp in Entries like client.c already does for a merged file? That seems a lot cleaner than sleeps. Note that this patch also includes some changes regarding "const" which is a separate issue and should be considered separately. -kingdon, May 1998] From: "Griswold, Victor" To: "'Cygnus:GNU-WIN32'" , "'bug-cvs@prep.ai.mit.edu'" Subject: CVS 1.9: RCS_merge Timestamp Anomaly Date: Wed, 5 Mar 1997 11:18:19 -0500 Gentlemen: Enclosed is a patch to the CVS 1.9 distribution which corrects a subtle problem with "cvs checkout -j". The problem is that, on fast workstations, a checkout followed by a merge can take place within the time quantum of the filesystem (1 second on Unix, 2 seconds on DOS/Windows FAT). The result is that, on a subsequent commit, cvs compares the checkout timestamp with the file timestamp (which are equal) and incorrectly believes that no merge had been done on the file. This patch also stabilizes running sanity.sh somewhat when executing on fast workstations, particularly ones utilizing the DOS/Windows FAT filesystem. The mechanism utilized by the patch is simply to check the file timestamp before the merge, then after the merge. If they remain the same, RCS_merge waits one second, touches the merged file, and checks again. This message is cross-posted to the cygnus gnu-win32 mailing list because of recent interest in CVS in that mailing list. Future bug-fixes will be posted only to the bug-cvs mailing list. For those interested in that mailing list, subscribe by sending "subscribe bug-cvs" in a mail message to bug-cvs-request@prep.ai.mit.edu. Victor J. Griswold, D.Sc. Aironet Wireless Communications, Inc. voice: 330-664-7987 fax: 330-664-7301 email: (MS-Mail) vgris@aironet.com (MIME) Victor.Griswold@pobox.com Index: CVS_src/ChangeLog diff -c CVS_src/ChangeLog:1.1.1.1 CVS_src/ChangeLog:1.1.1.1.2.1 *** CVS_src/ChangeLog:1.1.1.1 Wed Dec 31 19:00:49 1969 --- CVS_src/ChangeLog Wed Dec 31 18:56:32 1969 *************** *** 1,3 **** --- 1,10 ---- + Wed Mar 5 1997 Victor Griswold + * Fixed RCS_merge timestamp anomaly in which a merged file could + have the same timestamp as the original file. This can occur + on a fast workstation when executing "cvs checkout -j". The + result is that a later "cvs commit" fails to detect the merged + files because the timestamps are the same. + Tue Oct 1 14:32:44 1996 Jim Kingdon * NEWS, README: Revert changes regarding -D, -g, and A4. They Index: CVS_src/src/cvs.h diff -c CVS_src/src/cvs.h:1.1.1.1.2.1 CVS_src/src/cvs.h:1.1.1.1.2.2 *** CVS_src/src/cvs.h:1.1.1.1.2.1 Wed Dec 31 18:58:46 1969 --- CVS_src/src/cvs.h Wed Dec 31 18:56:45 1969 *************** *** 334,340 **** typedef enum direnter_type Dtype; #endif ! extern char *program_name, *program_path, *command_name; extern char *Rcsbin, *Tmpdir, *Editor; extern int cvsadmin_root; extern char *CurDir; --- 334,341 ---- typedef enum direnter_type Dtype; #endif ! extern const char *program_name; ! extern char *program_path, *command_name; extern char *Rcsbin, *Tmpdir, *Editor; extern int cvsadmin_root; extern char *CurDir; *************** *** 404,410 **** char *Short_Repository PROTO((char *repository)); char *gca PROTO((char *rev1, char *rev2)); char *getcaller PROTO((void)); ! char *time_stamp PROTO((char *file)); char *xmalloc PROTO((size_t bytes)); void *xrealloc PROTO((void *ptr, size_t bytes)); char *xstrdup PROTO((const char *str)); --- 405,411 ---- char *Short_Repository PROTO((char *repository)); char *gca PROTO((char *rev1, char *rev2)); char *getcaller PROTO((void)); ! char *time_stamp PROTO((const char *file)); char *xmalloc PROTO((size_t bytes)); void *xrealloc PROTO((void *ptr, size_t bytes)); char *xstrdup PROTO((const char *str)); *************** *** 422,428 **** int iswritable PROTO((const char *file)); int isaccessible PROTO((const char *file, const int mode)); int isabsolute PROTO((const char *filename)); ! char *last_component PROTO((char *path)); char *get_homedir PROTO ((void)); char *cvs_temp_name PROTO ((void)); --- 423,429 ---- int iswritable PROTO((const char *file)); int isaccessible PROTO((const char *file, const int mode)); int isabsolute PROTO((const char *filename)); ! const char *last_component PROTO((const char *path)); char *get_homedir PROTO ((void)); char *cvs_temp_name PROTO ((void)); Index: CVS_src/src/filesubr.c diff -c CVS_src/src/filesubr.c:1.1.1.1.2.2 CVS_src/src/filesubr.c:1.1.1.1.2.4 *** CVS_src/src/filesubr.c:1.1.1.1.2.2 Wed Dec 31 18:57:23 1969 --- CVS_src/src/filesubr.c Wed Dec 31 18:56:47 1969 *************** *** 834,844 **** /* Return a pointer into PATH's last component. */ ! char * ! last_component (char *path) { ! char *scan; ! char *last = 0; for (scan = path; *scan; scan++) if (ISDIRSEP (*scan)) --- 838,848 ---- /* Return a pointer into PATH's last component. */ ! const char * ! last_component (const char *path) { ! const char *scan; ! const char *last = 0; for (scan = path; *scan; scan++) if (ISDIRSEP (*scan)) Index: CVS_src/src/main.c diff -c CVS_src/src/main.c:1.1.1.1.2.1 CVS_src/src/main.c:1.1.1.1.2.2 *** CVS_src/src/main.c:1.1.1.1.2.1 Wed Dec 31 18:57:30 1969 --- CVS_src/src/main.c Wed Dec 31 18:56:48 1969 *************** *** 20,26 **** extern int gethostname (); #endif ! char *program_name; char *program_path; char *command_name; --- 20,26 ---- extern int gethostname (); #endif ! const char *program_name; char *program_path; char *command_name; Index: CVS_src/src/rcs.h diff -c CVS_src/src/rcs.h:1.1.1.1 CVS_src/src/rcs.h:1.1.1.1.2.1 *** CVS_src/src/rcs.h:1.1.1.1 Wed Dec 31 19:00:58 1969 --- CVS_src/src/rcs.h Wed Dec 31 18:56:49 1969 *************** *** 17,22 **** --- 17,23 ---- #define RCS_RCSMERGE "rcsmerge" #define RCS_MERGE_PAT "^>>>>>>> " /* runs "grep" with this pattern */ #define RCSEXT ",v" + #define RCSEXT_LEN (2) #define RCSPAT "*,v" #define RCSHEAD "head" #define RCSBRANCH "branch" Index: CVS_src/src/rcscmds.c diff -c CVS_src/src/rcscmds.c:1.1.1.1 CVS_src/src/rcscmds.c:1.1.1.1.2.1 *** CVS_src/src/rcscmds.c:1.1.1.1 Wed Dec 31 19:01:02 1969 --- CVS_src/src/rcscmds.c Wed Dec 31 18:56:50 1969 *************** *** 143,151 **** --- 143,161 ---- const char *rev2; { int status; + int delay_status; + const char *archiveName = last_component(path); + char *userFile; + char *timeBefore; + char *timeAfter; /* XXX - Do merge by hand instead of using rcsmerge, due to -k handling */ + userFile = xstrdup(archiveName); + userFile[strlen(archiveName) - RCSEXT_LEN] = '\0'; + + timeBefore = time_stamp(userFile); + run_setup ("%s%s -x,v/ %s -r%s -r%s %s", Rcsbin, RCS_RCSMERGE, options, rev1, rev2, path); status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL); *************** *** 160,165 **** --- 170,201 ---- } #endif + + timeAfter = time_stamp(userFile); + while (timeAfter && strcmp(timeBefore, timeAfter) == 0) { + free(timeAfter); + + if (trace) { + fprintf(stderr, "-> RCS_merge: timestamp of file %s is %s before and after merge.\n", + userFile, timeBefore); + } + + /* Force the time stamp on the merged file to be different */ + /* if the file was just checked out. This avoids confusing */ + /* Classify_File. */ + /**/ + sleep(1); + run_setup ("touch %s", userFile); + delay_status = run_exec (RUN_TTY, DEVNULL, RUN_TTY, RUN_NORMAL); + + timeAfter = time_stamp(userFile); + } + + free(userFile); + if (timeBefore) + free(timeBefore); + if (timeAfter) + free(timeAfter); return status; } Index: CVS_src/src/recurse.c diff -c CVS_src/src/recurse.c:1.1.1.1.2.1 CVS_src/src/recurse.c:1.1.1.1.2.2 *** CVS_src/src/recurse.c:1.1.1.1.2.1 Wed Dec 31 18:59:05 1969 --- CVS_src/src/recurse.c Wed Dec 31 18:56:51 1969 *************** *** 189,195 **** /* Now break out argv[i] into directory part (DIR) and file part (COMP). DIR and COMP will each point to a newly malloc'd string. */ dir = xstrdup (argv[i]); ! comp = last_component (dir); if (comp == dir) { /* no dir component. What we have is an implied "./" */ --- 189,195 ---- /* Now break out argv[i] into directory part (DIR) and file part (COMP). DIR and COMP will each point to a newly malloc'd string. */ dir = xstrdup (argv[i]); ! comp = (char*)last_component (dir); if (comp == dir) { /* no dir component. What we have is an implied "./" */ *************** *** 670,676 **** } /* put back update_dir */ ! cp = last_component (update_dir); if (cp > update_dir) cp[-1] = '\0'; else --- 670,676 ---- } /* put back update_dir */ ! cp = (char*)last_component (update_dir); if (cp > update_dir) cp[-1] = '\0'; else Index: CVS_src/src/vers_ts.c diff -c CVS_src/src/vers_ts.c:1.1.1.1 CVS_src/src/vers_ts.c:1.1.1.1.2.1 *** CVS_src/src/vers_ts.c:1.1.1.1 Wed Dec 31 19:01:03 1969 --- CVS_src/src/vers_ts.c Wed Dec 31 18:56:55 1969 *************** *** 274,280 **** */ char * time_stamp (file) ! char *file; { struct stat sb; char *cp; --- 274,280 ---- */ char * time_stamp (file) ! const char *file; { struct stat sb; char *cp;