aboutsummaryrefslogtreecommitdiff
path: root/diff-lib.c
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2008-02-22 22:54:37 -0800
committerJunio C Hamano <gitster@pobox.com>2008-02-22 22:54:37 -0800
commit50f3ac29cbadbf7e0ff099b493b00cfa4129e1e0 (patch)
tree72b756b4c7d60709b7484cceeb3a1d82a18a86af /diff-lib.c
parentcb97cc9fef60ea2ff1ce51cf575314c04488dbfd (diff)
parent4cd883d724ec36a120263d47058e65c6d1de642f (diff)
downloadgit-50f3ac29cbadbf7e0ff099b493b00cfa4129e1e0.tar.gz
git-50f3ac29cbadbf7e0ff099b493b00cfa4129e1e0.tar.xz
Merge branch 'bc/reflog-fix' into js/reflog-delete
* bc/reflog-fix: (1490 commits) builtin-reflog.c: don't install new reflog on write failure hash: fix lookup_hash semantics gitweb: Better chopping in commit search results builtin-tag.c: remove cruft git-merge-index documentation: clarify synopsis send-email: fix In-Reply-To regression git-reset --hard and git-read-tree --reset: fix read_cache_unmerged() Teach git-grep --name-only as synonym for -l diff: fix java funcname pattern for solaris t3404: use configured shell instead of /bin/sh git_config_*: don't assume we are parsing a config file prefix_path: use is_absolute_path() instead of *orig == '/' git-clean: handle errors if removing files fails Clarified the meaning of git-add -u in the documentation git-clone.sh: properly configure remote even if remote's head is dangling git.el: Set process-environment instead of invoking env Documentation/git-stash: document options for git stash list send-email: squelch warning due to comparing undefined $_ to "" cvsexportcommit: be graceful when "cvs status" reorders the arguments Rename git-core rpm to just git and rename the meta-pacakge to git-all. ... Conflicts: Documentation/git-reflog.txt t/t1410-reflog.sh
Diffstat (limited to 'diff-lib.c')
-rw-r--r--diff-lib.c313
1 files changed, 186 insertions, 127 deletions
diff --git a/diff-lib.c b/diff-lib.c
index da5571302..03eaa7cef 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -9,6 +9,7 @@
#include "revision.h"
#include "cache-tree.h"
#include "path-list.h"
+#include "unpack-trees.h"
/*
* diff-files
@@ -37,7 +38,7 @@ static int get_mode(const char *path, int *mode)
if (!path || !strcmp(path, "/dev/null"))
*mode = 0;
else if (!strcmp(path, "-"))
- *mode = ntohl(create_ce_mode(0666));
+ *mode = create_ce_mode(0666);
else if (stat(path, &st))
return error("Could not access '%s'", path);
else
@@ -121,7 +122,7 @@ static int queue_diff(struct diff_options *o,
} else {
struct diff_filespec *d1, *d2;
- if (o->reverse_diff) {
+ if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
unsigned tmp;
const char *tmp_c;
tmp = mode1; mode1 = mode2; mode2 = tmp;
@@ -173,9 +174,10 @@ static int is_in_index(const char *path)
}
static int handle_diff_files_args(struct rev_info *revs,
- int argc, const char **argv, int *silent)
+ int argc, const char **argv,
+ unsigned int *options)
{
- *silent = 0;
+ *options = 0;
/* revs->max_count == -2 means --no-index */
while (1 < argc && argv[1][0] == '-') {
@@ -188,11 +190,11 @@ static int handle_diff_files_args(struct rev_info *revs,
else if (!strcmp(argv[1], "-n") ||
!strcmp(argv[1], "--no-index")) {
revs->max_count = -2;
- revs->diffopt.exit_with_status = 1;
- revs->diffopt.no_index = 1;
+ DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS);
+ DIFF_OPT_SET(&revs->diffopt, NO_INDEX);
}
else if (!strcmp(argv[1], "-q"))
- *silent = 1;
+ *options |= DIFF_SILENT_ON_REMOVED;
else
return error("invalid option: %s", argv[1]);
argv++; argc--;
@@ -207,7 +209,7 @@ static int handle_diff_files_args(struct rev_info *revs,
if (!is_in_index(revs->diffopt.paths[0]) ||
!is_in_index(revs->diffopt.paths[1])) {
revs->max_count = -2;
- revs->diffopt.no_index = 1;
+ DIFF_OPT_SET(&revs->diffopt, NO_INDEX);
}
}
@@ -230,7 +232,7 @@ static int handle_diff_files_args(struct rev_info *revs,
static int is_outside_repo(const char *path, int nongit, const char *prefix)
{
int i;
- if (nongit || !strcmp(path, "-") || path[0] == '/')
+ if (nongit || !strcmp(path, "-") || is_absolute_path(path))
return 1;
if (prefixcmp(path, "../"))
return 0;
@@ -258,7 +260,7 @@ int setup_diff_no_index(struct rev_info *revs,
break;
} else if (i < argc - 3 && !strcmp(argv[i], "--no-index")) {
i = argc - 3;
- revs->diffopt.exit_with_status = 1;
+ DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS);
break;
}
if (argc != i + 2 || (!is_outside_repo(argv[i + 1], nongit, prefix) &&
@@ -296,7 +298,7 @@ int setup_diff_no_index(struct rev_info *revs,
else
revs->diffopt.paths = argv + argc - 2;
revs->diffopt.nr_paths = 2;
- revs->diffopt.no_index = 1;
+ DIFF_OPT_SET(&revs->diffopt, NO_INDEX);
revs->max_count = -2;
if (diff_setup_done(&revs->diffopt) < 0)
die("diff_setup_done failed");
@@ -305,12 +307,12 @@ int setup_diff_no_index(struct rev_info *revs,
int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)
{
- int silent_on_removed;
+ unsigned int options;
- if (handle_diff_files_args(revs, argc, argv, &silent_on_removed))
+ if (handle_diff_files_args(revs, argc, argv, &options))
return -1;
- if (revs->diffopt.no_index) {
+ if (DIFF_OPT_TST(&revs->diffopt, NO_INDEX)) {
if (revs->diffopt.nr_paths != 2)
return error("need two files/directories with --no-index");
if (queue_diff(&revs->diffopt, revs->diffopt.paths[0],
@@ -329,13 +331,16 @@ int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)
perror("read_cache");
return -1;
}
- return run_diff_files(revs, silent_on_removed);
+ return run_diff_files(revs, options);
}
-int run_diff_files(struct rev_info *revs, int silent_on_removed)
+int run_diff_files(struct rev_info *revs, unsigned int option)
{
int entries, i;
int diff_unmerged_stage = revs->max_count;
+ int silent_on_removed = option & DIFF_SILENT_ON_REMOVED;
+ unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED)
+ ? CE_MATCH_RACY_IS_DIRTY : 0);
if (diff_unmerged_stage < 0)
diff_unmerged_stage = 2;
@@ -346,7 +351,8 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
struct cache_entry *ce = active_cache[i];
int changed;
- if (revs->diffopt.quiet && revs->diffopt.has_changes)
+ if (DIFF_OPT_TST(&revs->diffopt, QUIET) &&
+ DIFF_OPT_TST(&revs->diffopt, HAS_CHANGES))
break;
if (!ce_path_match(ce, revs->prune_data))
@@ -379,7 +385,7 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
continue;
}
else
- dpath->mode = ntohl(ce_mode_from_stat(ce, st.st_mode));
+ dpath->mode = ce_mode_from_stat(ce, st.st_mode);
while (i < entries) {
struct cache_entry *nce = active_cache[i];
@@ -393,10 +399,10 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
*/
stage = ce_stage(nce);
if (2 <= stage) {
- int mode = ntohl(nce->ce_mode);
+ int mode = nce->ce_mode;
num_compare_stages++;
hashcpy(dpath->parent[stage-2].sha1, nce->sha1);
- dpath->parent[stage-2].mode = ntohl(ce_mode_from_stat(nce, mode));
+ dpath->parent[stage-2].mode = ce_mode_from_stat(nce, mode);
dpath->parent[stage-2].status =
DIFF_STATUS_MODIFIED;
}
@@ -430,6 +436,8 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
continue;
}
+ if (ce_uptodate(ce))
+ continue;
if (lstat(ce->name, &st) < 0) {
if (errno != ENOENT && errno != ENOTDIR) {
perror(ce->name);
@@ -437,15 +445,15 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
}
if (silent_on_removed)
continue;
- diff_addremove(&revs->diffopt, '-', ntohl(ce->ce_mode),
+ diff_addremove(&revs->diffopt, '-', ce->ce_mode,
ce->sha1, ce->name, NULL);
continue;
}
- changed = ce_match_stat(ce, &st, 0);
- if (!changed && !revs->diffopt.find_copies_harder)
+ changed = ce_match_stat(ce, &st, ce_option);
+ if (!changed && !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
continue;
- oldmode = ntohl(ce->ce_mode);
- newmode = ntohl(ce_mode_from_stat(ce, st.st_mode));
+ oldmode = ce->ce_mode;
+ newmode = ce_mode_from_stat(ce, st.st_mode);
diff_change(&revs->diffopt, oldmode, newmode,
ce->sha1, (changed ? null_sha1 : ce->sha1),
ce->name, NULL);
@@ -466,7 +474,7 @@ static void diff_index_show_file(struct rev_info *revs,
struct cache_entry *ce,
unsigned char *sha1, unsigned int mode)
{
- diff_addremove(&revs->diffopt, prefix[0], ntohl(mode),
+ diff_addremove(&revs->diffopt, prefix[0], mode,
sha1, ce->name, NULL);
}
@@ -545,14 +553,14 @@ static int show_modified(struct rev_info *revs,
p->len = pathlen;
memcpy(p->path, new->name, pathlen);
p->path[pathlen] = 0;
- p->mode = ntohl(mode);
+ p->mode = 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);
+ p->parent[0].mode = 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);
+ p->parent[1].mode = old->ce_mode;
hashcpy(p->parent[1].sha1, old->sha1);
show_combined_diff(p, 2, revs->dense_combined_merges, revs);
free(p);
@@ -561,121 +569,157 @@ static int show_modified(struct rev_info *revs,
oldmode = old->ce_mode;
if (mode == oldmode && !hashcmp(sha1, old->sha1) &&
- !revs->diffopt.find_copies_harder)
+ !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
return 0;
- mode = ntohl(mode);
- oldmode = ntohl(oldmode);
-
diff_change(&revs->diffopt, oldmode, mode,
old->sha1, sha1, old->name, NULL);
return 0;
}
-static int diff_cache(struct rev_info *revs,
- struct cache_entry **ac, int entries,
- const char **pathspec,
- int cached, int match_missing)
+/*
+ * This turns all merge entries into "stage 3". That guarantees that
+ * when we read in the new tree (into "stage 1"), we won't lose sight
+ * of the fact that we had unmerged entries.
+ */
+static void mark_merge_entries(void)
{
- while (entries) {
- struct cache_entry *ce = *ac;
- int same = (entries > 1) && ce_same_name(ce, ac[1]);
+ int i;
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ if (!ce_stage(ce))
+ continue;
+ ce->ce_flags |= CE_STAGEMASK;
+ }
+}
- if (revs->diffopt.quiet && revs->diffopt.has_changes)
- break;
+/*
+ * This gets a mix of an existing index and a tree, one pathname entry
+ * at a time. The index entry may be a single stage-0 one, but it could
+ * also be multiple unmerged entries (in which case idx_pos/idx_nr will
+ * give you the position and number of entries in the index).
+ */
+static void do_oneway_diff(struct unpack_trees_options *o,
+ struct cache_entry *idx,
+ struct cache_entry *tree,
+ int idx_pos, int idx_nr)
+{
+ struct rev_info *revs = o->unpack_data;
+ int match_missing, cached;
+
+ /*
+ * Backward compatibility wart - "diff-index -m" does
+ * not mean "do not ignore merges", but "match_missing".
+ *
+ * But with the revision flag parsing, that's found in
+ * "!revs->ignore_merges".
+ */
+ cached = o->index_only;
+ match_missing = !revs->ignore_merges;
+
+ if (cached && idx && ce_stage(idx)) {
+ if (tree)
+ diff_unmerge(&revs->diffopt, idx->name, idx->ce_mode, idx->sha1);
+ return;
+ }
+
+ /*
+ * Something added to the tree?
+ */
+ if (!tree) {
+ show_new_file(revs, idx, cached, match_missing);
+ return;
+ }
+
+ /*
+ * Something removed from the tree?
+ */
+ if (!idx) {
+ diff_index_show_file(revs, "-", tree, tree->sha1, tree->ce_mode);
+ return;
+ }
+
+ /* Show difference between old and new */
+ show_modified(revs, tree, idx, 1, cached, match_missing);
+}
+
+/*
+ * Count how many index entries go with the first one
+ */
+static inline int count_skip(const struct cache_entry *src, int pos)
+{
+ int skip = 1;
- if (!ce_path_match(ce, pathspec))
- goto skip_entry;
+ /* We can only have multiple entries if the first one is not stage-0 */
+ if (ce_stage(src)) {
+ struct cache_entry **p = active_cache + pos;
+ int namelen = ce_namelen(src);
- switch (ce_stage(ce)) {
- case 0:
- /* No stage 1 entry? That means it's a new file */
- if (!same) {
- show_new_file(revs, ce, cached, match_missing);
+ for (;;) {
+ const struct cache_entry *ce;
+ pos++;
+ if (pos >= active_nr)
break;
- }
- /* Show difference between old and new */
- show_modified(revs, ac[1], ce, 1,
- cached, match_missing);
- break;
- case 1:
- /* No stage 3 (merge) entry?
- * That means it's been deleted.
- */
- if (!same) {
- diff_index_show_file(revs, "-", ce,
- ce->sha1, ce->ce_mode);
+ ce = *++p;
+ if (ce_namelen(ce) != namelen)
break;
- }
- /* We come here with ce pointing at stage 1
- * (original tree) and ac[1] pointing at stage
- * 3 (unmerged). show-modified with
- * report-missing set to false does not say the
- * file is deleted but reports true if work
- * tree does not have it, in which case we
- * fall through to report the unmerged state.
- * Otherwise, we show the differences between
- * the original tree and the work tree.
- */
- if (!cached &&
- !show_modified(revs, ce, ac[1], 0,
- cached, match_missing))
+ if (memcmp(ce->name, src->name, namelen))
break;
- diff_unmerge(&revs->diffopt, ce->name,
- ntohl(ce->ce_mode), ce->sha1);
- break;
- case 3:
- diff_unmerge(&revs->diffopt, ce->name,
- 0, null_sha1);
- break;
-
- default:
- die("impossible cache entry stage");
+ skip++;
}
-
-skip_entry:
- /*
- * Ignore all the different stages for this file,
- * we've handled the relevant cases now.
- */
- do {
- ac++;
- entries--;
- } while (entries && ce_same_name(ce, ac[0]));
}
- return 0;
+ return skip;
}
/*
- * This turns all merge entries into "stage 3". That guarantees that
- * when we read in the new tree (into "stage 1"), we won't lose sight
- * of the fact that we had unmerged entries.
+ * The unpack_trees() interface is designed for merging, so
+ * the different source entries are designed primarily for
+ * the source trees, with the old index being really mainly
+ * used for being replaced by the result.
+ *
+ * For diffing, the index is more important, and we only have a
+ * single tree.
+ *
+ * We're supposed to return how many index entries we want to skip.
+ *
+ * This wrapper makes it all more readable, and takes care of all
+ * the fairly complex unpack_trees() semantic requirements, including
+ * the skipping, the path matching, the type conflict cases etc.
*/
-static void mark_merge_entries(void)
+static int oneway_diff(struct cache_entry **src,
+ struct unpack_trees_options *o,
+ int index_pos)
{
- int i;
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
- if (!ce_stage(ce))
- continue;
- ce->ce_flags |= htons(CE_STAGEMASK);
- }
+ int skip = 0;
+ struct cache_entry *idx = src[0];
+ struct cache_entry *tree = src[1];
+ struct rev_info *revs = o->unpack_data;
+
+ if (index_pos >= 0)
+ skip = count_skip(idx, index_pos);
+
+ /*
+ * Unpack-trees generates a DF/conflict entry if
+ * there was a directory in the index and a tree
+ * in the tree. From a diff standpoint, that's a
+ * delete of the tree and a create of the file.
+ */
+ if (tree == o->df_conflict_entry)
+ tree = NULL;
+
+ if (ce_path_match(idx ? idx : tree, revs->prune_data))
+ do_oneway_diff(o, idx, tree, index_pos, skip);
+
+ return skip;
}
int run_diff_index(struct rev_info *revs, int cached)
{
- int ret;
struct object *ent;
struct tree *tree;
const char *tree_name;
- int match_missing = 0;
-
- /*
- * Backward compatibility wart - "diff-index -m" does
- * not mean "do not ignore merges", but totally different.
- */
- if (!revs->ignore_merges)
- match_missing = 1;
+ struct unpack_trees_options opts;
+ struct tree_desc t;
mark_merge_entries();
@@ -684,13 +728,20 @@ int run_diff_index(struct rev_info *revs, int cached)
tree = parse_tree_indirect(ent->sha1);
if (!tree)
return error("bad tree object %s", tree_name);
- if (read_tree(tree, 1, revs->prune_data))
- return error("unable to read tree object %s", tree_name);
- ret = diff_cache(revs, active_cache, active_nr, revs->prune_data,
- cached, match_missing);
+
+ memset(&opts, 0, sizeof(opts));
+ opts.head_idx = 1;
+ opts.index_only = cached;
+ opts.merge = 1;
+ opts.fn = oneway_diff;
+ opts.unpack_data = revs;
+
+ init_tree_desc(&t, tree->buffer, tree->size);
+ unpack_trees(1, &t, &opts);
+
diffcore_std(&revs->diffopt);
diff_flush(&revs->diffopt);
- return ret;
+ return 0;
}
int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
@@ -700,6 +751,8 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
int i;
struct cache_entry **dst;
struct cache_entry *last = NULL;
+ struct unpack_trees_options opts;
+ struct tree_desc t;
/*
* This is used by git-blame to run diff-cache internally;
@@ -716,8 +769,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
cache_tree_invalidate_path(active_cache_tree,
ce->name);
last = ce;
- ce->ce_mode = 0;
- ce->ce_flags &= ~htons(CE_STAGEMASK);
+ ce->ce_flags |= CE_REMOVE;
}
*dst++ = ce;
}
@@ -728,8 +780,15 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
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);
+
+ memset(&opts, 0, sizeof(opts));
+ opts.head_idx = 1;
+ opts.index_only = 1;
+ opts.merge = 1;
+ opts.fn = oneway_diff;
+ opts.unpack_data = &revs;
+
+ init_tree_desc(&t, tree->buffer, tree->size);
+ unpack_trees(1, &t, &opts);
+ return 0;
}