Make sure the empty tree exists when needed in merge-recursive.
[gitweb.git] / revision.c
index db01682750a4dfa820ea3f0e4a99e306aa6b075a..993bb668a205a9fb3e2f5ddd2f378f10ac7905b4 100644 (file)
@@ -6,6 +6,8 @@
 #include "diff.h"
 #include "refs.h"
 #include "revision.h"
+#include <regex.h>
+#include "grep.h"
 
 static char *path_name(struct name_path *path, const char *name)
 {
@@ -416,8 +418,6 @@ static void limit_list(struct rev_info *revs)
 
                if (revs->max_age != -1 && (commit->date < revs->max_age))
                        obj->flags |= UNINTERESTING;
-               if (revs->unpacked && has_sha1_pack(obj->sha1))
-                       obj->flags |= UNINTERESTING;
                add_parents_to_list(revs, commit, &list);
                if (obj->flags & UNINTERESTING) {
                        mark_parents_uninteresting(commit);
@@ -465,7 +465,7 @@ static void limit_list(struct rev_info *revs)
 static int all_flags;
 static struct rev_info *all_revs;
 
-static int handle_one_ref(const char *path, const unsigned char *sha1)
+static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
        struct object *object = get_reference(all_revs, path, sha1, all_flags);
        add_pending_object(all_revs, object, "");
@@ -476,7 +476,7 @@ static void handle_all(struct rev_info *revs, unsigned flags)
 {
        all_revs = revs;
        all_flags = flags;
-       for_each_ref(handle_one_ref);
+       for_each_ref(handle_one_ref, NULL);
 }
 
 static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
@@ -657,6 +657,13 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
                        return 0;
                *dotdot = '^';
        }
+       dotdot = strstr(arg, "^!");
+       if (dotdot && !dotdot[2]) {
+               *dotdot = 0;
+               if (!add_parents_only(revs, arg, flags ^ UNINTERESTING))
+                       *dotdot = '^';
+       }
+
        local_flags = 0;
        if (*arg == '^') {
                local_flags = UNINTERESTING;
@@ -671,6 +678,52 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
        return 0;
 }
 
+static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token what)
+{
+       if (!revs->grep_filter) {
+               struct grep_opt *opt = xcalloc(1, sizeof(*opt));
+               opt->status_only = 1;
+               opt->pattern_tail = &(opt->pattern_list);
+               opt->regflags = REG_NEWLINE;
+               revs->grep_filter = opt;
+       }
+       append_grep_pattern(revs->grep_filter, ptn,
+                           "command line", 0, what);
+}
+
+static void add_header_grep(struct rev_info *revs, const char *field, const char *pattern)
+{
+       char *pat;
+       const char *prefix;
+       int patlen, fldlen;
+
+       fldlen = strlen(field);
+       patlen = strlen(pattern);
+       pat = xmalloc(patlen + fldlen + 10);
+       prefix = ".*";
+       if (*pattern == '^') {
+               prefix = "";
+               pattern++;
+       }
+       sprintf(pat, "^%s %s%s", field, prefix, pattern);
+       add_grep(revs, pat, GREP_PATTERN_HEAD);
+}
+
+static void add_message_grep(struct rev_info *revs, const char *pattern)
+{
+       add_grep(revs, pattern, GREP_PATTERN_BODY);
+}
+
+static void add_ignore_packed(struct rev_info *revs, const char *name)
+{
+       int num = ++revs->num_ignore_packed;
+
+       revs->ignore_packed = xrealloc(revs->ignore_packed,
+                                      sizeof(const char **) * (num + 1));
+       revs->ignore_packed[num-1] = name;
+       revs->ignore_packed[num] = NULL;
+}
+
 /*
  * Parse revision information, filling in the "rev_info" structure,
  * and removing the used arguments from the argument list.
@@ -683,6 +736,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
        int i, flags, seen_dashdash, show_merge;
        const char **unrecognized = argv + 1;
        int left = 1;
+       int all_match = 0;
 
        /* First, search for "--" */
        seen_dashdash = 0;
