Coverage Report

Created: 2024-09-08 06:23

/src/git/builtin/diff-tree.c
Line
Count
Source (jump to first uncovered line)
1
#include "builtin.h"
2
#include "config.h"
3
#include "diff.h"
4
#include "commit.h"
5
#include "gettext.h"
6
#include "hex.h"
7
#include "log-tree.h"
8
#include "read-cache-ll.h"
9
#include "repository.h"
10
#include "revision.h"
11
#include "tmp-objdir.h"
12
#include "tree.h"
13
14
static struct rev_info log_tree_opt;
15
16
static int diff_tree_commit_oid(const struct object_id *oid)
17
0
{
18
0
  struct commit *commit = lookup_commit_reference(the_repository, oid);
19
0
  if (!commit)
20
0
    return -1;
21
0
  return log_tree_commit(&log_tree_opt, commit);
22
0
}
23
24
/* Diff one or more commits. */
25
static int stdin_diff_commit(struct commit *commit, const char *p)
26
0
{
27
0
  struct object_id oid;
28
0
  struct commit_list **pptr = NULL;
29
30
  /* Graft the fake parents locally to the commit */
31
0
  while (isspace(*p++) && !parse_oid_hex(p, &oid, &p)) {
32
0
    struct commit *parent = lookup_commit(the_repository, &oid);
33
0
    if (!pptr) {
34
      /* Free the real parent list */
35
0
      free_commit_list(commit->parents);
36
0
      commit->parents = NULL;
37
0
      pptr = &(commit->parents);
38
0
    }
39
0
    if (parent) {
40
0
      pptr = &commit_list_insert(parent, pptr)->next;
41
0
    }
42
0
  }
43
0
  return log_tree_commit(&log_tree_opt, commit);
44
0
}
45
46
/* Diff two trees. */
47
static int stdin_diff_trees(struct tree *tree1, const char *p)
48
0
{
49
0
  struct object_id oid;
50
0
  struct tree *tree2;
51
0
  if (!isspace(*p++) || parse_oid_hex(p, &oid, &p) || *p)
52
0
    return error("Need exactly two trees, separated by a space");
53
0
  tree2 = lookup_tree(the_repository, &oid);
54
0
  if (!tree2 || parse_tree(tree2))
55
0
    return -1;
56
0
  printf("%s %s\n", oid_to_hex(&tree1->object.oid),
57
0
        oid_to_hex(&tree2->object.oid));
58
0
  diff_tree_oid(&tree1->object.oid, &tree2->object.oid,
59
0
          "", &log_tree_opt.diffopt);
60
0
  log_tree_diff_flush(&log_tree_opt);
61
0
  return 0;
62
0
}
63
64
static int diff_tree_stdin(char *line)
65
0
{
66
0
  int len = strlen(line);
67
0
  struct object_id oid;
68
0
  struct object *obj;
69
0
  const char *p;
70
71
0
  if (!len || line[len-1] != '\n')
72
0
    return -1;
73
0
  line[len-1] = 0;
74
0
  if (parse_oid_hex(line, &oid, &p))
75
0
    return -1;
76
0
  obj = parse_object(the_repository, &oid);
77
0
  if (!obj)
78
0
    return -1;
79
0
  if (obj->type == OBJ_COMMIT)
80
0
    return stdin_diff_commit((struct commit *)obj, p);
81
0
  if (obj->type == OBJ_TREE)
82
0
    return stdin_diff_trees((struct tree *)obj, p);
83
0
  error("Object %s is a %s, not a commit or tree",
84
0
        oid_to_hex(&oid), type_name(obj->type));
85
0
  return -1;
86
0
}
87
88
static const char diff_tree_usage[] =
89
"git diff-tree [--stdin] [-m] [-s] [-v] [--no-commit-id] [--pretty]\n"
90
"              [-t] [-r] [-c | --cc] [--combined-all-paths] [--root] [--merge-base]\n"
91
"              [<common-diff-options>] <tree-ish> [<tree-ish>] [<path>...]\n"
92
"\n"
93
"  -r            diff recursively\n"
94
"  -c            show combined diff for merge commits\n"
95
"  --cc          show combined diff for merge commits removing uninteresting hunks\n"
96
"  --combined-all-paths\n"
97
"                show name of file in all parents for combined diffs\n"
98
"  --root        include the initial commit as diff against /dev/null\n"
99
COMMON_DIFF_OPTIONS_HELP;
100
101
static void diff_tree_tweak_rev(struct rev_info *rev)
102
0
{
103
0
  if (!rev->diffopt.output_format) {
104
0
    if (rev->dense_combined_merges)
105
0
      rev->diffopt.output_format = DIFF_FORMAT_PATCH;
106
0
    else
107
0
      rev->diffopt.output_format = DIFF_FORMAT_RAW;
108
0
  }
109
0
}
110
111
int cmd_diff_tree(int argc, const char **argv, const char *prefix)
112
0
{
113
0
  char line[1000];
114
0
  struct object *tree1, *tree2;
115
0
  static struct rev_info *opt = &log_tree_opt;
116
0
  struct setup_revision_opt s_r_opt;
117
0
  struct userformat_want w;
118
0
  int read_stdin = 0;
119
0
  int merge_base = 0;
120
121
0
  if (argc == 2 && !strcmp(argv[1], "-h"))
122
0
    usage(diff_tree_usage);
123
124
0
  git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
125
126
0
  prepare_repo_settings(the_repository);
127
0
  the_repository->settings.command_requires_full_index = 0;
128
129
0
  repo_init_revisions(the_repository, opt, prefix);
130
0
  if (repo_read_index(the_repository) < 0)
131
0
    die(_("index file corrupt"));
132
0
  opt->abbrev = 0;
133
0
  opt->diff = 1;
134
0
  opt->disable_stdin = 1;
135
0
  memset(&s_r_opt, 0, sizeof(s_r_opt));
136
0
  s_r_opt.tweak = diff_tree_tweak_rev;
137
138
0
  prefix = precompose_argv_prefix(argc, argv, prefix);
139
0
  argc = setup_revisions(argc, argv, opt, &s_r_opt);
140
141
0
  memset(&w, 0, sizeof(w));
142
0
  userformat_find_requirements(NULL, &w);
143
144
0
  if (!opt->show_notes_given && w.notes)
145
0
    opt->show_notes = 1;
146
0
  if (opt->show_notes)
147
0
    load_display_notes(&opt->notes_opt);
148
149
0
  while (--argc > 0) {
150
0
    const char *arg = *++argv;
151
152
0
    if (!strcmp(arg, "--stdin")) {
153
0
      read_stdin = 1;
154
0
      continue;
155
0
    }
156
0
    if (!strcmp(arg, "--merge-base")) {
157
0
      merge_base = 1;
158
0
      continue;
159
0
    }
160
0
    usage(diff_tree_usage);
161
0
  }
162
163
0
  if (read_stdin && merge_base)
164
0
    die(_("options '%s' and '%s' cannot be used together"), "--stdin", "--merge-base");
165
0
  if (merge_base && opt->pending.nr != 2)
166
0
    die(_("--merge-base only works with two commits"));
167
168
0
  opt->diffopt.rotate_to_strict = 1;
169
170
0
  if (opt->remerge_diff) {
171
0
    opt->remerge_objdir = tmp_objdir_create("remerge-diff");
172
0
    if (!opt->remerge_objdir)
173
0
      die(_("unable to create temporary object directory"));
174
0
    tmp_objdir_replace_primary_odb(opt->remerge_objdir, 1);
175
0
  }
176
177
  /*
178
   * NOTE!  We expect "a..b" to expand to "^a b" but it is
179
   * perfectly valid for revision range parser to yield "b ^a",
180
   * which means the same thing. If we get the latter, i.e. the
181
   * second one is marked UNINTERESTING, we recover the original
182
   * order the user gave, i.e. "a..b", by swapping the trees.
183
   */
184
0
  switch (opt->pending.nr) {
185
0
  case 0:
186
0
    if (!read_stdin)
187
0
      usage(diff_tree_usage);
188
0
    break;
189
0
  case 1:
190
0
    tree1 = opt->pending.objects[0].item;
191
0
    diff_tree_commit_oid(&tree1->oid);
192
0
    break;
193
0
  case 2:
194
0
    tree1 = opt->pending.objects[0].item;
195
0
    tree2 = opt->pending.objects[1].item;
196
0
    if (merge_base) {
197
0
      struct object_id oid;
198
199
0
      diff_get_merge_base(opt, &oid);
200
0
      tree1 = lookup_object(the_repository, &oid);
201
0
    } else if (tree2->flags & UNINTERESTING) {
202
0
      SWAP(tree2, tree1);
203
0
    }
204
0
    diff_tree_oid(&tree1->oid, &tree2->oid, "", &opt->diffopt);
205
0
    log_tree_diff_flush(opt);
206
0
    break;
207
0
  }
208
209
0
  if (read_stdin) {
210
0
    int saved_nrl = 0;
211
0
    int saved_dcctc = 0;
212
213
0
    opt->diffopt.rotate_to_strict = 0;
214
0
    opt->diffopt.no_free = 1;
215
0
    if (opt->diffopt.detect_rename) {
216
0
      if (the_repository->index->cache)
217
0
        repo_read_index(the_repository);
218
0
      opt->diffopt.setup |= DIFF_SETUP_USE_SIZE_CACHE;
219
0
    }
220
0
    while (fgets(line, sizeof(line), stdin)) {
221
0
      struct object_id oid;
222
223
0
      if (get_oid_hex(line, &oid)) {
224
0
        fputs(line, stdout);
225
0
        fflush(stdout);
226
0
      }
227
0
      else {
228
0
        diff_tree_stdin(line);
229
0
        if (saved_nrl < opt->diffopt.needed_rename_limit)
230
0
          saved_nrl = opt->diffopt.needed_rename_limit;
231
0
        if (opt->diffopt.degraded_cc_to_c)
232
0
          saved_dcctc = 1;
233
0
      }
234
0
    }
235
0
    opt->diffopt.degraded_cc_to_c = saved_dcctc;
236
0
    opt->diffopt.needed_rename_limit = saved_nrl;
237
0
    opt->diffopt.no_free = 0;
238
0
    diff_free(&opt->diffopt);
239
0
  }
240
241
0
  if (opt->remerge_diff) {
242
0
    tmp_objdir_destroy(opt->remerge_objdir);
243
0
    opt->remerge_objdir = NULL;
244
0
  }
245
246
0
  return diff_result_code(&opt->diffopt);
247
0
}