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.
This commit is contained in:
16
NEWS
16
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 <bug-tar@gnu.org>
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
19
doc/tar.1
19
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 <http://www.gnu.org/licenses/>.
|
||||
.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
|
||||
|
||||
96
doc/tar.texi
96
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
|
||||
|
||||
@@ -28,6 +28,7 @@ tar_SOURCES = \
|
||||
create.c\
|
||||
delete.c\
|
||||
exit.c\
|
||||
exclist.c\
|
||||
extract.c\
|
||||
xheader.c\
|
||||
incremen.c\
|
||||
|
||||
14
src/common.h
14
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
|
||||
|
||||
14
src/create.c
14
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);
|
||||
}
|
||||
|
||||
|
||||
361
src/exclist.c
Normal file
361
src/exclist.c
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <system.h>
|
||||
#include <quotearg.h>
|
||||
#include <fnmatch.h>
|
||||
#include <wordsplit.h>
|
||||
#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);
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
25
src/tar.c
25
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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user