Fossil

Check-in [76f169fc]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Detect when the check-out contains missing files and filesystem objects that ought to be files but are not. Issue reasonable warnings.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 76f169fca6c040496b14bd37a9e2c5e00f0a797b
User & Date: drh 2009-12-18 00:29:51
References
2009-12-18
00:31 Fixed ticket [9ad1a540]: MISSING files are not handled gracefully plus 2 other changes artifact: 676ec85c user: drh
Context
2009-12-18
20:15
Add an "annotate" command to the command-line to suppliment the "annotate" web page. check-in: 73b7faa5 user: drh tags: trunk
00:29
Detect when the check-out contains missing files and filesystem objects that ought to be files but are not. Issue reasonable warnings. check-in: 76f169fc user: drh tags: trunk
2009-12-17
22:55
Enhancements to the "update" command. Missing files are reverted. One or more files can be specified on the "update" command line after the VERSION and only the files named will be updated. check-in: 2d996b08 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/checkin.c.

29
30
31
32
33
34
35



36
37




38
39

40
41
42
43
44
45
46
..
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
..
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
...
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
...
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
...
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
#include <assert.h>

/*
** Generate text describing all changes.  Prepend zPrefix to each line
** of output.
**
** We assume that vfile_check_signature has been run.



*/
static void status_report(Blob *report, const char *zPrefix){




  Stmt q;
  int nPrefix = strlen(zPrefix);

  db_prepare(&q, 
    "SELECT pathname, deleted, chnged, rid, coalesce(origname!=pathname,0)"
    "  FROM vfile "
    " WHERE file_is_selected(id)"
    "   AND (chnged OR deleted OR rid=0 OR pathname!=origname) ORDER BY 1"
  );
  while( db_step(&q)==SQLITE_ROW ){
................................................................................
    int isDeleted = db_column_int(&q, 1);
    int isChnged = db_column_int(&q,2);
    int isNew = db_column_int(&q,3)==0;
    int isRenamed = db_column_int(&q,4);
    char *zFullName = mprintf("%s/%s", g.zLocalRoot, zPathname);
    blob_append(report, zPrefix, nPrefix);
    if( isDeleted ){
      blob_appendf(report, "DELETED  %s\n", zPathname);

    }else if( access(zFullName, 0) ){






      blob_appendf(report, "MISSING  %s\n", zPathname);





    }else if( isNew ){
      blob_appendf(report, "ADDED    %s\n", zPathname);
    }else if( isDeleted ){
      blob_appendf(report, "DELETED  %s\n", zPathname);
    }else if( isChnged==2 ){
      blob_appendf(report, "UPDATED_BY_MERGE %s\n", zPathname);
    }else if( isChnged==3 ){
      blob_appendf(report, "ADDED_BY_MERGE %s\n", zPathname);
    }else if( isChnged==1 ){
      blob_appendf(report, "EDITED   %s\n", zPathname);
    }else if( isRenamed ){
      blob_appendf(report, "RENAMED  %s\n", zPathname);
    }
    free(zFullName);
  }
  db_finalize(&q);
  db_prepare(&q, "SELECT uuid FROM vmerge JOIN blob ON merge=rid"
                 " WHERE id=0");
  while( db_step(&q)==SQLITE_ROW ){
    blob_append(report, zPrefix, nPrefix);
    blob_appendf(report, "MERGED_WITH %s\n", db_column_text(&q, 0));
  }
  db_finalize(&q);



}

/*
** COMMAND: changes
**
** Usage: %fossil changes
**
................................................................................
*/
void changes_cmd(void){
  Blob report;
  int vid;
  db_must_be_within_tree();
  blob_zero(&report);
  vid = db_lget_int("checkout", 0);
  vfile_check_signature(vid);
  status_report(&report, "");
  blob_write_to_file(&report, "-");
}

