Add test-chmtime: a utility to change mtime on files
[gitweb.git] / diff-lib.c
index d93cd55510d702ae6c5104ae9f896264ddc98f80..556d5345bfc74c720d35097d24322436ffdebe57 100644 (file)
@@ -7,6 +7,7 @@
 #include "diff.h"
 #include "diffcore.h"
 #include "revision.h"
+#include "cache-tree.h"
 
 /*
  * diff-files
@@ -34,21 +35,23 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
                        continue;
 
                if (ce_stage(ce)) {
-                       struct {
-                               struct combine_diff_path p;
-                               struct combine_diff_parent filler[5];
-                       } combine;
+                       struct combine_diff_path *dpath;
                        int num_compare_stages = 0;
+                       size_t path_len;
 
-                       combine.p.next = NULL;
-                       combine.p.len = ce_namelen(ce);
-                       combine.p.path = xmalloc(combine.p.len + 1);
-                       memcpy(combine.p.path, ce->name, combine.p.len);
-                       combine.p.path[combine.p.len] = 0;
-                       combine.p.mode = 0;
-                       memset(combine.p.sha1, 0, 20);
-                       memset(&combine.p.parent[0], 0,
-                              sizeof(combine.filler));
+                       path_len = ce_namelen(ce);
+
+                       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;
+                       hashclr(dpath->sha1);
+                       memset(&(dpath->parent[0]), 0,
+                                       sizeof(struct combine_diff_parent)*5);
 
                        while (i < entries) {
                                struct cache_entry *nce = active_cache[i];
@@ -64,11 +67,10 @@ 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(combine.p.parent[stage-2].sha1,
-                                              nce->sha1, 20);
-                                       combine.p.parent[stage-2].mode =
+                                       hashcpy(dpath->parent[stage-2].sha1, nce->sha1);
+                                       dpath->parent[stage-2].mode =
                                                canon_mode(mode);
-                                       combine.p.parent[stage-2].status =
+                                       dpath->parent[stage-2].status =
                                                DIFF_STATUS_MODIFIED;
                                }
 
@@ -83,19 +85,20 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
                        i--;
 
                        if (revs->combine_merges && num_compare_stages == 2) {
-                               show_combined_diff(&combine.p, 2,
+                               show_combined_diff(dpath, 2,
                                                   revs->dense_combined_merges,
                                                   revs);
-                               free(combine.p.path);
+                               free(dpath);
                                continue;
                        }
-                       free(combine.p.path);
+                       free(dpath);
+                       dpath = NULL;
 
                        /*
                         * 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;
                }
@@ -167,9 +170,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;
                }
        }
@@ -211,8 +212,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;
 
@@ -244,7 +270,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:
@@ -270,9 +296,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:
@@ -342,3 +371,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);
+}