Fixed a problem where the "listp" pointer in make_exclude() might become
[rsync/rsync-patches.git] / filter.diff
CommitLineData
8a529471
WD
1After applying this patch and running configure, you MUST run this
2command before "make":
3
4 make proto
5
524989ae
WD
6This patch adds the ability to merge rules into your excludes/includes
7using a ". FILE" idiom. If you specify a name without slashes, that
8filename will be looked for in every subdirectory that rsync visits,
0c7d1fd8
WD
9and the rules found in that subdirectory's file will affect that dir
10and its subdirectories.
a55d21aa
WD
11
12For example:
13
14 rsync -av --exclude='. .excl' from/ to
15
16The above will look for a file named ".excl" in every directory of the
524989ae
WD
17hierarchy that rsync visits, and it will exclude (by default) names
18based on the rules found therein. If one of the .excl files contains
19this:
a55d21aa
WD
20
21 + *.c
524989ae
WD
22 . .excl2
23 . ./.excl3
24 *.o
a55d21aa 25
524989ae
WD
26Then the file ".excl2" will also be read in the current dir, and all
27subdirs of the current dir. The file ".excl3" would just be read in
28for the current dir because its name contained a slash.
a55d21aa
WD
29
30..wayne..
31
7b22909b
WD
32--- exclude.c 16 May 2004 14:08:34 -0000 1.79
33+++ exclude.c 16 May 2004 22:04:31 -0000
0c7d1fd8 34@@ -30,14 +30,54 @@ extern int verbose;
a55d21aa
WD
35 extern int eol_nulls;
36 extern int list_only;
37 extern int recurse;
38+extern int io_error;
39+extern int sanitize_paths;
40
41 extern char curr_dir[];
42
43-struct exclude_list_struct exclude_list = { 0, 0, "" };
524989ae 44-struct exclude_list_struct local_exclude_list = { 0, 0, "per-dir .cvsignore " };
a55d21aa 45-struct exclude_list_struct server_exclude_list = { 0, 0, "server " };
7cb7ae4e
WD
46+struct exclude_list_struct exclude_list = { 0, 0, 0, "" };
47+struct exclude_list_struct server_exclude_list = { 0, 0, 0, "server " };
a55d21aa
WD
48 char *exclude_path_prefix = NULL;
49
524989ae 50+struct exclude_list_root {
d1e7c1c8 51+ struct exclude_list_struct *array;
524989ae
WD
52+ int cnt;
53+} local_lists;
54+
a55d21aa
WD
55+static char dirbuf[MAXPATHLEN];
56+static unsigned int dirbuf_offset = 0;
a55d21aa 57+
d1e7c1c8
WD
58+/* Each exclude_list_struct describes a singly-linked list by keeping track
59+ * of both the head and tail pointers. The list is slightly unusual in that
60+ * a parent-dir's content can be appended to the end of the local list in a
61+ * special way: the last item in the local list has its "next" pointer set
0c7d1fd8 62+ * to point to the inherited list, but the local list's tail pointer points
d1e7c1c8 63+ * at the end of the local list. Thus, if the local list is empty, the head
0c7d1fd8 64+ * will be pointing at the inherited content but the tail will be NULL. To
d1e7c1c8
WD
65+ * help you visualize this, here are the possible list arrangements:
66+ *
67+ * Completely Empty Local Content Only
68+ * ================================== ====================================
69+ * head -> NULL head -> Local1 -> Local2 -> NULL
70+ * tail -> NULL tail -------------^
71+ *
72+ * Inherited Content Only Both Local and Inherited Content
73+ * ================================== ====================================
74+ * head -> Parent1 -> Parent2 -> NULL head -> L1 -> L2 -> P1 -> P2 -> NULL
75+ * tail -> NULL tail ---------^
76+ *
77+ * This means that anyone wanting to traverse the whole list to USE it just
78+ * needs to start at the head and use the "next" pointers until it goes
79+ * NULL. To add new local content, we insert the item after the tail item
80+ * and update the tail (obviously, if "tail" was NULL, we insert it at the
81+ * head). To clear the local list, WE MUST NOT FREE THE INHERITED CONTENT
bc95f62b
WD
82+ * because it is shared between the current list and our parent list(s).
83+ * The easiest way to handle this is to simply truncate the list after the
84+ * tail item and then free the local list from the head. When inheriting
85+ * the list for a new local dir, we just save off the exclude_list_struct
0c7d1fd8 86+ * values (so we can pop back to them later) and set the tail to NULL.
d1e7c1c8
WD
87+ */
88+
0c7d1fd8
WD
89 /** Build an exclude structure given an exclude pattern. */
90 static void make_exclude(struct exclude_list_struct *listp, const char *pat,
91 unsigned int pat_len, unsigned int mflags)
92@@ -46,6 +86,24 @@ static void make_exclude(struct exclude_
a55d21aa 93 const char *cp;
0c7d1fd8
WD
94 unsigned int ex_len;
95
524989ae 96+ if (mflags & MATCHFLG_MERGE_FILE) {
a55d21aa
WD
97+ struct exclude_struct *ex;
98+ /* If the local include file was already mentioned, don't
524989ae 99+ * add it again. */
a55d21aa 100+ for (ex = listp->head; ex; ex = ex->next) {
0c7d1fd8 101+ if (ex->match_flags & MATCHFLG_MERGE_FILE
a55d21aa 102+ && strlen(ex->pattern) == pat_len
524989ae 103+ && strncmp(ex->pattern, pat, pat_len) == 0)
a55d21aa
WD
104+ return;
105+ }
524989ae
WD
106+ if ((pat_len == 10 || (pat_len > 10 && pat[pat_len-11] == '/'))
107+ && strncmp(pat+pat_len-10, ".cvsignore", 10) == 0) {
a55d21aa
WD
108+ mflags |= MATCHFLG_CVSIGNORE;
109+ mflags &= ~MATCHFLG_INCLUDE;
110+ } else
111+ mflags &= ~MATCHFLG_CVSIGNORE;
112+ }
0c7d1fd8 113+
a55d21aa
WD
114 ret = new(struct exclude_struct);
115 if (!ret)
116 out_of_memory("make_exclude");
7b22909b
WD
117@@ -81,14 +139,35 @@ static void make_exclude(struct exclude_
118 mflags |= MATCHFLG_DIRECTORY;
a55d21aa
WD
119 }
120
7b22909b 121- for (cp = ret->pattern; (cp = strchr(cp, '/')) != NULL; cp++)
bc95f62b 122- ret->slash_cnt++;
524989ae 123+ if (mflags & MATCHFLG_MERGE_FILE) {
7b22909b 124+ struct exclude_list_struct *lp = local_lists.array;
524989ae 125+ int ndx = local_lists.cnt++;
d1e7c1c8 126+ local_lists.array = realloc_array(local_lists.array,
524989ae 127+ struct exclude_list_struct, local_lists.cnt);
d1e7c1c8 128+ if (!local_lists.array)
a55d21aa 129+ out_of_memory("make_exclude");
7b22909b
WD
130+ /* If "listp" was pointing inside the memory we just
131+ * reallocated, update it. */
132+ if (ndx && listp >= lp && listp < lp + ndx)
133+ listp = &local_lists.array[listp - lp];
d1e7c1c8 134+ lp = &local_lists.array[ndx];
7cb7ae4e 135+ lp->head = lp->tail = NULL;
bc95f62b 136+ lp->parent = ret;
524989ae 137+ if (asprintf(&lp->debug_type, "per-dir %s ", ret->pattern) < 0)
a55d21aa 138+ out_of_memory("make_exclude");
7b22909b
WD
139+ ret->u.array_index = ndx;
140+ } else {
141+ for (cp = ret->pattern; (cp = strchr(cp, '/')) != NULL; cp++)
142+ ret->u.slash_cnt++;
abe86b1f 143+ }
7b22909b 144
0c7d1fd8 145 ret->match_flags = mflags;
a55d21aa 146
7b22909b
WD
147- if (!listp->tail)
148+ if (!listp->tail) {
149+ ret->next = listp->head;
150 listp->head = listp->tail = ret;
151- else {
152+ } else {
153+ ret->next = listp->tail->next;
154 listp->tail->next = ret;
155 listp->tail = ret;
156 }
157@@ -100,18 +179,90 @@ static void free_exclude(struct exclude_
a55d21aa
WD
158 free(ex);
159 }
160
161-void free_exclude_list(struct exclude_list_struct *listp)
162+static void free_exclude_list(struct exclude_list_struct *listp)
163 {
7cb7ae4e
WD
164- struct exclude_struct *ent, *next;
165-
166- for (ent = listp->head; ent; ent = next) {
167- next = ent->next;
168- free_exclude(ent);
169+ if (listp->tail) {
170+ struct exclude_struct *ent, *next;
bc95f62b 171+ /* Truncate any inherited items from the local list. */
7cb7ae4e
WD
172+ listp->tail->next = NULL;
173+ for (ent = listp->head; ent; ent = next) {
174+ next = ent->next;
175+ free_exclude(ent);
176+ }
524989ae
WD
177 }
178
7cb7ae4e
WD
179 listp->head = listp->tail = NULL;
180 }
a55d21aa 181
a55d21aa
WD
182+void *push_local_excludes(char *fname, unsigned int offset)
183+{
184+ int i;
524989ae
WD
185+ struct exclude_list_root *push = new_array(struct exclude_list_root, 1);
186+
187+ if (!push)
a55d21aa
WD
188+ out_of_memory("push_local_excludes");
189+
524989ae 190+ push->cnt = local_lists.cnt;
d1e7c1c8
WD
191+ push->array = new_array(struct exclude_list_struct, local_lists.cnt);
192+ if (!push->array)
524989ae 193+ out_of_memory("push_local_excludes");
a55d21aa 194+
d1e7c1c8 195+ memcpy(push->array, local_lists.array,
524989ae
WD
196+ sizeof (struct exclude_list_struct) * local_lists.cnt);
197+
d1e7c1c8
WD
198+ /* Make it easy to construct the full path for a merge-file that was
199+ * specified with a relative path by saving off the current dir. */
a55d21aa
WD
200+ memcpy(dirbuf, fname, offset);
201+ dirbuf_offset = offset;
202+
524989ae 203+ for (i = 0; i < local_lists.cnt; i++) {
d1e7c1c8 204+ struct exclude_list_struct *listp = &local_lists.array[i];
a55d21aa
WD
205+ char *file = listp->parent->pattern;
206+ int flags;
524989ae
WD
207+
208+ if (verbose > 2) {
209+ rprintf(FINFO, "[%s] pushing %sexclude list\n",
210+ who_am_i(), listp->debug_type);
211+ }
a55d21aa 212+ if (listp->parent->match_flags & MATCHFLG_CVSIGNORE) {
bc95f62b 213+ listp->head = NULL; /* Subdirs don't inherit rules. */
ee1af13c 214+ flags = XFLG_WORD_SPLIT | XFLG_WORDS_ONLY;
a55d21aa 215+ } else {
524989ae
WD
216+ flags = listp->parent->match_flags & MATCHFLG_INCLUDE
217+ ? XFLG_DEF_INCLUDE : 0;
a55d21aa 218+ }
bc95f62b 219+ listp->tail = NULL; /* Signals no local content. */
a55d21aa 220+ if (strlcpy(fname + offset, file, MAXPATHLEN - offset)
524989ae 221+ < MAXPATHLEN - offset)
a55d21aa 222+ add_exclude_file(listp, fname, flags);
524989ae 223+ else {
a55d21aa
WD
224+ io_error |= IOERR_GENERAL;
225+ rprintf(FINFO,
226+ "cannot add local excludes in long-named directory %s\n",
227+ full_fname(fname));
228+ }
229+ }
230+
524989ae 231+ return (void*)push;
a55d21aa
WD
232+}
233+
234+void pop_local_excludes(void *mem)
235+{
236+ int i;
524989ae
WD
237+
238+ for (i = 0; i < local_lists.cnt; i++) {
d1e7c1c8 239+ struct exclude_list_struct *listp = &local_lists.array[i];
524989ae
WD
240+ if (verbose > 2) {
241+ rprintf(FINFO, "[%s] popping %sexclude list\n",
242+ who_am_i(), listp->debug_type);
243+ }
a55d21aa
WD
244+ free_exclude_list(listp);
245+ }
d1e7c1c8 246+ free(local_lists.array);
524989ae 247+ local_lists = *(struct exclude_list_root*)mem;
a55d21aa 248+ free(mem);
7cb7ae4e
WD
249+}
250+
a55d21aa 251 static int check_one_exclude(char *name, struct exclude_struct *ex,
7cb7ae4e
WD
252 int name_is_dir)
253 {
7b22909b 254@@ -122,7 +273,7 @@ static int check_one_exclude(char *name,
bc95f62b
WD
255 /* If the pattern does not have any slashes AND it does not have
256 * a "**" (which could match a slash), then we just match the
257 * name portion of the path. */
258- if (!ex->slash_cnt && !(ex->match_flags & MATCHFLG_WILD2)) {
259+ if (!ex->u.slash_cnt && !(ex->match_flags & MATCHFLG_WILD2)) {
260 if ((p = strrchr(name,'/')) != NULL)
261 name = p+1;
262 }
7b22909b 263@@ -148,9 +299,9 @@ static int check_one_exclude(char *name,
bc95f62b
WD
264 if (ex->match_flags & MATCHFLG_WILD) {
265 /* A non-anchored match with an infix slash and no "**"
266 * needs to match the last slash_cnt+1 name elements. */
0c7d1fd8
WD
267- if (!match_start && ex->slash_cnt
268+ if (!match_start && ex->u.slash_cnt
269 && !(ex->match_flags & MATCHFLG_WILD2)) {
bc95f62b
WD
270- int cnt = ex->slash_cnt + 1;
271+ int cnt = ex->u.slash_cnt + 1;
272 for (p = name + strlen(name) - 1; p >= name; p--) {
273 if (*p == '/' && !--cnt)
274 break;
7b22909b 275@@ -221,6 +372,14 @@ int check_exclude(struct exclude_list_st
a55d21aa
WD
276 struct exclude_struct *ent;
277
278 for (ent = listp->head; ent; ent = ent->next) {
524989ae 279+ if (ent->match_flags & MATCHFLG_MERGE_FILE) {
a55d21aa 280+ struct exclude_list_struct *lp
bc95f62b 281+ = &local_lists.array[ent->u.array_index];
a55d21aa
WD
282+ int rc = check_exclude(lp, name, name_is_dir);
283+ if (rc)
284+ return rc;
285+ continue;
286+ }
287 if (check_one_exclude(name, ent, name_is_dir)) {
288 report_exclude_result(name, ent, name_is_dir,
289 listp->debug_type);
7b22909b 290@@ -254,11 +413,16 @@ static const char *get_exclude_tok(const
a55d21aa
WD
291 p = (const char *)s;
292 }
293
294- /* Is this a '+' or '-' followed by a space (not whitespace)? */
295+ /* Is this a +/-/. followed by a space (not whitespace)? */
ee1af13c 296 if (!(xflags & XFLG_WORDS_ONLY)
a55d21aa 297- && (*s == '-' || *s == '+') && s[1] == ' ') {
a55d21aa 298+ && (*s == '-' || *s == '+' || *s == '.') && s[1] == ' ') {
0c7d1fd8
WD
299 if (*s == '+')
300 mflags |= MATCHFLG_INCLUDE;
a55d21aa 301+ else if (*s == '.') {
524989ae 302+ mflags |= MATCHFLG_MERGE_FILE;
a55d21aa
WD
303+ if (xflags & XFLG_DEF_INCLUDE)
304+ mflags |= MATCHFLG_INCLUDE;
305+ }
306 s += 2;
0c7d1fd8
WD
307 } else if (xflags & XFLG_DEF_INCLUDE)
308 mflags |= MATCHFLG_INCLUDE;
7b22909b 309@@ -307,11 +471,42 @@ void add_exclude(struct exclude_list_str
0c7d1fd8
WD
310 continue;
311 }
a55d21aa 312
524989ae 313+ if (mflags & MATCHFLG_MERGE_FILE) {
a55d21aa 314+ char name[MAXPATHLEN];
bc95f62b
WD
315+ if (pat_len >= sizeof name) {
316+ rprintf(FERROR,
317+ "merge filename too long: %s\n", cp);
318+ continue;
319+ }
a55d21aa 320+ strlcpy(name, cp, pat_len+1);
524989ae 321+ if (strchr(name, '/') != NULL) {
a55d21aa
WD
322+ if (sanitize_paths)
323+ sanitize_path(name, curr_dir);
324+ if (*name == '/')
325+ cp = name;
326+ else {
327+ if (strlcpy(dirbuf + dirbuf_offset,
328+ name, MAXPATHLEN - dirbuf_offset)
bc95f62b
WD
329+ >= MAXPATHLEN - dirbuf_offset) {
330+ rprintf(FERROR,
331+ "merge filename too long: %s...\n",
332+ dirbuf);
333+ continue;
334+ }
a55d21aa
WD
335+ cp = dirbuf;
336+ }
337+ add_exclude_file(listp, cp,
338+ xflags | XFLG_FATAL_ERRORS);
339+ continue;
0c7d1fd8
WD
340+ }
341+ }
a55d21aa 342+
0c7d1fd8
WD
343 make_exclude(listp, cp, pat_len, mflags);
344
345 if (verbose > 2) {
346- rprintf(FINFO, "[%s] add_exclude(%.*s, %s%sclude)\n",
a55d21aa 347+ rprintf(FINFO, "[%s] add_exclude(%.*s, %s%s%sclude)\n",
0c7d1fd8 348 who_am_i(), pat_len, cp, listp->debug_type,
524989ae 349+ mflags & MATCHFLG_MERGE_FILE ? "FILE " : "",
0c7d1fd8 350 mflags & MATCHFLG_INCLUDE ? "in" : "ex");
a55d21aa 351 }
0c7d1fd8 352 }
7b22909b 353@@ -403,7 +598,11 @@ void send_exclude_list(int f)
0c7d1fd8 354 if (ent->match_flags & MATCHFLG_INCLUDE) {
a55d21aa
WD
355 write_int(f, l + 2);
356 write_buf(f, "+ ", 2);
357- } else if ((*p == '-' || *p == '+') && p[1] == ' ') {
524989ae 358+ } else if (ent->match_flags & MATCHFLG_MERGE_FILE) {
a55d21aa
WD
359+ write_int(f, l + 2);
360+ write_buf(f, ". ", 2);
361+ } else if ((*p == '-' || *p == '+' || *p == '.')
362+ && p[1] == ' ') {
363 write_int(f, l + 2);
364 write_buf(f, "- ", 2);
365 } else
7b22909b 366@@ -444,6 +643,7 @@ void add_cvs_excludes(void)
a55d21aa
WD
367 char fname[MAXPATHLEN];
368 char *p;
369
370+ add_exclude(&exclude_list, ". .cvsignore", 0);
371 add_exclude(&exclude_list, default_cvsignore,
ee1af13c 372 XFLG_WORD_SPLIT | XFLG_WORDS_ONLY);
a55d21aa 373
bc95f62b 374--- flist.c 15 May 2004 19:31:10 -0000 1.223
7b22909b 375+++ flist.c 16 May 2004 22:04:31 -0000
a55d21aa
WD
376@@ -39,8 +39,6 @@ extern int module_id;
377 extern int ignore_errors;
378 extern int numeric_ids;
379
380-extern int cvs_exclude;
381-
382 extern int recurse;
383 extern char curr_dir[MAXPATHLEN];
384 extern char *files_from;
7cb7ae4e 385@@ -65,7 +63,6 @@ extern int write_batch;
a55d21aa
WD
386
387 extern struct exclude_list_struct exclude_list;
388 extern struct exclude_list_struct server_exclude_list;
389-extern struct exclude_list_struct local_exclude_list;
390
391 int io_error;
392
7cb7ae4e 393@@ -210,8 +207,6 @@ int link_stat(const char *path, STRUCT_S
a55d21aa
WD
394 */
395 static int check_exclude_file(char *fname, int is_dir, int exclude_level)
396 {
397- int rc;
398-
399 #if 0 /* This currently never happens, so avoid a useless compare. */
400 if (exclude_level == NO_EXCLUDES)
401 return 0;
7cb7ae4e 402@@ -233,10 +228,7 @@ static int check_exclude_file(char *fnam
a55d21aa
WD
403 if (exclude_level != ALL_EXCLUDES)
404 return 0;
405 if (exclude_list.head
406- && (rc = check_exclude(&exclude_list, fname, is_dir)) != 0)
407- return rc < 0;
408- if (local_exclude_list.head
409- && check_exclude(&local_exclude_list, fname, is_dir) < 0)
410+ && check_exclude(&exclude_list, fname, is_dir) < 0)
411 return 1;
412 return 0;
413 }
7cb7ae4e 414@@ -946,15 +938,7 @@ void send_file_name(int f, struct file_l
a55d21aa
WD
415
416 if (recursive && S_ISDIR(file->mode)
417 && !(file->flags & FLAG_MOUNT_POINT)) {
418- struct exclude_list_struct last_list = local_exclude_list;
419- local_exclude_list.head = local_exclude_list.tail = NULL;
420 send_directory(f, flist, f_name_to(file, fbuf));
7cb7ae4e
WD
421- if (verbose > 2) {
422- rprintf(FINFO, "[%s] popping %sexclude list\n",
423- who_am_i(), local_exclude_list.debug_type);
424- }
a55d21aa
WD
425- free_exclude_list(&local_exclude_list);
426- local_exclude_list = last_list;
427 }
428 }
429
7cb7ae4e 430@@ -965,6 +949,7 @@ static void send_directory(int f, struct
a55d21aa
WD
431 struct dirent *di;
432 char fname[MAXPATHLEN];
433 unsigned int offset;
434+ void *save_excludes;
435 char *p;
436
437 d = opendir(dir);
bc95f62b 438@@ -988,18 +973,7 @@ static void send_directory(int f, struct
a55d21aa
WD
439 offset++;
440 }
441
442- if (cvs_exclude) {
443- if (strlcpy(p, ".cvsignore", MAXPATHLEN - offset)
444- < MAXPATHLEN - offset) {
445- add_exclude_file(&local_exclude_list, fname,
ee1af13c 446- XFLG_WORD_SPLIT | XFLG_WORDS_ONLY);
a55d21aa
WD
447- } else {
448- io_error |= IOERR_GENERAL;
449- rprintf(FINFO,
450- "cannot cvs-exclude in long-named directory %s\n",
451- full_fname(fname));
452- }
453- }
454+ save_excludes = push_local_excludes(fname, offset);
455
456 for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) {
457 char *dname = d_name(di);
bc95f62b
WD
458@@ -1019,6 +993,8 @@ static void send_directory(int f, struct
459 io_error |= IOERR_GENERAL;
460 rsyserr(FERROR, errno, "readdir(%s)", dir);
a55d21aa
WD
461 }
462+
463+ pop_local_excludes(save_excludes);
464
465 closedir(d);
466 }
0c7d1fd8 467--- rsync.h 16 May 2004 07:28:24 -0000 1.204
7b22909b 468+++ rsync.h 16 May 2004 22:04:33 -0000
0c7d1fd8
WD
469@@ -496,16 +496,21 @@ struct map_struct {
470 #define MATCHFLG_INCLUDE (1<<4) /* this is an include, not an exclude */
471 #define MATCHFLG_DIRECTORY (1<<5) /* this matches only directories */
472 #define MATCHFLG_CLEAR_LIST (1<<6) /* this item is the "!" token */
524989ae 473+#define MATCHFLG_MERGE_FILE (1<<7) /* specifies a file to merge */
a55d21aa
WD
474+#define MATCHFLG_CVSIGNORE (1<<8) /* parse this as a .cvsignore file */
475 struct exclude_struct {
476 struct exclude_struct *next;
477 char *pattern;
0c7d1fd8 478 unsigned int match_flags;
bc95f62b 479- int slash_cnt;
bc95f62b
WD
480+ union {
481+ int slash_cnt;
482+ int array_index;
483+ } u;
a55d21aa
WD
484 };
485
486 struct exclude_list_struct {
487- struct exclude_struct *head;
488- struct exclude_struct *tail;
489+ struct exclude_struct *head, *tail;
7cb7ae4e 490+ struct exclude_struct *parent;
a55d21aa
WD
491 char *debug_type;
492 };
493
ea238f1c 494--- rsync.yo 7 May 2004 00:18:37 -0000 1.169
7b22909b 495+++ rsync.yo 16 May 2004 22:04:33 -0000
ea238f1c 496@@ -1075,6 +1075,72 @@ itemize(
524989ae
WD
497 it would be excluded by the "*")
498 )
499
500+manpagesection(MERGING EXCLUDE FILES)
501+
502+You can merge whole files into an exclude file using a rule that starts
503+with a ". " (a dot followed by a space) and has a filename in place of the
504+pattern. There are two types of merge rules, single-instance and
505+per-directory:
506+
507+itemize(
508+ it() If the filename has no slashes in it, it is a per-directory merge;
bc95f62b
WD
509+ rsync scans every directory that it traverses for the named file, merging
510+ its contents (when it exists) file at the start of this per-directory
511+ sub-list (subdirectories inherit the contents of their parent directories
512+ by default, and each subdirectory's rules have precedence over the parent
513+ directory's rules).
524989ae
WD
514+
515+ it() If a filename has a slash in it, it is a single-instance merge; the
516+ named file's contents will be merged into the current exclude file,
517+ replacing the merge rule. Thus, you should use the name ./foo instead of
518+ foo if you don't want to scan for "foo" in all the subdirectories of the
519+ current directory.
520+)
521+
522+Note also that you can eliminate all the inherited rules for the current
523+per-directory ruleset by putting the list-clearing token (!) in the file.
bc95f62b
WD
524+This only clears the rules for the current per-directory sub-list (up
525+through the ! token) and only for the current directory and its
524989ae
WD
526+subdirectories.
527+
bc95f62b
WD
528+Here's an example exclude file (which you'd specify via the normal
529+--exclude-from option):
524989ae
WD
530+
531+verb(
532+ . /home/user/.global_excludes
533+ - *.gz
534+ . .excl
535+ + *.[ch]
536+ - *.o
537+)
538+
539+This will merge the contents of the /home/user/.global_excludes file at the
540+start of the list and also turns the ".excl" filename into a per-directory
541+exclude file whose local contents will be merged into the list in place of
542+the .excl line.
543+
544+Additionally, you can affect where the --cvs-exclude (-C) option's
545+inclusion of a per-directory .cvsignore file gets placed into your rules by
bc95f62b 546+adding an explicit merge rule for ".cvsignore". For instance, specifying
524989ae
WD
547+this:
548+
549+verb(
550+ rsync -avC --exclude='. .cvsignore' --exclude-from=foo a/ b
551+)
552+
553+will merge all the per-directory .cvsignore rules at the start of your list
554+rather than at the end. This allows their dir-specific rules to supersede
555+your rules instead of being subservient to them. (The global rules taken
556+from the $HOME/.cvsignore file and from $CVSIGNORE are not affected by
557+this.)
558+
559+Note also that the parsing of any merge-file named ".cvsignore" is always
560+done in a CVS-compatible manner (even if -C wasn't specified) -- i.e. the
561+rules are always exclude rules (even when specified by an include option),
562+they are split on whitespace, no special prefixes or list-clearing tokens
563+are honored, and (for per-directory files) subdirectories don't inherit the
564+parent directory's rules.
565+
566 manpagesection(BATCH MODE)
567
568 bf(Note:) Batch mode should be considered experimental in this version