git-svn: bugfix and optimize the 'log' command
[gitweb.git] / blame.c
diff --git a/blame.c b/blame.c
index 1ee0d323ca7d5397c95f0d55f2279d2ca5dc0c8f..88bfec262f741a80ee4aac6ef19d7cf359559464 100644 (file)
--- a/blame.c
+++ b/blame.c
@@ -149,7 +149,7 @@ static void free_patch(struct patch *p)
        free(p);
 }
 
-static int get_blob_sha1_internal(unsigned char *sha1, const char *base,
+static int get_blob_sha1_internal(const unsigned char *sha1, const char *base,
                                  int baselen, const char *pathname,
                                  unsigned mode, int stage);
 
@@ -178,7 +178,7 @@ static int get_blob_sha1(struct tree *t, const char *pathname,
        return 0;
 }
 
-static int get_blob_sha1_internal(unsigned char *sha1, const char *base,
+static int get_blob_sha1_internal(const unsigned char *sha1, const char *base,
                                  int baselen, const char *pathname,
                                  unsigned mode, int stage)
 {
@@ -508,25 +508,33 @@ static void process_commits(struct rev_info *rev, const char *path,
 static int compare_tree_path(struct rev_info* revs,
                             struct commit* c1, struct commit* c2)
 {
+       int ret;
        const char* paths[2];
        struct util_info* util = c2->object.util;
        paths[0] = util->pathname;
        paths[1] = NULL;
 
-       diff_tree_setup_paths(get_pathspec(revs->prefix, paths));
-       return rev_compare_tree(c1->tree, c2->tree);
+       diff_tree_setup_paths(get_pathspec(revs->prefix, paths),
+                             &revs->pruning);
+       ret = rev_compare_tree(revs, c1->tree, c2->tree);
+       diff_tree_release_paths(&revs->pruning);
+       return ret;
 }
 
 
 static int same_tree_as_empty_path(struct rev_info *revs, struct tree* t1,
                                   const char* path)
 {
+       int ret;
        const char* paths[2];
        paths[0] = path;
        paths[1] = NULL;
 
-       diff_tree_setup_paths(get_pathspec(revs->prefix, paths));
-       return rev_same_tree_as_empty(t1);
+       diff_tree_setup_paths(get_pathspec(revs->prefix, paths),
+                             &revs->pruning);
+       ret = rev_same_tree_as_empty(revs, t1);
+       diff_tree_release_paths(&revs->pruning);
+       return ret;
 }
 
 static const char* find_rename(struct commit* commit, struct commit* parent)
@@ -546,7 +554,7 @@ static const char* find_rename(struct commit* commit, struct commit* parent)
        diff_opts.recursive = 1;
        diff_opts.detect_rename = DIFF_DETECT_RENAME;
        paths[0] = NULL;
-       diff_tree_setup_paths(paths);
+       diff_tree_setup_paths(paths, &diff_opts);
        if (diff_setup_done(&diff_opts) < 0)
                die("diff_setup_done failed");
 
@@ -703,11 +711,30 @@ static void* topo_getter(struct commit* c)
        return util->topo_data;
 }
 
+static int read_ancestry(const char *graft_file,
+                        unsigned char **start_sha1)
+{
+       FILE *fp = fopen(graft_file, "r");
+       char buf[1024];
+       if (!fp)
+               return -1;
+       while (fgets(buf, sizeof(buf), fp)) {
+               /* The format is just "Commit Parent1 Parent2 ...\n" */
+               int len = strlen(buf);
+               struct commit_graft *graft = read_graft_line(buf, len);
+               register_commit_graft(graft, 0);
+               if (!*start_sha1)
+                       *start_sha1 = graft->sha1;
+       }
+       fclose(fp);
+       return 0;
+}
+
 int main(int argc, const char **argv)
 {
        int i;
        struct commit *initial = NULL;
-       unsigned char sha1[20];
+       unsigned char sha1[20], *sha1_p = NULL;
 
        const char *filename = NULL, *commit = NULL;
        char filename_buf[256];
@@ -741,6 +768,14 @@ int main(int argc, const char **argv)
                                  !strcmp(argv[i], "--compability")) {
                                compability = 1;
                                continue;
+                       } else if(!strcmp(argv[i], "-S")) {
+                               if (i + 1 < argc &&
+                                   !read_ancestry(argv[i + 1], &sha1_p)) {
+                                       compability = 1;
+                                       i++;
+                                       continue;
+                               }
+                               usage(blame_usage);
                        } else if(!strcmp(argv[i], "--")) {
                                options = 0;
                                continue;
@@ -762,7 +797,9 @@ int main(int argc, const char **argv)
 
        if(!filename)
                usage(blame_usage);
-       if(!commit)
+       if (commit && sha1_p)
+               usage(blame_usage);
+       else if(!commit)
                commit = "HEAD";
 
        if(prefix)
@@ -771,9 +808,12 @@ int main(int argc, const char **argv)
                strcpy(filename_buf, filename);
        filename = filename_buf;
 
-       if (get_sha1(commit, sha1))
-               die("get_sha1 failed, commit '%s' not found", commit);
-       start_commit = lookup_commit_reference(sha1);
+       if (!sha1_p) {
+               if (get_sha1(commit, sha1))
+                       die("get_sha1 failed, commit '%s' not found", commit);
+               sha1_p = sha1;
+       }
+       start_commit = lookup_commit_reference(sha1_p);
        get_util(start_commit)->pathname = filename;
        if (fill_util_info(start_commit)) {
                printf("%s not found in %s\n", filename, commit);
@@ -787,13 +827,14 @@ int main(int argc, const char **argv)
        rev.prune_fn = simplify_commit;
        rev.topo_setter = topo_setter;
        rev.topo_getter = topo_getter;
+       rev.parents = 1;
        rev.limited = 1;
 
        commit_list_insert(start_commit, &rev.commits);
 
        args[0] = filename;
        args[1] = NULL;
-       diff_tree_setup_paths(args);
+       diff_tree_setup_paths(args, &rev.pruning);
        prepare_revision_walk(&rev);
        process_commits(&rev, filename, &initial);