Coverage Report

Created: 2026-03-31 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/patch-ids.c
Line
Count
Source
1
#include "git-compat-util.h"
2
#include "diff.h"
3
#include "commit.h"
4
#include "hash.h"
5
#include "hex.h"
6
#include "patch-ids.h"
7
8
static int patch_id_defined(struct commit *commit)
9
0
{
10
  /* must be 0 or 1 parents */
11
0
  return !commit->parents || !commit->parents->next;
12
0
}
13
14
int commit_patch_id(struct commit *commit, struct diff_options *options,
15
        struct object_id *oid, int diff_header_only)
16
0
{
17
0
  if (!patch_id_defined(commit))
18
0
    return -1;
19
20
0
  if (commit->parents)
21
0
    diff_tree_oid(&commit->parents->item->object.oid,
22
0
            &commit->object.oid, "", options);
23
0
  else
24
0
    diff_root_tree_oid(&commit->object.oid, "", options);
25
0
  diffcore_std(options);
26
0
  return diff_flush_patch_id(options, oid, diff_header_only);
27
0
}
28
29
/*
30
 * When we cannot load the full patch-id for both commits for whatever
31
 * reason, the function returns -1 (i.e. return error(...)). Despite
32
 * the "neq" in the name of this function, the caller only cares about
33
 * the return value being zero (a and b are equivalent) or non-zero (a
34
 * and b are different), and returning non-zero would keep both in the
35
 * result, even if they actually were equivalent, in order to err on
36
 * the side of safety.  The actual value being negative does not have
37
 * any significance; only that it is non-zero matters.
38
 */
39
static int patch_id_neq(const void *cmpfn_data,
40
      const struct hashmap_entry *eptr,
41
      const struct hashmap_entry *entry_or_key,
42
      const void *keydata UNUSED)
43
0
{
44
  /*
45
   * We drop the 'const' modifier here intentionally.
46
   *
47
   * Even though eptr and entry_or_key are const, we want to
48
   * lazily compute their .patch_id members; see b3dfeebb (rebase:
49
   * avoid computing unnecessary patch IDs, 2016-07-29). So we cast
50
   * the constness away with container_of().
51
   */
52
0
  struct diff_options *opt = (void *)cmpfn_data;
53
0
  struct patch_id *a, *b;
54
55
0
  a = container_of(eptr, struct patch_id, ent);
56
0
  b = container_of(entry_or_key, struct patch_id, ent);
57
58
0
  if (is_null_oid(&a->patch_id) &&
59
0
      commit_patch_id(a->commit, opt, &a->patch_id, 0))
60
0
    return error("Could not get patch ID for %s",
61
0
      oid_to_hex(&a->commit->object.oid));
62
0
  if (is_null_oid(&b->patch_id) &&
63
0
      commit_patch_id(b->commit, opt, &b->patch_id, 0))
64
0
    return error("Could not get patch ID for %s",
65
0
      oid_to_hex(&b->commit->object.oid));
66
0
  return !oideq(&a->patch_id, &b->patch_id);
67
0
}
68
69
int init_patch_ids(struct repository *r, struct patch_ids *ids)
70
0
{
71
0
  memset(ids, 0, sizeof(*ids));
72
0
  repo_diff_setup(r, &ids->diffopts);
73
0
  ids->diffopts.detect_rename = 0;
74
0
  ids->diffopts.flags.recursive = 1;
75
0
  diff_setup_done(&ids->diffopts);
76
0
  hashmap_init(&ids->patches, patch_id_neq, &ids->diffopts, 256);
77
0
  return 0;
78
0
}
79
80
int free_patch_ids(struct patch_ids *ids)
81
0
{
82
0
  hashmap_clear_and_free(&ids->patches, struct patch_id, ent);
83
0
  return 0;
84
0
}
85
86
static int init_patch_id_entry(struct patch_id *patch,
87
             struct commit *commit,
88
             struct patch_ids *ids)
89
0
{
90
0
  struct object_id header_only_patch_id;
91
92
0
  patch->commit = commit;
93
0
  if (commit_patch_id(commit, &ids->diffopts, &header_only_patch_id, 1))
94
0
    return -1;
95
96
0
  hashmap_entry_init(&patch->ent, oidhash(&header_only_patch_id));
97
0
  return 0;
98
0
}
99
100
struct patch_id *patch_id_iter_first(struct commit *commit,
101
             struct patch_ids *ids)
102
0
{
103
0
  struct patch_id patch;
104
105
0
  if (!patch_id_defined(commit))
106
0
    return NULL;
107
108
0
  memset(&patch, 0, sizeof(patch));
109
0
  if (init_patch_id_entry(&patch, commit, ids))
110
0
    return NULL;
111
112
0
  return hashmap_get_entry(&ids->patches, &patch, ent, NULL);
113
0
}
114
115
struct patch_id *patch_id_iter_next(struct patch_id *cur,
116
            struct patch_ids *ids)
117
0
{
118
0
  return hashmap_get_next_entry(&ids->patches, cur, ent);
119
0
}
120
121
int has_commit_patch_id(struct commit *commit,
122
      struct patch_ids *ids)
123
0
{
124
0
  return !!patch_id_iter_first(commit, ids);
125
0
}
126
127
struct patch_id *add_commit_patch_id(struct commit *commit,
128
             struct patch_ids *ids)
129
0
{
130
0
  struct patch_id *key;
131
132
0
  if (!patch_id_defined(commit))
133
0
    return NULL;
134
135
0
  CALLOC_ARRAY(key, 1);
136
0
  if (init_patch_id_entry(key, commit, ids)) {
137
0
    free(key);
138
0
    return NULL;
139
0
  }
140
141
0
  hashmap_add(&ids->patches, &key->ent);
142
0
  return key;
143
0
}