Merge branch 'maint'
[gitweb.git] / diff-lib.c
index 116b5a9d6834c9517ccc2202510b37a1e1d863a5..60c0fa6488eda7ae382500b70041aac4202a59c0 100644 (file)
@@ -7,6 +7,7 @@
 #include "diff.h"
 #include "diffcore.h"
 #include "revision.h"
+#include "cache-tree.h"
 
 /*
  * diff-files
@@ -40,17 +41,27 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
 
                        path_len = ce_namelen(ce);
 
-                       dpath = xmalloc (combine_diff_path_size (5, path_len));
+                       dpath = xmalloc(combine_diff_path_size(5, path_len));
                        dpath->path = (char *) &(dpath->parent[5]);
 
                        dpath->next = NULL;
                        dpath->len = path_len;
                        memcpy(dpath->path, ce->name, path_len);
                        dpath->path[path_len] = '\0';
-                       dpath->mode = 0;
-                       memset(dpath->sha1, 0, 20);
+                       hashclr(dpath->sha1);
                        memset(&(dpath->parent[0]), 0,
-                                       sizeof(struct combine_diff_parent)*5);
+                              sizeof(struct combine_diff_parent)*5);
+
+                       if (lstat(ce->name, &st) < 0) {
+                               if (errno != ENOENT && errno != ENOTDIR) {
+                                       perror(ce->name);
+                                       continue;
+                               }
+                               if (silent_on_removed)
+                                       continue;
+                       }
+                       else
+                               dpath->mode = canon_mode(st.st_mode);
 
                        while (i < entries) {
                                struct cache_entry *nce = active_cache[i];
@@ -66,8 +77,7 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
                                if (2 <= stage) {
                                        int mode = ntohl(nce->ce_mode);
                                        num_compare_stages++;
-                                       memcpy(dpath->parent[stage-2].sha1,
-                                              nce->sha1, 20);
+                                       hashcpy(dpath->parent[stage-2].sha1, nce->sha1);
                                        dpath->parent[stage-2].mode =
                                                canon_mode(mode);
                                        dpath->parent[stage-2].status =
@@ -98,7 +108,7 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
                         * Show the diff for the 'ce' if we found the one
                         * from the desired stage.
                         */
-                       diff_unmerge(&revs->diffopt, ce->name);
+                       diff_unmerge(&revs->diffopt, ce->name, 0, null_sha1);
                        if (ce_stage(ce) != diff_unmerged_stage)
                                continue;
                }
@@ -170,9 +180,7 @@ static int get_stat_data(struct cache_entry *ce,
                }
                changed = ce_match_stat(ce, &st, 0);
                if (changed) {
-                       mode = create_ce_mode(st.st_mode);
-                       if (!trust_executable_bit && S_ISREG(st.st_mode))
-                               mode = ce->ce_mode;
+                       mode = ce_mode_from_stat(ce, st.st_mode);
                        sha1 = no_sha1;
                }
        }
@@ -214,8 +222,33 @@ static int show_modified(struct rev_info *revs,
                return -1;
        }
 
+       if (revs->combine_merges && !cached &&
+           (hashcmp(sha1, old->sha1) || hashcmp(old->sha1, new->sha1))) {
+               struct combine_diff_path *p;
+               int pathlen = ce_namelen(new);
+
+               p = xmalloc(combine_diff_path_size(2, pathlen));
+               p->path = (char *) &p->parent[2];
+               p->next = NULL;
+               p->len = pathlen;
+               memcpy(p->path, new->name, pathlen);
+               p->path[pathlen] = 0;
+               p->mode = ntohl(mode);
+               hashclr(p->sha1);
+               memset(p->parent, 0, 2 * sizeof(struct combine_diff_parent));
+               p->parent[0].status = DIFF_STATUS_MODIFIED;
+               p->parent[0].mode = ntohl(new->ce_mode);
+               hashcpy(p->parent[0].sha1, new->sha1);
+               p->parent[1].status = DIFF_STATUS_MODIFIED;
+               p->parent[1].mode = ntohl(old->ce_mode);
+               hashcpy(p->parent[1].sha1, old->sha1);
+               show_combined_diff(p, 2, revs->dense_combined_merges, revs);
+               free(p);
+               return 0;
+       }
+
        oldmode = old->ce_mode;
-       if (mode == oldmode && !memcmp(sha1, old->sha1, 20) &&
+       if (mode == oldmode && !hashcmp(sha1, old->sha1) &&
            !revs->diffopt.find_copies_harder)
                return 0;
 
@@ -247,7 +280,7 @@ static int diff_cache(struct rev_info *revs,
                                break;
                        }
                        /* Show difference between old and new */
-                       show_modified(revs,ac[1], ce, 1,
+                       show_modified(revs, ac[1], ce, 1,
                                      cached, match_missing);
                        break;
                case 1:
@@ -273,9 +306,12 @@ static int diff_cache(struct rev_info *revs,
                            !show_modified(revs, ce, ac[1], 0,
                                           cached, match_missing))
                                break;
-                       /* fallthru */
+                       diff_unmerge(&revs->diffopt, ce->name,
+                                    ntohl(ce->ce_mode), ce->sha1);
+                       break;
                case 3:
-                       diff_unmerge(&revs->diffopt, ce->name);
+                       diff_unmerge(&revs->diffopt, ce->name,
+                                    0, null_sha1);
                        break;
 
                default:
@@ -345,3 +381,44 @@ int run_diff_index(struct rev_info *revs, int cached)
        diff_flush(&revs->diffopt);
        return ret;
 }
+
+int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
+{
+       struct tree *tree;
+       struct rev_info revs;
+       int i;
+       struct cache_entry **dst;
+       struct cache_entry *last = NULL;
+
+       /*
+        * This is used by git-blame to run diff-cache internally;
+        * it potentially needs to repeatedly run this, so we will
+        * start by removing the higher order entries the last round
+        * left behind.
+        */
+       dst = active_cache;
+       for (i = 0; i < active_nr; i++) {
+               struct cache_entry *ce = active_cache[i];
+               if (ce_stage(ce)) {
+                       if (last && !strcmp(ce->name, last->name))
+                               continue;
+                       cache_tree_invalidate_path(active_cache_tree,
+                                                  ce->name);
+                       last = ce;
+                       ce->ce_mode = 0;
+                       ce->ce_flags &= ~htons(CE_STAGEMASK);
+               }
+               *dst++ = ce;
+       }
+       active_nr = dst - active_cache;
+
+       init_revisions(&revs, NULL);
+       revs.prune_data = opt->paths;
+       tree = parse_tree_indirect(tree_sha1);
+       if (!tree)
+               die("bad tree object %s", sha1_to_hex(tree_sha1));
+       if (read_tree(tree, 1, opt->paths))
+               return error("unable to read tree %s", sha1_to_hex(tree_sha1));
+       return diff_cache(&revs, active_cache, active_nr, revs.prune_data,
+                         1, 0);
+}