Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
git-mirror
/
git
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
0
Pull requests
0
Actions
Projects
0
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Security
Insights
Files
9a8c2b6
Documentation
block-sha1
builtin
compat
contrib
ewah
git-gui
gitk-git
gitweb
mergetools
perl
po
ppc
t
templates
vcs-svn
xdiff
.gitattributes
.gitignore
.mailmap
COPYING
GIT-VERSION-GEN
INSTALL
LGPL-2.1
Makefile
README
RelNotes
abspath.c
aclocal.m4
advice.c
advice.h
alias.c
alloc.c
archive-tar.c
archive-zip.c
archive.c
archive.h
argv-array.c
argv-array.h
attr.c
attr.h
base85.c
bisect.c
bisect.h
blob.c
blob.h
branch.c
branch.h
builtin.h
bulk-checkin.c
bulk-checkin.h
bundle.c
bundle.h
cache-tree.c
cache-tree.h
cache.h
check-builtins.sh
check-racy.c
check_bindir
color.c
color.h
column.c
column.h
combine-diff.c
command-list.txt
commit-slab.h
commit.c
commit.h
config.c
config.mak.in
config.mak.uname
configure.ac
connect.c
connect.h
connected.c
connected.h
convert.c
convert.h
copy.c
credential-cache--daemon.c
credential-cache.c
credential-store.c
credential.c
credential.h
csum-file.c
csum-file.h
ctype.c
daemon.c
date.c
decorate.c
decorate.h
delta.h
diff-delta.c
diff-lib.c
diff-no-index.c
diff.c
diff.h
diffcore-break.c
diffcore-delta.c
diffcore-order.c
diffcore-pickaxe.c
diffcore-rename.c
diffcore.h
dir.c
dir.h
editor.c
entry.c
environment.c
exec_cmd.c
exec_cmd.h
fast-import.c
fetch-pack.c
fetch-pack.h
fmt-merge-msg.h
fsck.c
fsck.h
generate-cmdlist.sh
gettext.c
gettext.h
git-add--interactive.perl
git-am.sh
git-archimport.perl
git-bisect.sh
git-compat-util.h
git-cvsexportcommit.perl
git-cvsimport.perl
git-cvsserver.perl
git-difftool--helper.sh
git-difftool.perl
git-filter-branch.sh
git-instaweb.sh
git-merge-octopus.sh
git-merge-one-file.sh
git-merge-resolve.sh
git-mergetool--lib.sh
git-mergetool.sh
git-p4.py
git-parse-remote.sh
git-pull.sh
git-quiltimport.sh
git-rebase--am.sh
git-rebase--interactive.sh
git-rebase--merge.sh
git-rebase.sh
git-relink.perl
git-remote-testgit.sh
git-request-pull.sh
git-send-email.perl
git-sh-i18n.sh
git-sh-setup.sh
git-stash.sh
git-submodule.sh
git-svn.perl
git-web--browse.sh
git.c
git.rc
git.spec.in
gpg-interface.c
gpg-interface.h
graph.c
graph.h
grep.c
grep.h
hashmap.c
hashmap.h
help.c
help.h
hex.c
http-backend.c
http-fetch.c
http-push.c
http-walker.c
http.c
http.h
ident.c
imap-send.c
khash.h
kwset.c
kwset.h
levenshtein.c
levenshtein.h
line-log.c
line-log.h
line-range.c
line-range.h
list-objects.c
list-objects.h
ll-merge.c
ll-merge.h
lockfile.c
log-tree.c
log-tree.h
mailmap.c
mailmap.h
match-trees.c
merge-blobs.c
merge-blobs.h
merge-recursive.c
merge-recursive.h
merge.c
mergesort.c
mergesort.h
name-hash.c
notes-cache.c
notes-cache.h
notes-merge.c
notes-merge.h
notes-utils.c
notes-utils.h
notes.c
notes.h
object.c
object.h
pack-bitmap-write.c
pack-bitmap.c
pack-bitmap.h
pack-check.c
pack-objects.c
pack-objects.h
pack-revindex.c
pack-revindex.h
pack-write.c
pack.h
pager.c
parse-options-cb.c
parse-options.c
parse-options.h
patch-delta.c
patch-ids.c
patch-ids.h
path.c
pathspec.c
pathspec.h
pkt-line.c
pkt-line.h
preload-index.c
pretty.c
prio-queue.c
prio-queue.h
progress.c
progress.h
prompt.c
prompt.h
quote.c
quote.h
reachable.c
reachable.h
read-cache.c
reflog-walk.c
reflog-walk.h
refs.c
refs.h
remote-curl.c
remote-testsvn.c
remote.c
remote.h
replace_object.c
rerere.c
rerere.h
resolve-undo.c
resolve-undo.h
revision.c
revision.h
run-command.c
run-command.h
send-pack.c
send-pack.h
sequencer.c
sequencer.h
server-info.c
setup.c
sh-i18n--envsubst.c
sha1-array.c
sha1-array.h
sha1-lookup.c
sha1-lookup.h
sha1_file.c
sha1_name.c
shallow.c
shell.c
shortlog.h
show-index.c
sideband.c
sideband.h
sigchain.c
sigchain.h
strbuf.c
strbuf.h
streaming.c
streaming.h
string-list.c
string-list.h
submodule.c
submodule.h
symlinks.c
tag.c
tag.h
tar.h
test-chmtime.c
test-ctype.c
test-date.c
test-delta.c
test-dump-cache-tree.c
test-genrandom.c
test-hashmap.c
test-index-version.c
test-line-buffer.c
test-match-trees.c
test-mergesort.c
test-mktemp.c
test-parse-options.c
test-path-utils.c
test-prio-queue.c
test-read-cache.c
test-regex.c
test-revision-walking.c
test-run-command.c
test-scrap-cache-tree.c
test-sha1.c
test-sha1.sh
test-sigchain.c
test-string-list.c
test-subprocess.c
test-svn-fe.c
test-urlmatch-normalization.c
test-wildmatch.c
thread-utils.c
thread-utils.h
trace.c
transport-helper.c
transport.c
transport.h
tree-diff.c
tree-walk.c
tree-walk.h
tree.c
tree.h
unimplemented.sh
unix-socket.c
unix-socket.h
unpack-trees.c
unpack-trees.h
upload-pack.c
url.c
url.h
urlmatch.c
urlmatch.h
usage.c
userdiff.c
userdiff.h
utf8.c
utf8.h
varint.c
varint.h
version.c
version.h
versioncmp.c
walker.c
walker.h
wildmatch.c
wildmatch.h
wrap-for-bin.sh
wrapper.c
write_or_die.c
ws.c
wt-status.c
wt-status.h
xdiff-interface.c
xdiff-interface.h
zlib.c
Breadcrumbs
git
/
diff-lib.c
Blame
Blame
Latest commit
History
History
531 lines (470 loc) · 14 KB
Breadcrumbs
git
/
diff-lib.c
Top
File metadata and controls
Code
Blame
531 lines (470 loc) · 14 KB
Raw
/* * Copyright (C) 2005 Junio C Hamano */ #include "cache.h" #include "quote.h" #include "commit.h" #include "diff.h" #include "diffcore.h" #include "revision.h" #include "cache-tree.h" #include "unpack-trees.h" #include "refs.h" #include "submodule.h" #include "dir.h" /* * diff-files */ /* * Has the work tree entity been removed? * * Return 1 if it was removed from the work tree, 0 if an entity to be * compared with the cache entry ce still exists (the latter includes * the case where a directory that is not a submodule repository * exists for ce that is a submodule -- it is a submodule that is not * checked out). Return negative for an error. */ static int check_removed(const struct cache_entry *ce, struct stat *st) { if (lstat(ce->name, st) < 0) { if (errno != ENOENT && errno != ENOTDIR) return -1; return 1; } if (has_symlink_leading_path(ce->name, ce_namelen(ce))) return 1; if (S_ISDIR(st->st_mode)) { unsigned char sub[20]; /* * If ce is already a gitlink, we can have a plain * directory (i.e. the submodule is not checked out), * or a checked out submodule. Either case this is not * a case where something was removed from the work tree, * so we will return 0. * * Otherwise, if the directory is not a submodule * repository, that means ce which was a blob turned into * a directory --- the blob was removed! */ if (!S_ISGITLINK(ce->ce_mode) && resolve_gitlink_ref(ce->name, "HEAD", sub)) return 1; } return 0; } /* * Has a file changed or has a submodule new commits or a dirty work tree? * * Return 1 when changes are detected, 0 otherwise. If the DIRTY_SUBMODULES * option is set, the caller does not only want to know if a submodule is * modified at all but wants to know all the conditions that are met (new * commits, untracked content and/or modified content). */ static int match_stat_with_submodule(struct diff_options *diffopt, const struct cache_entry *ce, struct stat *st, unsigned ce_option, unsigned *dirty_submodule) { int changed = ce_match_stat(ce, st, ce_option); if (S_ISGITLINK(ce->ce_mode)) { unsigned orig_flags = diffopt->flags; if (!DIFF_OPT_TST(diffopt, OVERRIDE_SUBMODULE_CONFIG)) set_diffopt_flags_from_submodule_config(diffopt, ce->name); if (DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES)) changed = 0; else if (!DIFF_OPT_TST(diffopt, IGNORE_DIRTY_SUBMODULES) && (!changed || DIFF_OPT_TST(diffopt, DIRTY_SUBMODULES))) *dirty_submodule = is_submodule_modified(ce->name, DIFF_OPT_TST(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES)); diffopt->flags = orig_flags; } return changed; } int run_diff_files(struct rev_info *revs, unsigned int option) { int entries, i; int diff_unmerged_stage = revs->max_count; unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED) ? CE_MATCH_RACY_IS_DIRTY : 0); diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/"); if (diff_unmerged_stage < 0) diff_unmerged_stage = 2; entries = active_nr; for (i = 0; i < entries; i++) { unsigned int oldmode, newmode; struct cache_entry *ce = active_cache[i]; int changed; unsigned dirty_submodule = 0; if (diff_can_quit_early(&revs->diffopt)) break; if (!ce_path_match(ce, &revs->prune_data, NULL)) continue; if (ce_stage(ce)) { struct combine_diff_path *dpath; struct diff_filepair *pair; unsigned int wt_mode = 0; int num_compare_stages = 0; size_t path_len; struct stat st; path_len = ce_namelen(ce); dpath = xmalloc(combine_diff_path_size(5, path_len)); dpath->path = (char *) &(dpath->parent[5]); dpath->next = NULL; memcpy(dpath->path, ce->name, path_len); dpath->path[path_len] = '\0'; hashclr(dpath->sha1); memset(&(dpath->parent[0]), 0, sizeof(struct combine_diff_parent)*5); changed = check_removed(ce, &st); if (!changed) wt_mode = ce_mode_from_stat(ce, st.st_mode); else { if (changed < 0) { perror(ce->name); continue; } wt_mode = 0; } dpath->mode = wt_mode; while (i < entries) { struct cache_entry *nce = active_cache[i]; int stage; if (strcmp(ce->name, nce->name)) break; /* Stage #2 (ours) is the first parent, * stage #3 (theirs) is the second. */ stage = ce_stage(nce); if (2 <= stage) { int mode = nce->ce_mode; num_compare_stages++; hashcpy(dpath->parent[stage-2].sha1, nce->sha1); dpath->parent[stage-2].mode = ce_mode_from_stat(nce, mode); dpath->parent[stage-2].status = DIFF_STATUS_MODIFIED; } /* diff against the proper unmerged stage */ if (stage == diff_unmerged_stage) ce = nce; i++; } /* * Compensate for loop update */ i--; if (revs->combine_merges && num_compare_stages == 2) { show_combined_diff(dpath, 2, revs->dense_combined_merges, revs); free(dpath); continue; } free(dpath); dpath = NULL; /* * Show the diff for the 'ce' if we found the one * from the desired stage. */ pair = diff_unmerge(&revs->diffopt, ce->name); if (wt_mode) pair->two->mode = wt_mode; if (ce_stage(ce) != diff_unmerged_stage) continue; } if (ce_uptodate(ce) || ce_skip_worktree(ce)) continue; /* If CE_VALID is set, don't look at workdir for file removal */ if (ce->ce_flags & CE_VALID) { changed = 0; newmode = ce->ce_mode; } else { struct stat st; changed = check_removed(ce, &st); if (changed) { if (changed < 0) { perror(ce->name); continue; } diff_addremove(&revs->diffopt, '-', ce->ce_mode, ce->sha1, !is_null_sha1(ce->sha1), ce->name, 0); continue; } changed = match_stat_with_submodule(&revs->diffopt, ce, &st, ce_option, &dirty_submodule); newmode = ce_mode_from_stat(ce, st.st_mode); } if (!changed && !dirty_submodule) { ce_mark_uptodate(ce); if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER)) continue; } oldmode = ce->ce_mode; diff_change(&revs->diffopt, oldmode, newmode, ce->sha1, (changed ? null_sha1 : ce->sha1), !is_null_sha1(ce->sha1), (changed ? 0 : !is_null_sha1(ce->sha1)), ce->name, 0, dirty_submodule); } diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); return 0; } /* * diff-index */ /* A file entry went away or appeared */ static void diff_index_show_file(struct rev_info *revs, const char *prefix, const struct cache_entry *ce, const unsigned char *sha1, int sha1_valid, unsigned int mode, unsigned dirty_submodule) { diff_addremove(&revs->diffopt, prefix[0], mode, sha1, sha1_valid, ce->name, dirty_submodule); } static int get_stat_data(const struct cache_entry *ce, const unsigned char **sha1p, unsigned int *modep, int cached, int match_missing, unsigned *dirty_submodule, struct diff_options *diffopt) { const unsigned char *sha1 = ce->sha1; unsigned int mode = ce->ce_mode; if (!cached && !ce_uptodate(ce)) { int changed; struct stat st; changed = check_removed(ce, &st); if (changed < 0) return -1; else if (changed) { if (match_missing) { *sha1p = sha1; *modep = mode; return 0; } return -1; } changed = match_stat_with_submodule(diffopt, ce, &st, 0, dirty_submodule); if (changed) { mode = ce_mode_from_stat(ce, st.st_mode); sha1 = null_sha1; } } *sha1p = sha1; *modep = mode; return 0; } static void show_new_file(struct rev_info *revs, const struct cache_entry *new, int cached, int match_missing) { const unsigned char *sha1; unsigned int mode; unsigned dirty_submodule = 0; /* * New file in the index: it might actually be different in * the working tree. */ if (get_stat_data(new, &sha1, &mode, cached, match_missing, &dirty_submodule, &revs->diffopt) < 0) return; diff_index_show_file(revs, "+", new, sha1, !is_null_sha1(sha1), mode, dirty_submodule); } static int show_modified(struct rev_info *revs, const struct cache_entry *old, const struct cache_entry *new, int report_missing, int cached, int match_missing) { unsigned int mode, oldmode; const unsigned char *sha1; unsigned dirty_submodule = 0; if (get_stat_data(new, &sha1, &mode, cached, match_missing, &dirty_submodule, &revs->diffopt) < 0) { if (report_missing) diff_index_show_file(revs, "-", old, old->sha1, 1, old->ce_mode, 0); 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; memcpy(p->path, new->name, pathlen); p->path[pathlen] = 0; 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 = new->ce_mode; hashcpy(p->parent[0].sha1, new->sha1); p->parent[1].status = DIFF_STATUS_MODIFIED; 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); return 0; } oldmode = old->ce_mode; if (mode == oldmode && !hashcmp(sha1, old->sha1) && !dirty_submodule && !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER)) return 0; diff_change(&revs->diffopt, oldmode, mode, old->sha1, sha1, 1, !is_null_sha1(sha1), old->name, 0, dirty_submodule); return 0; } /* * 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, const struct cache_entry *idx, const struct cache_entry *tree) { struct rev_info *revs = o->unpack_data; int match_missing, cached; /* if the entry is not checked out, don't examine work tree */ cached = o->index_only || (idx && ((idx->ce_flags & CE_VALID) || ce_skip_worktree(idx))); /* * 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". */ match_missing = !revs->ignore_merges; if (cached && idx && ce_stage(idx)) { struct diff_filepair *pair; pair = diff_unmerge(&revs->diffopt, idx->name); if (tree) fill_filespec(pair->one, tree->sha1, 1, tree->ce_mode); 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, 1, tree->ce_mode, 0); return; } /* Show difference between old and new */ show_modified(revs, tree, idx, 1, cached, match_missing); } /* * 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 advance o->pos to skip what we have already processed. * * 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 int oneway_diff(const struct cache_entry * const *src, struct unpack_trees_options *o) { const struct cache_entry *idx = src[0]; const struct cache_entry *tree = src[1]; struct rev_info *revs = o->unpack_data; /* * 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, NULL)) { do_oneway_diff(o, idx, tree); if (diff_can_quit_early(&revs->diffopt)) { o->exiting_early = 1; return -1; } } return 0; } static int diff_cache(struct rev_info *revs, const unsigned char *tree_sha1, const char *tree_name, int cached) { struct tree *tree; struct tree_desc t; struct unpack_trees_options opts; tree = parse_tree_indirect(tree_sha1); if (!tree) return error("bad tree object %s", tree_name ? tree_name : sha1_to_hex(tree_sha1)); memset(&opts, 0, sizeof(opts)); opts.head_idx = 1; opts.index_only = cached; opts.diff_index_cached = (cached && !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER)); opts.merge = 1; opts.fn = oneway_diff; opts.unpack_data = revs; opts.src_index = &the_index; opts.dst_index = NULL; opts.pathspec = &revs->diffopt.pathspec; opts.pathspec->recursive = 1; init_tree_desc(&t, tree->buffer, tree->size); return unpack_trees(1, &t, &opts); } int run_diff_index(struct rev_info *revs, int cached) { struct object_array_entry *ent; ent = revs->pending.objects; if (diff_cache(revs, ent->item->sha1, ent->name, cached)) exit(128); diff_set_mnemonic_prefix(&revs->diffopt, "c/", cached ? "i/" : "w/"); diffcore_fix_diff_index(&revs->diffopt); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); return 0; } int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt) { struct rev_info revs; init_revisions(&revs, NULL); copy_pathspec(&revs.prune_data, &opt->pathspec); revs.diffopt = *opt; if (diff_cache(&revs, tree_sha1, NULL, 1)) exit(128); return 0; } int index_differs_from(const char *def, int diff_flags) { struct rev_info rev; struct setup_revision_opt opt; init_revisions(&rev, NULL); memset(&opt, 0, sizeof(opt)); opt.def = def; setup_revisions(0, NULL, &rev, &opt); DIFF_OPT_SET(&rev.diffopt, QUICK); DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS); rev.diffopt.flags |= diff_flags; run_diff_index(&rev, 1); if (rev.pending.alloc) free(rev.pending.objects); return (DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
You can’t perform that action at this time.