/*
** COMMAND: status
**
** Usage: %fossil status
................................................................................
  int vid;
  Stmt q;
  int isBrief;

  isBrief = find_option("l","l", 0)==0;
  db_must_be_within_tree();
  vid = db_lget_int("checkout", 0);
  vfile_check_signature(vid);
  db_prepare(&q,
     "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)"
     "  FROM vfile"
     " ORDER BY 1"
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zPathname = db_column_text(&q,0);
................................................................................
    int isNew = db_column_int(&q,2)==0;
    int chnged = db_column_int(&q,3);
    int renamed = db_column_int(&q,4);
    char *zFullName = mprintf("%s/%s", g.zLocalRoot, zPathname);
    if( isBrief ){
      printf("%s\n", zPathname);
    }else if( isNew ){
      printf("ADDED     %s\n", zPathname);

    }else if( access(zFullName, 0) ){
      printf("MISSING   %s\n", zPathname);



    }else if( isDeleted ){
      printf("DELETED   %s\n", zPathname);
    }else if( chnged ){
      printf("EDITED    %s\n", zPathname);
    }else if( renamed ){
      printf("RENAMED   %s\n", zPathname);
    }else{
      printf("UNCHANGED %s\n", zPathname);
    }
    free(zFullName);
  }
  db_finalize(&q);
}

/*
................................................................................
  if( g.markPrivate ){
    blob_append(&text,
      "# PRIVATE BRANCH: This check-in will be private and will not sync to\n"
      "# repositories.\n"
      "#\n", -1
    );
  }
  status_report(&text, "# ");
  zEditor = db_get("editor", 0);
  if( zEditor==0 ){
    zEditor = getenv("VISUAL");
  }
  if( zEditor==0 ){
    zEditor = getenv("EDITOR");
  }







>
>
>

|
>
>
>
>


>







 







|
>
|
>
>
>
>
>
>
|
>
>
>
>
>

|

|





|

|











>
>
>







 







|
|







 







|







 







|
>
|
|
>
>
>

|

|

|

|







 







|







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
..
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
...
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
...
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
...
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
...
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
#include <assert.h>

/*
** Generate text describing all changes.  Prepend zPrefix to each line
** of output.
**
** We assume that vfile_check_signature has been run.
**
** If missingIsFatal is true, then any files that are missing or which
** are not true files results in a fatal error.
*/
static void status_report(
  Blob *report,          /* Append the status report here */
  const char *zPrefix,   /* Prefix on each line of the report */
  int missingIsFatal     /* MISSING and NOT_A_FILE are fatal errors */
){
  Stmt q;
  int nPrefix = strlen(zPrefix);
  int nErr = 0;
  db_prepare(&q, 
    "SELECT pathname, deleted, chnged, rid, coalesce(origname!=pathname,0)"
    "  FROM vfile "
    " WHERE file_is_selected(id)"
    "   AND (chnged OR deleted OR rid=0 OR pathname!=origname) ORDER BY 1"
  );
  while( db_step(&q)==SQLITE_ROW ){
................................................................................
    int isDeleted = db_column_int(&q, 1);
    int isChnged = db_column_int(&q,2);
    int isNew = db_column_int(&q,3)==0;
    int isRenamed = db_column_int(&q,4);
    char *zFullName = mprintf("%s/%s", g.zLocalRoot, zPathname);
    blob_append(report, zPrefix, nPrefix);
    if( isDeleted ){
      blob_appendf(report, "DELETED    %s\n", zPathname);
    }else if( !file_isfile(zFullName) ){
      if( access(zFullName, 0)==0 ){
        blob_appendf(report, "NOT_A_FILE %s\n", zPathname);
        if( missingIsFatal ){
          fossil_warning("not a file: %s", zPathname);
          nErr++;
        }
      }else{
        blob_appendf(report, "MISSING    %s\n", zPathname);
        if( missingIsFatal ){
          fossil_warning("missing file: %s", zPathname);
          nErr++;
        }
      }
    }else if( isNew ){
      blob_appendf(report, "ADDED      %s\n", zPathname);
    }else if( isDeleted ){
      blob_appendf(report, "DELETED    %s\n", zPathname);
    }else if( isChnged==2 ){
      blob_appendf(report, "UPDATED_BY_MERGE %s\n", zPathname);
    }else if( isChnged==3 ){
      blob_appendf(report, "ADDED_BY_MERGE %s\n", zPathname);
    }else if( isChnged==1 ){
      blob_appendf(report, "EDITED     %s\n", zPathname);
    }else if( isRenamed ){
      blob_appendf(report, "RENAMED    %s\n", zPathname);
    }
    free(zFullName);
  }
  db_finalize(&q);
  db_prepare(&q, "SELECT uuid FROM vmerge JOIN blob ON merge=rid"
                 " WHERE id=0");
  while( db_step(&q)==SQLITE_ROW ){
    blob_append(report, zPrefix, nPrefix);
    blob_appendf(report, "MERGED_WITH %s\n", db_column_text(&q, 0));
  }
  db_finalize(&q);
  if( nErr ){
    fossil_fatal("aborting due to prior errors");
  }
}

/*
** COMMAND: changes
**
** Usage: %fossil changes
**
................................................................................
*/
void changes_cmd(void){
  Blob report;
  int vid;
  db_must_be_within_tree();
  blob_zero(&report);
  vid = db_lget_int("checkout", 0);
  vfile_check_signature(vid, 0);
  status_report(&report, "", 0);
  blob_write_to_file(&report, "-");
}