@@ -811,6 +865,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                        }
                        if (!strcmp(arg, "--unpacked")) {
                                revs->unpacked = 1;
+                               free(revs->ignore_packed);
+                               revs->ignore_packed = NULL;
+                               revs->num_ignore_packed = 0;
+                               continue;
+                       }
+                       if (!strncmp(arg, "--unpacked=", 11)) {
+                               revs->unpacked = 1;
+                               add_ignore_packed(revs, arg+11);
                                continue;
                        }
                        if (!strcmp(arg, "-r")) {
@@ -894,6 +956,27 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                revs->relative_date = 1;
                                continue;
                        }
+
+                       /*
+                        * Grepping the commit log
+                        */
+                       if (!strncmp(arg, "--author=", 9)) {
+                               add_header_grep(revs, "author", arg+9);
+                               continue;
+                       }
+                       if (!strncmp(arg, "--committer=", 12)) {
+                               add_header_grep(revs, "committer", arg+12);
+                               continue;
+                       }
+                       if (!strncmp(arg, "--grep=", 7)) {
+                               add_message_grep(revs, arg+7);
+                               continue;
+                       }
+                       if (!strcmp(arg, "--all-match")) {
+                               all_match = 1;
+                               continue;
+                       }
+
                        opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
                        if (opts > 0) {
                                revs->diff = 1;
@@ -936,7 +1019,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                add_pending_object(revs, object, def);
        }
 
-       if (revs->topo_order || revs->unpacked)
+       if (revs->topo_order)
                revs->limited = 1;
 
        if (revs->prune_data) {
@@ -954,6 +1037,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
        if (diff_setup_done(&revs->diffopt) < 0)
                die("diff_setup_done failed");
 
+       if (revs->grep_filter) {
+               revs->grep_filter->all_match = all_match;
+               compile_grep_patterns(revs->grep_filter);
+       }
+
        return left;
 }
 
@@ -1026,6 +1114,15 @@ static void mark_boundary_to_show(struct commit *commit)
        }
 }
 
+static int commit_match(struct commit *commit, struct rev_info *opt)
+{
+       if (!opt->grep_filter)
+               return 1;
+       return grep_buffer(opt->grep_filter,
+                          NULL, /* we say nothing, not even filename */
+                          commit->buffer, strlen(commit->buffer));
+}
+
 struct commit *get_revision(struct rev_info *revs)
 {
        struct commit_list *list = revs->commits;
@@ -1056,16 +1153,18 @@ struct commit *get_revision(struct rev_info *revs)
                 * that we'd otherwise have done in limit_list().
                 */
                if (!revs->limited) {
-                       if ((revs->unpacked &&
-                            has_sha1_pack(commit->object.sha1)) ||
-                           (revs->max_age != -1 &&
-                            (commit->date < revs->max_age)))
+                       if (revs->max_age != -1 &&
+                           (commit->date < revs->max_age))
                                continue;
                        add_parents_to_list(revs, commit, &revs->commits);
                }
                if (commit->object.flags & SHOWN)
                        continue;
 
+               if (revs->unpacked && has_sha1_pack(commit->object.sha1,
+                                                   revs->ignore_packed))
+                   continue;
+
                /* We want to show boundary commits only when their
                 * children are shown.  When path-limiter is in effect,
                 * rewrite_parents() drops some commits from getting shown,
@@ -1085,6 +1184,8 @@ struct commit *get_revision(struct rev_info *revs)
                if (revs->no_merges &&
                    commit->parents && commit->parents->next)
                        continue;
+               if (!commit_match(commit, revs))
+                       continue;
                if (revs->prune_fn && revs->dense) {
                        /* Commit without changes? */
                        if (!(commit->object.flags & TREECHANGE)) {