From: Junio C Hamano <junkio@cox.net>
To: Linus Torvalds <torvalds@osdl.org>
Cc: git@vger.kernel.org
Subject: [PATCH] git-ls-files: --exclude mechanism updates.
Date: Sun, 24 Jul 2005 15:50:57 -0700 [thread overview]
Message-ID: <7v64uz3jji.fsf_-_@assigned-by-dhcp.cox.net> (raw)
In-Reply-To: <7vd5p73jlu.fsf@assigned-by-dhcp.cox.net> (Junio C. Hamano's message of "Sun, 24 Jul 2005 15:49:33 -0700")
Add --exclude-per-directory=<name> option that specifies a file
to contain exclude patterns local to that directory and its
subdirectories. Update the exclusion logic to be able to say
"include files that match this more specific pattern, even
though later exclude patterns may match them". Also enhances
that a pattern can contain '/' in which case fnmatch is called
with FNM_PATHNAME flag to match the entire path.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
ls-files.c | 123 ++++++++++++++++++++++++++++++------
t/t3001-ls-files-others-exclude.sh | 55 ++++++++++++++++
2 files changed, 157 insertions(+), 21 deletions(-)
create mode 100755 t/t3001-ls-files-others-exclude.sh
d1466fd8701ca79a91b41c6225c115a0a9866d6e
diff --git a/ls-files.c b/ls-files.c
--- a/ls-files.c
+++ b/ls-files.c
@@ -25,20 +25,31 @@ static const char *tag_removed = "";
static const char *tag_other = "";
static const char *tag_killed = "";
+static char *exclude_per_dir = NULL;
static int nr_excludes;
-static const char **excludes;
static int excludes_alloc;
+static struct exclude {
+ const char *pattern;
+ const char *base;
+ int baselen;
+} **excludes;
-static void add_exclude(const char *string)
+static void add_exclude(const char *string, const char *base, int baselen)
{
+ struct exclude *x = xmalloc(sizeof (*x));
+
+ x->pattern = string;
+ x->base = base;
+ x->baselen = baselen;
if (nr_excludes == excludes_alloc) {
excludes_alloc = alloc_nr(excludes_alloc);
excludes = realloc(excludes, excludes_alloc*sizeof(char *));
}
- excludes[nr_excludes++] = string;
+ excludes[nr_excludes++] = x;
}
-static void add_excludes_from_file(const char *fname)
+static int add_excludes_from_file_1(const char *fname,
+ const char *base, int baselen)
{
int fd, i;
long size;
@@ -53,7 +64,7 @@ static void add_excludes_from_file(const
lseek(fd, 0, SEEK_SET);
if (size == 0) {
close(fd);
- return;
+ return 0;
}
buf = xmalloc(size);
if (read(fd, buf, size) != size)
@@ -63,28 +74,89 @@ static void add_excludes_from_file(const
entry = buf;
for (i = 0; i < size; i++) {
if (buf[i] == '\n') {
- if (entry != buf + i) {
+ if (entry != buf + i && entry[0] != '#') {
buf[i] = 0;
- add_exclude(entry);
+ add_exclude(entry, base, baselen);
}
entry = buf + i + 1;
}
}
- return;
+ return 0;
-err: perror(fname);
- exit(1);
+ err:
+ if (0 <= fd)
+ close(fd);
+ return -1;
+}
+
+static void add_excludes_from_file(const char *fname)
+{
+ if (add_excludes_from_file_1(fname, "", 0) < 0)
+ die("cannot use %s as an exclude file", fname);
+}
+
+static int push_exclude_per_directory(const char *base, int baselen)
+{
+ char exclude_file[PATH_MAX];
+ int current_nr = nr_excludes;
+
+ if (exclude_per_dir) {
+ memcpy(exclude_file, base, baselen);
+ strcpy(exclude_file + baselen, exclude_per_dir);
+ add_excludes_from_file_1(exclude_file, base, baselen);
+ }
+ return current_nr;
+}
+
+static void pop_exclude_per_directory(int stk)
+{
+ while (stk < nr_excludes)
+ free(excludes[--nr_excludes]);
}
static int excluded(const char *pathname)
{
int i;
+
if (nr_excludes) {
- const char *basename = strrchr(pathname, '/');
- basename = (basename) ? basename+1 : pathname;
- for (i = 0; i < nr_excludes; i++)
- if (fnmatch(excludes[i], basename, 0) == 0)
- return 1;
+ int pathlen = strlen(pathname);
+
+ for (i = 0; i < nr_excludes; i++) {
+ struct exclude *x = excludes[i];
+ const char *exclude = x->pattern;
+ int to_exclude = 1;
+
+ if (*exclude == '!') {
+ to_exclude = 0;
+ exclude++;
+ }
+
+ if (!strchr(exclude, '/')) {
+ /* match basename */
+ const char *basename = strrchr(pathname, '/');
+ basename = (basename) ? basename+1 : pathname;
+ if (fnmatch(exclude, basename, 0) == 0)
+ return to_exclude;
+ }
+ else {
+ /* match with FNM_PATHNAME:
+ * exclude has base (baselen long) inplicitly
+ * in front of it.
+ */
+ int baselen = x->baselen;
+ if (*exclude == '/')
+ exclude++;
+
+ if (pathlen < baselen ||
+ (baselen && pathname[baselen-1] != '/') ||
+ strncmp(pathname, x->base, baselen))
+ continue;
+
+ if (fnmatch(exclude, pathname+baselen,
+ FNM_PATHNAME) == 0)
+ return to_exclude;
+ }
+ }
}
return 0;
}
@@ -121,7 +193,7 @@ static void add_name(const char *pathnam
* doesn't handle them at all yet. Maybe that will change some
* day.
*
- * Also, we currently ignore all names starting with a dot.
+ * Also, we ignore the name ".git" (even if it is not a directory).
* That likely will not change.
*/
static void read_directory(const char *path, const char *base, int baselen)
@@ -129,10 +201,13 @@ static void read_directory(const char *p
DIR *dir = opendir(path);
if (dir) {
+ int exclude_stk;
struct dirent *de;
char fullname[MAXPATHLEN + 1];
memcpy(fullname, base, baselen);
+ exclude_stk = push_exclude_per_directory(base, baselen);
+
while ((de = readdir(dir)) != NULL) {
int len;
@@ -141,10 +216,10 @@ static void read_directory(const char *p
!strcmp(de->d_name + 1, ".") ||
!strcmp(de->d_name + 1, "git")))
continue;
- if (excluded(de->d_name) != show_ignored)
- continue;
len = strlen(de->d_name);
memcpy(fullname + baselen, de->d_name, len+1);
+ if (excluded(fullname) != show_ignored)
+ continue;
switch (DTYPE(de)) {
struct stat st;
@@ -170,6 +245,8 @@ static void read_directory(const char *p
add_name(fullname, baselen + len);
}
closedir(dir);
+
+ pop_exclude_per_directory(exclude_stk);
}
}
@@ -287,7 +364,9 @@ static void show_files(void)
static const char *ls_files_usage =
"git-ls-files [-z] [-t] (--[cached|deleted|others|stage|unmerged|killed])* "
- "[ --ignored [--exclude=<pattern>] [--exclude-from=<file>) ]";
+ "[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
+ "[ --exclude-per-directory=<filename> ]";
+;
int main(int argc, char **argv)
{
@@ -323,13 +402,15 @@ int main(int argc, char **argv)
show_stage = 1;
show_unmerged = 1;
} else if (!strcmp(arg, "-x") && i+1 < argc) {
- add_exclude(argv[++i]);
+ add_exclude(argv[++i], "", 0);
} else if (!strncmp(arg, "--exclude=", 10)) {
- add_exclude(arg+10);
+ add_exclude(arg+10, "", 0);
} else if (!strcmp(arg, "-X") && i+1 < argc) {
add_excludes_from_file(argv[++i]);
} else if (!strncmp(arg, "--exclude-from=", 15)) {
add_excludes_from_file(arg+15);
+ } else if (!strncmp(arg, "--exclude-per-directory=", 24)) {
+ exclude_per_dir = arg + 24;
} else
usage(ls_files_usage);
}
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
new file mode 100755
--- /dev/null
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-ls-files --others --exclude
+
+This test runs git-ls-files --others and tests --exclude patterns.
+'
+
+. ./test-lib.sh
+
+rm -fr one three
+for dir in . one one/two three
+do
+ mkdir -p $dir &&
+ for i in 1 2 3 4 5
+ do
+ >$dir/a.$i
+ done
+done
+
+cat >expect <<EOF
+a.2
+a.4
+a.5
+one/a.3
+one/a.4
+one/a.5
+one/two/a.3
+one/two/a.5
+three/a.2
+three/a.3
+three/a.4
+three/a.5
+EOF
+
+echo '.gitignore
+output
+expect
+.gitignore
+' >.git/ignore
+
+echo '*.1
+/*.3' >.gitignore
+echo '*.2
+two/*.4' >one/.gitignore
+
+test_expect_success \
+ 'git-ls-files --others --exclude.' \
+ 'git-ls-files --others \
+ --exclude-per-directory=.gitignore \
+ --exclude-from=.git/ignore \
+ >output &&
+ diff -u expect output'
next prev parent reply other threads:[~2005-07-24 22:53 UTC|newest]
Thread overview: 55+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-07-21 20:23 [PATCH 1/1] Tell vim the textwidth is 75 Bryan larsen
2005-07-22 2:50 ` Junio C Hamano
2005-07-22 10:37 ` Catalin Marinas
2005-07-22 19:24 ` Sam Ravnborg
2005-07-22 20:39 ` Junio C Hamano
2005-07-22 20:59 ` Petr Baudis
2005-07-24 22:49 ` [RFC] extending git-ls-files --exclude Junio C Hamano
2005-07-24 22:50 ` Junio C Hamano [this message]
2005-07-24 22:51 ` [PATCH] Documentation: describe git-ls-files --exclude patterns Junio C Hamano
2005-07-25 9:19 ` [RFC] extending git-ls-files --exclude Catalin Marinas
2005-07-25 19:58 ` Junio C Hamano
2005-07-25 20:09 ` Linus Torvalds
2005-07-25 20:27 ` Junio C Hamano
2005-07-25 20:51 ` Catalin Marinas
2005-07-28 15:57 ` Petr Baudis
2005-07-25 20:59 ` Catalin Marinas
2005-07-28 15:52 ` Petr Baudis
2005-07-28 16:04 ` A Large Angry SCM
2005-07-28 19:25 ` Matthias Urlichs
2005-07-29 7:21 ` Petr Baudis
2005-07-29 7:37 ` Matthias Urlichs
2005-07-29 13:49 ` A Large Angry SCM
2005-07-29 5:04 ` Junio C Hamano
2005-07-29 7:36 ` Petr Baudis
2005-07-29 8:24 ` Junio C Hamano
2005-07-29 8:41 ` Petr Baudis
2005-08-01 16:14 ` Wayne Scott
2005-07-29 7:50 ` [PATCH] ls-files: rework exclude patterns Junio C Hamano
2005-07-29 7:51 ` [PATCH] Documentation and tests: ls-files exclude pattern Junio C Hamano
2005-07-22 21:43 ` [PATCH 1/1] Tell vim the textwidth is 75 Catalin Marinas
2005-07-22 23:07 ` Junio C Hamano
2005-07-23 8:41 ` Catalin Marinas
2005-07-23 9:30 ` Petr Baudis
2005-07-23 10:27 ` Catalin Marinas
2005-07-23 16:33 ` Bryan Larsen
2005-07-23 20:52 ` Catalin Marinas
2005-07-28 19:47 ` Petr Baudis
2005-07-29 2:24 ` Junio C Hamano
2005-07-29 2:59 ` Linus Torvalds
2005-07-29 9:55 ` Catalin Marinas
2005-07-29 11:10 ` Petr Baudis
2005-07-29 12:34 ` Catalin Marinas
2005-07-30 2:11 ` Junio C Hamano
2005-07-23 9:04 ` Petr Baudis
2005-07-24 1:13 ` Junio C Hamano
2005-07-22 21:00 ` Catalin Marinas
2005-07-22 20:41 ` Petr Baudis
2005-07-22 21:16 ` Junio C Hamano
2005-07-22 21:27 ` Petr Baudis
2005-07-22 23:24 ` Junio C Hamano
2005-07-22 23:50 ` Petr Baudis
2005-07-23 10:32 ` Catalin Marinas
2005-07-26 0:18 ` Updating diff-raw status letter to 'A' for added files Junio C Hamano
2005-07-26 0:20 ` [PATCH 1/2] Use symbolic constants for diff-raw status indicators Junio C Hamano
2005-07-26 0:21 ` [PATCH 2/2] diff-raw: Use 'A' instead of 'N' for added files Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=7v64uz3jji.fsf_-_@assigned-by-dhcp.cox.net \
--to=junkio@cox.net \
--cc=git@vger.kernel.org \
--cc=torvalds@osdl.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).