/*
** COMMAND: status
**
** Usage: %fossil status
................................................................................
  int vid;
  Stmt q;
  int isBrief;

  isBrief = find_option("l","l", 0)==0;
  db_must_be_within_tree();
  vid = db_lget_int("checkout", 0);
  vfile_check_signature(vid, 0);
  db_prepare(&q,
     "SELECT pathname, deleted, rid, chnged, coalesce(origname!=pathname,0)"
     "  FROM vfile"
     " ORDER BY 1"
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zPathname = db_column_text(&q,0);
................................................................................
    int isNew = db_column_int(&q,2)==0;
    int chnged = db_column_int(&q,3);
    int renamed = db_column_int(&q,4);
    char *zFullName = mprintf("%s/%s", g.zLocalRoot, zPathname);
    if( isBrief ){
      printf("%s\n", zPathname);
    }else if( isNew ){
      printf("ADDED      %s\n", zPathname);
    }else if( !file_isfile(zFullName) ){
      if( access(zFullName, 0)==0 ){
        printf("NOT_A_FILE %s\n", zPathname);
      }else{
        printf("MISSING    %s\n", zPathname);
      }
    }else if( isDeleted ){
      printf("DELETED    %s\n", zPathname);
    }else if( chnged ){
      printf("EDITED     %s\n", zPathname);
    }else if( renamed ){
      printf("RENAMED    %s\n", zPathname);
    }else{
      printf("UNCHANGED  %s\n", zPathname);
    }
    free(zFullName);
  }
  db_finalize(&q);
}

/*
................................................................................
  if( g.markPrivate ){
    blob_append(&text,
      "# PRIVATE BRANCH: This check-in will be private and will not sync to\n"
      "# repositories.\n"
      "#\n", -1
    );
  }
  status_report(&text, "# ", 1);
  zEditor = db_get("editor", 0);
  if( zEditor==0 ){
    zEditor = getenv("VISUAL");
  }
  if( zEditor==0 ){
    zEditor = getenv("EDITOR");
  }

Changes to src/checkout.c.

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
**     2:   There is no existing checkout
*/
int unsaved_changes(void){
  int vid;
  db_must_be_within_tree();
  vid = db_lget_int("checkout",0);
  if( vid==0 ) return 2;
  vfile_check_signature(vid);
  return db_exists("SELECT 1 FROM vfile WHERE chnged"
                   " OR coalesce(origname!=pathname,0)");
}

/*
** Undo the current check-out.  Unlink all files from the disk.
** Clear the VFILE table.







|







37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
**     2:   There is no existing checkout
*/
int unsaved_changes(void){
  int vid;
  db_must_be_within_tree();
  vid = db_lget_int("checkout",0);
  if( vid==0 ) return 2;
  vfile_check_signature(vid, 1);
  return db_exists("SELECT 1 FROM vfile WHERE chnged"
                   " OR coalesce(origname!=pathname,0)");
}

