From 93906c238d9309f95afeddfa9ac8d7ce92351e70 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Fri, 21 Feb 2014 17:57:26 +0200 Subject: [PATCH] Support exclusion patterns from various VCS ignore lists. * src/Makefile.am (tar_SOURCES): Add exclist.c * src/common.h (EXCL_DEFAULT, EXCL_RECURSIVE) (EXCL_NON_RECURSIVE): New flags. (excfile_add, info_attach_exclist) (info_cleanup_exclist,info_free_exclist) (exclude_vcs_ignores): New prototypes. * src/create.c (dump_dir0): Call info_attach_exclist. * src/exclist.c: New file. * src/incremen.c (scan_directory): Call info_attach_exclist. * src/names.c (excluded_name): Moved to exclist.c. Change signature. All uses updated. * src/tar.c: New options: --exclude-ignore, --exclude-ignore-recursive and --exclude-vcs-ignores. (tar_stat_destroy): Free exclist. * src/tar.h (tar_stat_info): New member exclude_list. * NEWS: Document new exclusion options. * doc/tar.texi: Likewise. * doc/tar.1: Likewise. --- NEWS | 16 ++- doc/tar.1 | 19 ++- doc/tar.texi | 96 ++++++++++++- src/Makefile.am | 1 + src/common.h | 14 +- src/create.c | 14 +- src/exclist.c | 361 ++++++++++++++++++++++++++++++++++++++++++++++++ src/incremen.c | 4 +- src/list.c | 3 +- src/names.c | 6 - src/tar.c | 25 ++++ src/tar.h | 3 + src/update.c | 2 +- 13 files changed, 544 insertions(+), 20 deletions(-) create mode 100644 src/exclist.c diff --git a/NEWS b/NEWS index ec9c8a7..fa24142 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -GNU tar NEWS - User visible changes. 2014-02-14 +GNU tar NEWS - User visible changes. 2014-02-21 Please send GNU tar bug reports to @@ -48,6 +48,20 @@ is uniform and reproducible. Using --sort=inode reduces the number of disk seeks made when creating the archive and thus can considerably speed up archivation. +* New exclusion options + + --exclude-ignore=FILE Before dumping a directory check if it + contains FILE, and if so read exclude + patterns for this directory from FILE. + --exclude-ignore-recursive=FILE + Same as above, but the exclusion patterns + read from FILE remain in effect for any + subdirectory, recursively. + --exclude-vcs-ignores Read exclude tags from VCS ignore files, + where such files exist. Supported VCS's + are: CVS, Git, Bazaar, Mercurial. + + * Manpages This release includes official tar(1) and rmt(8) manpages. diff --git a/doc/tar.1 b/doc/tar.1 index b33f55b..cd133d9 100644 --- a/doc/tar.1 +++ b/doc/tar.1 @@ -13,7 +13,7 @@ .\" .\" You should have received a copy of the GNU General Public License .\" along with this program. If not, see . -.TH TAR 1 "February 14, 2014" "TAR" "GNU TAR Manual" +.TH TAR 1 "February 22, 2014" "TAR" "GNU TAR Manual" .SH NAME tar \- an archiving utility .SH SYNOPSIS @@ -788,6 +788,15 @@ Exclude directories containing file \fBCACHEDIR.TAG\fR and the file itself. \fB\-\-exclude\-caches\-under\fR Exclude everything under directories containing \fBCACHEDIR.TAG\fR .TP +\fB\-\-exclude\-ignore=\fIFILE\fR +Before dumping a directory, see if it contains \fIFILE\fR. +If so, read exclusion patterns from this file. The patterns affect +only the directory itself. +.TP +\fB\-\-exclude\-ignore\-recursive=\fIFILE\fR +Same as \fB\-\-exclude\-ignore\fR, except that patterns from +\fIFILE\fR affect both the directory and all its subdirectories. +.TP \fB\-\-exclude\-tag\fR=\fIFILE\fR Exclude contents of directories containing \fIFILE\fR, except for \fIFILE\fR itself. @@ -801,6 +810,14 @@ Exclude everything under directories containing \fIFILE\fR. \fB\-\-exclude\-vcs\fR Exclude version control system directories. .TP +\fB\-\-exclude\-vcs\-ignores\fR +Exclude files that match patterns read from VCS-specific ignore +files. Supported files are: +.BR .cvsignore , +.BR .gitignore , +.BR .bzrignore ", and" +.BR .hgignore . +.TP \fB\-h\fR, \fB\-\-dereference\fR Follow symlinks; archive and dump the files they point to. .TP diff --git a/doc/tar.texi b/doc/tar.texi index 9bb5a83..e3df0c9 100644 --- a/doc/tar.texi +++ b/doc/tar.texi @@ -2608,6 +2608,19 @@ tag file, but still dump the directory node itself. Exclude from dump any directory containing a valid cache directory tag file. @xref{exclude}. +@opsummary{exclude-ignore} +@item --exclude-ignore=@var{file} +Before dumping a directory, @command{tar} checks if it contains +@var{file}. If so, exclusion patterns are read from this file. +The patterns affect only the directory itself. @xref{exclude}. + +@opsummary{exclude-ignore-recursive} +@item --exclude-ignore-recursive=@var{file} +Before dumping a directory, @command{tar} checks if it contains +@var{file}. If so, exclusion patterns are read from this file. +The patterns affect the directory and all itssubdirectories. +@xref{exclude}. + @opsummary{exclude-tag} @item --exclude-tag=@var{file} @@ -2633,7 +2646,16 @@ Exclude from dump any directory containing file named @var{file}. Exclude from dump directories and files, that are internal for some widely used version control systems. -@xref{exclude,,exclude-vcs}. +@xref{exclude-vcs}. + +@opsummary{exclude-vcs-ignores} +@item --exclude-vcs-ignores +Exclude files that match patterns read from VCS-specific ignore +files. Supported files are: @file{.cvsignore}, @file{.gitignore}, +@file{.bzrignore}, and @file{.hgignore}. The semantics of each file +is the same as for the corresponding VCS, e.g. patterns read from +@file{.gitignore} affect the directory and all its subdirectories. +@xref{exclude-vcs-ignores}. @opsummary{file} @item --file=@var{archive} @@ -7381,6 +7403,77 @@ which is difficult to catch using text editors. However, empty lines are OK. +@cindex VCS, excluding patterns from ignore files +@cindex VCS, ignore files +@cindex CVS, ignore files +@cindex Git, ignore files +@cindex Bazaar, ignore files +@cindex Mercurial, ignore files +When archiving directories that are under some version control system (VCS), +it is often convenient to read exclusion patterns from this VCS' +ignore files (e.g. @file{.cvsignore}, @file{.gitignore}, etc.) The +following options provide such possibilty: + +@table @option +@anchor{exclude-vcs-ignores} +@opindex exclude-vcs-ignores +@item --exclude-vcs-ignores +Before archiving a directory, see if it contains any of the following +files: @file{cvsignore}, @file{.gitignore}, @file{.bzrignore}, or +@file{.hgignore}. If so, read ignore patterns from these files. + +The patterns are treated much as the corresponding VCS would treat +them, i.e.: + +@table @file +@findex .cvsignore +@item .cvsignore +Contains shell-style globbing patterns that apply only to the +directory where this file resides. No comments are allowed in the +file. Empty lines are ignored. + +@findex .gitignore +@item .gitignore +Contains shell-style globbing patterns. Applies to the directory +where @file{.gitfile} is located and all its subdirectories. + +Any line beginning with a @samp{#} is a comment. Backslash escapes +the comment character. + +@findex .bzrignore +@item .bzrignore +Contains shell globbing-patterns and regular expressions (if prefixed +with @samp{RE:}@footnote{According to the Bazaar docs, +globbing-patterns are Korn-shell style and regular expressions are +perl-style. As of @GNUTAR{} version @value{VERSION}, these are +treated as shell-style globs and posix extended regexps. This will be +fixed in future releases.}. Patterns affect the directory and all its +subdirectories. + +Any line beginning with a @samp{#} is a comment. + +@findex .hgignore +@item .hgignore +Contains posix regular expressions@footnote{Support for perl-style +regexps will appear in future releases.}. The line @samp{syntax: +glob} switches to shell globbing patterns. The line @samp{syntax: +regexp} switches back. Comments begin with a @samp{#}. Patterns +affect the directory and all its subdirectories. +@end table + +@opindex exclude-ignore +@item --exclude-ignore=@var{file} +Before dumping a directory, @command{tar} checks if it contains +@var{file}. If so, exclusion patterns are read from this file. +The patterns affect only the directory itself. + +@opindex exclude-ignore-recursive +@item --exclude-ignore-recursive=@var{file} +Same as @option{--exclude-ignore}, except that the patterns read +affect both the directory where @var{file} resides and all its +subdirectories. +@end table + @table @option @cindex version control system, excluding files @cindex VCS, excluding files @@ -7393,6 +7486,7 @@ However, empty lines are OK. @cindex Arch, excluding files @cindex Mercurial, excluding files @cindex Darcs, excluding files +@anchor{exclude-vcs} @opindex exclude-vcs @item --exclude-vcs Exclude files and directories used by following version control diff --git a/src/Makefile.am b/src/Makefile.am index a9d25ef..82b2d46 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -28,6 +28,7 @@ tar_SOURCES = \ create.c\ delete.c\ exit.c\ + exclist.c\ extract.c\ xheader.c\ incremen.c\ diff --git a/src/common.h b/src/common.h index 4d2c399..edf787c 100644 --- a/src/common.h +++ b/src/common.h @@ -743,8 +743,6 @@ char *new_name (const char *dir_name, const char *name); size_t stripped_prefix_len (char const *file_name, size_t num); bool all_names_found (struct tar_stat_info *st); -bool excluded_name (char const *name); - void add_avoided_name (char const *name); bool is_avoided_name (char const *name); @@ -921,4 +919,16 @@ void finish_deferred_unlinks (void); /* Module exit.c */ extern void (*fatal_exit_hook) (void); +/* Module exclist.c */ +#define EXCL_DEFAULT 0x00 +#define EXCL_RECURSIVE 0x01 +#define EXCL_NON_RECURSIVE 0x02 + +void excfile_add (const char *name, int flags); +void info_attach_exclist (struct tar_stat_info *dir); +void info_cleanup_exclist (struct tar_stat_info *dir); +void info_free_exclist (struct tar_stat_info *dir); +bool excluded_name (char const *name, struct tar_stat_info *st); +void exclude_vcs_ignores (void); + _GL_INLINE_HEADER_END diff --git a/src/create.c b/src/create.c index 3455bdd..e2f4ede 100644 --- a/src/create.c +++ b/src/create.c @@ -1113,6 +1113,8 @@ dump_dir0 (struct tar_stat_info *st, char const *directory) if (!blk) return; + info_attach_exclist (st); + if (incremental_option && archive_format != POSIX_FORMAT) blk->header.typeflag = GNUTYPE_DUMPDIR; else /* if (standard_option) */ @@ -1196,7 +1198,7 @@ dump_dir0 (struct tar_stat_info *st, char const *directory) char const *entry; size_t entry_len; size_t name_len; - + name_buf = xstrdup (st->orig_file_name); name_size = name_len = strlen (name_buf); @@ -1210,7 +1212,7 @@ dump_dir0 (struct tar_stat_info *st, char const *directory) name_buf = xrealloc (name_buf, name_size + 1); } strcpy (name_buf + name_len, entry); - if (!excluded_name (name_buf)) + if (!excluded_name (name_buf, st)) dump_file (st, entry, name_buf); } @@ -1339,12 +1341,12 @@ create_archive (void) collect_and_sort_names (); while ((p = name_from_list ()) != NULL) - if (!excluded_name (p->name)) + if (!excluded_name (p->name, NULL)) dump_file (0, p->name, p->name); blank_name_list (); while ((p = name_from_list ()) != NULL) - if (!excluded_name (p->name)) + if (!excluded_name (p->name, NULL)) { struct tar_stat_info st; size_t plen = strlen (p->name); @@ -1358,7 +1360,7 @@ create_archive (void) if (! ISSLASH (buffer[plen - 1])) buffer[plen++] = DIRECTORY_SEPARATOR; tar_stat_init (&st); - q = directory_contents (gnu_list_name->directory); + q = directory_contents (p->directory); if (q) while (*q) { @@ -1401,7 +1403,7 @@ create_archive (void) { const char *name; while ((name = name_next (1)) != NULL) - if (!excluded_name (name)) + if (!excluded_name (name, NULL)) dump_file (0, name, name); } diff --git a/src/exclist.c b/src/exclist.c new file mode 100644 index 0000000..7cccc74 --- /dev/null +++ b/src/exclist.c @@ -0,0 +1,361 @@ +/* Per-directory exclusion files for tar. + + Copyright 2014 Free Software Foundation, Inc. + + This file is part of GNU tar. + + GNU tar is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + GNU tar is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include +#include +#include +#include +#include "common.h" + +typedef void (*add_fn) (struct exclude *, char const *, int, void *); + +struct vcs_ignore_file +{ + char const *filename; + int flags; + add_fn addfn; + void *(*initfn) (void *); + void *data; +}; + +static struct vcs_ignore_file *get_vcs_ignore_file (const char *name); + +struct excfile +{ + struct excfile *next; + int flags; + char name[1]; +}; + +struct excfile *excfile_head, *excfile_tail; + +void +excfile_add (const char *name, int flags) +{ + struct excfile *p = xmalloc (sizeof (*p) + strlen (name)); + p->next = NULL; + p->flags = flags; + strcpy (p->name, name); + if (excfile_tail) + excfile_tail->next = p; + else + excfile_head = p; + excfile_tail = p; +} + +struct exclist +{ + struct exclist *next, *prev; + int flags; + struct exclude *excluded; +}; + +void +info_attach_exclist (struct tar_stat_info *dir) +{ + struct excfile *file; + struct exclist *head = NULL, *tail = NULL, *ent; + struct vcs_ignore_file *vcsfile; + + if (dir->exclude_list) + return; + for (file = excfile_head; file; file = file->next) + { + if (faccessat (dir ? dir->fd : chdir_fd, file->name, F_OK, 0) == 0) + { + FILE *fp; + struct exclude *ex = NULL; + int fd = subfile_open (dir, file->name, O_RDONLY); + if (fd == -1) + { + open_error (file->name); + continue; + } + fp = fdopen (fd, "r"); + if (!fp) + { + ERROR ((0, errno, _("%s: fdopen failed"), file->name)); + close (fd); + continue; + } + + if (!ex) + ex = new_exclude (); + + vcsfile = get_vcs_ignore_file (file->name); + + if (vcsfile->initfn) + vcsfile->data = vcsfile->initfn (vcsfile->data); + + if (add_exclude_fp (vcsfile->addfn, ex, fp, + EXCLUDE_WILDCARDS|EXCLUDE_ANCHORED, '\n', + vcsfile->data)) + { + int e = errno; + FATAL_ERROR ((0, e, "%s", quotearg_colon (file->name))); + } + fclose (fp); + + ent = xmalloc (sizeof (*ent)); + ent->excluded = ex; + ent->flags = file->flags == EXCL_DEFAULT + ? file->flags : vcsfile->flags; + ent->prev = tail; + ent->next = NULL; + + if (tail) + tail->next = ent; + else + head = ent; + tail = ent; + } + } + dir->exclude_list = head; +} + +void +info_cleanup_exclist (struct tar_stat_info *dir) +{ + struct exclist *ep = dir->exclude_list; + + while (ep) + { + struct exclist *next = ep->next; + + if (ep->flags & EXCL_NON_RECURSIVE) + { + + /* Remove the entry */ + if (ep->prev) + ep->prev->next = ep->next; + else + dir->exclude_list = ep->next; + + if (ep->next) + ep->next->prev = ep->prev; + + free_exclude (ep->excluded); + free (ep); + } + ep = next; + } +} + +void +info_free_exclist (struct tar_stat_info *dir) +{ + struct exclist *ep = dir->exclude_list; + + while (ep) + { + struct exclist *next = ep->next; + free_exclude (ep->excluded); + free (ep); + ep = next; + } + + dir->exclude_list = NULL; +} + + +/* Return nonzero if file NAME is excluded. */ +bool +excluded_name (char const *name, struct tar_stat_info *st) +{ + struct exclist *ep; + const char *rname = NULL; + char *bname = NULL; + bool result; + int nr = 0; + + name += FILE_SYSTEM_PREFIX_LEN (name); + + /* Try global exclusion list first */ + if (excluded_file_name (excluded, name)) + return true; + + if (!st) + return false; + + for (result = false; st && !result; st = st->parent, nr = EXCL_NON_RECURSIVE) + { + for (ep = st->exclude_list; ep; ep = ep->next) + { + if (ep->flags & nr) + continue; + if ((result = excluded_file_name (ep->excluded, name))) + break; + + if (!rname) + { + rname = name; + /* Skip leading ./ */ + while (*rname == '.' && ISSLASH (rname[1])) + rname += 2; + } + if ((result = excluded_file_name (ep->excluded, rname))) + break; + + if (!bname) + bname = base_name (name); + if ((result = excluded_file_name (ep->excluded, bname))) + break; + } + } + + free (bname); + + return result; +} + +static void +cvs_addfn (struct exclude *ex, char const *pattern, int options, void *data) +{ + struct wordsplit ws; + size_t i; + + if (wordsplit (pattern, &ws, + WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_SQUEEZE_DELIMS)) + return; + for (i = 0; i < ws.ws_wordc; i++) + add_exclude (ex, ws.ws_wordv[i], options); + wordsplit_free (&ws); +} + +static void +git_addfn (struct exclude *ex, char const *pattern, int options, void *data) +{ + while (isspace (*pattern)) + ++pattern; + if (*pattern == 0 || *pattern == '#') + return; + if (*pattern == '\\' && pattern[1] == '#') + ++pattern; + add_exclude (ex, pattern, options); +} + +static void +bzr_addfn (struct exclude *ex, char const *pattern, int options, void *data) +{ + while (isspace (*pattern)) + ++pattern; + if (*pattern == 0 || *pattern == '#') + return; + if (*pattern == '!') + { + if (*++pattern == '!') + ++pattern; + else + options |= EXCLUDE_INCLUDE; + } + /* FIXME: According to the docs, globbing patterns are rsync-style, + and regexps are perl-style. */ + if (strncmp (pattern, "RE:", 3) == 0) + { + pattern += 3; + options &= ~EXCLUDE_WILDCARDS; + options |= EXCLUDE_REGEX; + } + add_exclude (ex, pattern, options); +} + +static void * +hg_initfn (void *data) +{ + int *hgopt; + static int hg_options; + + if (!data) + hgopt = &hg_options; + + *hgopt = EXCLUDE_REGEX; + return hgopt; +} + +static void +hg_addfn (struct exclude *ex, char const *pattern, int options, void *data) +{ + int *hgopt = data; + size_t len; + + while (isspace (*pattern)) + ++pattern; + if (*pattern == 0 || *pattern == '#') + return; + if (strncmp (pattern, "syntax:", 7) == 0) + { + for (pattern += 7; isspace (*pattern); ++pattern) + ; + if (strcmp (pattern, "regexp") == 0) + /* FIXME: Regexps must be perl-style */ + *hgopt = EXCLUDE_REGEX; + else if (strcmp (pattern, "glob") == 0) + *hgopt = EXCLUDE_WILDCARDS; + /* Ignore unknown syntax */ + return; + } + + len = strlen(pattern); + if (pattern[len-1] == '/') + { + char *p; + + --len; + p = xmalloc (len+1); + memcpy (p, pattern, len); + p[len] = 0; + pattern = p; + exclude_add_pattern_buffer (ex, p); + options |= FNM_LEADING_DIR|EXCLUDE_ALLOC; + } + + add_exclude (ex, pattern, + ((*hgopt == EXCLUDE_REGEX) + ? (options & ~EXCLUDE_WILDCARDS) + : (options & ~EXCLUDE_REGEX)) | *hgopt); +} + +struct vcs_ignore_file vcs_ignore_files[] = { + { ".cvsignore", EXCL_NON_RECURSIVE, cvs_addfn, NULL, NULL }, + { ".gitignore", 0, git_addfn, NULL, NULL }, + { ".bzrignore", 0, bzr_addfn, NULL, NULL }, + { ".hgignore", 0, hg_addfn, hg_initfn , NULL }, + { NULL, 0, git_addfn, NULL, NULL } +}; + +static struct vcs_ignore_file * +get_vcs_ignore_file (const char *name) +{ + struct vcs_ignore_file *p; + + for (p = vcs_ignore_files; p->filename; p++) + if (strcmp (p->filename, name) == 0) + break; + + return p; +} + +void +exclude_vcs_ignores (void) +{ + struct vcs_ignore_file *p; + + for (p = vcs_ignore_files; p->filename; p++) + excfile_add (p->filename, EXCL_DEFAULT); +} diff --git a/src/incremen.c b/src/incremen.c index 884d2fa..b1b70ba 100644 --- a/src/incremen.c +++ b/src/incremen.c @@ -734,6 +734,8 @@ scan_directory (struct tar_stat_info *st) if (! dirp) savedir_error (dir); + info_attach_exclist (st); + tmp = xstrdup (dir); zap_slashes (tmp); @@ -762,7 +764,7 @@ scan_directory (struct tar_stat_info *st) if (*entry == 'I') /* Ignored entry */ *entry = 'N'; - else if (excluded_name (full_name)) + else if (excluded_name (full_name, st)) *entry = 'N'; else { diff --git a/src/list.c b/src/list.c index d46be65..b4277e0 100644 --- a/src/list.c +++ b/src/list.c @@ -203,7 +203,8 @@ read_and (void (*do_something) (void)) mtime.tv_nsec = 0, current_stat_info.mtime = mtime, OLDER_TAR_STAT_TIME (current_stat_info, m))) - || excluded_name (current_stat_info.file_name)) + || excluded_name (current_stat_info.file_name, + current_stat_info.parent)) { switch (current_header->header.typeflag) { diff --git a/src/names.c b/src/names.c index 60a5f70..9fc0ad5 100644 --- a/src/names.c +++ b/src/names.c @@ -1373,12 +1373,6 @@ new_name (const char *file_name, const char *name) return buffer; } -/* Return nonzero if file NAME is excluded. */ -bool -excluded_name (char const *name) -{ - return excluded_file_name (excluded, name + FILE_SYSTEM_PREFIX_LEN (name)); -} /* Return the size of the prefix of FILE_NAME that is removed after diff --git a/src/tar.c b/src/tar.c index 08f334f..ac32f97 100644 --- a/src/tar.c +++ b/src/tar.c @@ -284,10 +284,13 @@ enum EXCLUDE_CACHES_UNDER_OPTION, EXCLUDE_CACHES_ALL_OPTION, EXCLUDE_OPTION, + EXCLUDE_IGNORE_OPTION, + EXCLUDE_IGNORE_RECURSIVE_OPTION, EXCLUDE_TAG_OPTION, EXCLUDE_TAG_UNDER_OPTION, EXCLUDE_TAG_ALL_OPTION, EXCLUDE_VCS_OPTION, + EXCLUDE_VCS_IGNORES_OPTION, FORCE_LOCAL_OPTION, FULL_TIME_OPTION, GROUP_OPTION, @@ -732,12 +735,20 @@ static struct argp_option options[] = { {"exclude-tag", EXCLUDE_TAG_OPTION, N_("FILE"), 0, N_("exclude contents of directories containing FILE, except" " for FILE itself"), GRID+1 }, + {"exclude-ignore", EXCLUDE_IGNORE_OPTION, N_("FILE"), 0, + N_("read exclude patterns for each directory from FILE, if it exists"), + GRID+1 }, + {"exclude-ignore-recursive", EXCLUDE_IGNORE_RECURSIVE_OPTION, N_("FILE"), 0, + N_("read exclude patterns for each directory and its subdirectories " + "from FILE, if it exists"), GRID+1 }, {"exclude-tag-under", EXCLUDE_TAG_UNDER_OPTION, N_("FILE"), 0, N_("exclude everything under directories containing FILE"), GRID+1 }, {"exclude-tag-all", EXCLUDE_TAG_ALL_OPTION, N_("FILE"), 0, N_("exclude directories containing FILE"), GRID+1 }, {"exclude-vcs", EXCLUDE_VCS_OPTION, NULL, 0, N_("exclude version control system directories"), GRID+1 }, + {"exclude-vcs-ignores", EXCLUDE_VCS_IGNORES_OPTION, NULL, 0, + N_("read exclude patterns from the VCS ignore files"), GRID+1 }, {"exclude-backups", EXCLUDE_BACKUPS_OPTION, NULL, 0, N_("exclude backup and lock files"), GRID+1 }, {"no-recursion", NO_RECURSION_OPTION, 0, 0, @@ -1776,6 +1787,14 @@ parse_opt (int key, char *arg, struct argp_state *state) cachedir_file_p); break; + case EXCLUDE_IGNORE_OPTION: + excfile_add (arg, EXCL_NON_RECURSIVE); + break; + + case EXCLUDE_IGNORE_RECURSIVE_OPTION: + excfile_add (arg, EXCL_RECURSIVE); + break; + case EXCLUDE_TAG_OPTION: add_exclusion_tag (arg, exclusion_tag_contents, NULL); break; @@ -1792,6 +1811,10 @@ parse_opt (int key, char *arg, struct argp_state *state) add_exclude_array (vcs_file_table, 0); break; + case EXCLUDE_VCS_IGNORES_OPTION: + exclude_vcs_ignores (); + break; + case FORCE_LOCAL_OPTION: force_local_option = true; break; @@ -2304,6 +2327,7 @@ decode_options (int argc, char **argv) blocking_factor = DEFAULT_BLOCKING; record_size = DEFAULT_BLOCKING * BLOCKSIZE; excluded = new_exclude (); + newer_mtime_option.tv_sec = TYPE_MINIMUM (time_t); newer_mtime_option.tv_nsec = -1; recursion_option = FNM_LEADING_DIR; @@ -2849,6 +2873,7 @@ tar_stat_destroy (struct tar_stat_info *st) free (st->sparse_map); free (st->dumpdir); xheader_destroy (&st->xhdr); + info_free_exclist (st); memset (st, 0, sizeof (*st)); } diff --git a/src/tar.h b/src/tar.h index cb7d70c..3d69399 100644 --- a/src/tar.h +++ b/src/tar.h @@ -358,6 +358,9 @@ struct tar_stat_info It is negative if it could not be reopened after it was closed. Negate it to find out what errno was when the reopen failed. */ int fd; + + /* Exclusion list */ + struct exclist *exclude_list; }; union block diff --git a/src/update.c b/src/update.c index e9fad3f..c8fca0c 100644 --- a/src/update.c +++ b/src/update.c @@ -216,7 +216,7 @@ update_archive (void) while ((p = name_from_list ()) != NULL) { char *file_name = p->name; - if (excluded_name (file_name)) + if (excluded_name (file_name, NULL)) continue; if (interactive_option && !confirm ("add", file_name)) continue; -- 2.45.2