/*
** Undo the current check-out.  Unlink all files from the disk.
** Clear the VFILE table.

Changes to src/diffcmd.c.

205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
*/
static void diff_all_against_disk(const char *zFrom, const char *zDiffCmd){
  int vid;
  Blob sql;
  Stmt q;

  vid = db_lget_int("checkout", 0);
  vfile_check_signature(vid);
  blob_zero(&sql);
  db_begin_transaction();
  if( zFrom ){
    int rid = name_to_rid(zFrom);
    if( !is_a_version(rid) ){
      fossil_fatal("no such check-in: %s", zFrom);
    }







|







205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
*/
static void diff_all_against_disk(const char *zFrom, const char *zDiffCmd){
  int vid;
  Blob sql;
  Stmt q;

  vid = db_lget_int("checkout", 0);
  vfile_check_signature(vid, 1);
  blob_zero(&sql);
  db_begin_transaction();
  if( zFrom ){
    int rid = name_to_rid(zFrom);
    if( !is_a_version(rid) ){
      fossil_fatal("no such check-in: %s", zFrom);
    }

Changes to src/merge.c.

75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
  if( pid<=0 ){
    fossil_panic("cannot find a common ancestor between the current"
                 "checkout and %s", g.argv[2]);
  }
  if( pid>1 && !db_exists("SELECT 1 FROM plink WHERE cid=%d", pid) ){
    fossil_panic("not a version: record #%d", mid);
  }
  vfile_check_signature(vid);
  db_begin_transaction();
  undo_begin();
  load_vfile_from_rid(mid);
  load_vfile_from_rid(pid);

  /*
  ** The vfile.pathname field is used to match files against each other.  The







|







75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
  if( pid<=0 ){
    fossil_panic("cannot find a common ancestor between the current"
                 "checkout and %s", g.argv[2]);
  }
  if( pid>1 && !db_exists("SELECT 1 FROM plink WHERE cid=%d", pid) ){
    fossil_panic("not a version: record #%d", mid);
  }
  vfile_check_signature(vid, 1);
  db_begin_transaction();
  undo_begin();
  load_vfile_from_rid(mid);
  load_vfile_from_rid(pid);

  /*
  ** The vfile.pathname field is used to match files against each other.  The

Changes to src/update.c.

116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
    }
    tid = db_int(0, "SELECT rid FROM leaves, event"
                    " WHERE event.objid=leaves.rid"
                    " ORDER BY event.mtime DESC"); 
  }

  db_begin_transaction();
  vfile_check_signature(vid);
  undo_begin();
  load_vfile_from_rid(tid);

  /*
  ** The record.fn field is used to match files against each other.  The
  ** FV table contains one row for each each unique filename in
  ** in the current checkout, the pivot, and the version being merged.







|







116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
    }
    tid = db_int(0, "SELECT rid FROM leaves, event"
                    " WHERE event.objid=leaves.rid"
                    " ORDER BY event.mtime DESC"); 
  }

  db_begin_transaction();
  vfile_check_signature(vid, 1);
  undo_begin();
  load_vfile_from_rid(tid);

  /*
  ** The record.fn field is used to match files against each other.  The
  ** FV table contains one row for each each unique filename in
  ** in the current checkout, the pivot, and the version being merged.

Changes to src/vfile.c.

140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
...
166
167
168
169
170
171
172

173
174
175
176

177
178
179
180
181
182
183
184
** Set the VFILE.CHNGED field on every file that has changed.  Also 
** set VFILE.CHNGED on every folder that contains a file or folder 
** that has changed.
**
** If VFILE.DELETED is null or if VFILE.RID is zero, then we can assume
** the file has changed without having the check the on-disk image.
*/
void vfile_check_signature(int vid){
  int nErr = 0;
  Stmt q;
  Blob fileCksum, origCksum;
  int checkMtime = db_get_boolean("mtime-changes", 0);

  db_begin_transaction();
  db_prepare(&q, "SELECT id, %Q || pathname,"
................................................................................
    id = db_column_int(&q, 0);
    zName = db_column_text(&q, 1);
    rid = db_column_int(&q, 2);
    isDeleted = db_column_int(&q, 3);
    oldChnged = db_column_int(&q, 4);
    oldMtime = db_column_int64(&q, 6);
    if( !file_isfile(zName) && file_size(zName)>=0 ){

      fossil_warning("not a ordinary file: %s", zName);
      nErr++;
      continue;
    }

    if( oldChnged>=2 ){
      chnged = oldChnged;
    }else if( isDeleted || rid==0 ){
      chnged = 1;
    }
    if( chnged!=1 ){
      currentMtime = file_mtime(zName);
    }







|







 







>
|
|
<
|
>
|







140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
...
166
167
168
169
170
171
172
173
174
175

176
177
178
179
180
181
182
183
184
185
** Set the VFILE.CHNGED field on every file that has changed.  Also 
** set VFILE.CHNGED on every folder that contains a file or folder 
** that has changed.
**
** If VFILE.DELETED is null or if VFILE.RID is zero, then we can assume
** the file has changed without having the check the on-disk image.
*/
void vfile_check_signature(int vid, int notFileIsFatal){
  int nErr = 0;
  Stmt q;
  Blob fileCksum, origCksum;
  int checkMtime = db_get_boolean("mtime-changes", 0);

  db_begin_transaction();
  db_prepare(&q, "SELECT id, %Q || pathname,"
................................................................................
    id = db_column_int(&q, 0);
    zName = db_column_text(&q, 1);
    rid = db_column_int(&q, 2);
    isDeleted = db_column_int(&q, 3);
    oldChnged = db_column_int(&q, 4);
    oldMtime = db_column_int64(&q, 6);
    if( !file_isfile(zName) && file_size(zName)>=0 ){
      if( notFileIsFatal ){
        fossil_warning("not a ordinary file: %s", zName);
        nErr++;

      }
      chnged = 1;
    }else if( oldChnged>=2 ){
      chnged = oldChnged;
    }else if( isDeleted || rid==0 ){
      chnged = 1;
    }
    if( chnged!=1 ){
      currentMtime = file_mtime(zName);
    }