Coverage Report

Created: 2026-01-10 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/resolve-undo.c
Line
Count
Source
1
#define DISABLE_SIGN_COMPARE_WARNINGS
2
3
#include "git-compat-util.h"
4
#include "dir.h"
5
#include "hash.h"
6
#include "read-cache.h"
7
#include "resolve-undo.h"
8
#include "sparse-index.h"
9
#include "string-list.h"
10
11
/* The only error case is to run out of memory in string-list */
12
void record_resolve_undo(struct index_state *istate, struct cache_entry *ce)
13
0
{
14
0
  struct string_list_item *lost;
15
0
  struct resolve_undo_info *ui;
16
0
  struct string_list *resolve_undo;
17
0
  int stage = ce_stage(ce);
18
19
0
  if (!stage)
20
0
    return;
21
22
0
  if (!istate->resolve_undo) {
23
0
    CALLOC_ARRAY(resolve_undo, 1);
24
0
    resolve_undo->strdup_strings = 1;
25
0
    istate->resolve_undo = resolve_undo;
26
0
  }
27
0
  resolve_undo = istate->resolve_undo;
28
0
  lost = string_list_insert(resolve_undo, ce->name);
29
0
  if (!lost->util)
30
0
    lost->util = xcalloc(1, sizeof(*ui));
31
0
  ui = lost->util;
32
0
  oidcpy(&ui->oid[stage - 1], &ce->oid);
33
0
  ui->mode[stage - 1] = ce->ce_mode;
34
0
}
35
36
void resolve_undo_write(struct strbuf *sb, struct string_list *resolve_undo,
37
      const struct git_hash_algo *algop)
38
0
{
39
0
  struct string_list_item *item;
40
0
  for_each_string_list_item(item, resolve_undo) {
41
0
    struct resolve_undo_info *ui = item->util;
42
0
    int i;
43
44
0
    if (!ui)
45
0
      continue;
46
0
    strbuf_addstr(sb, item->string);
47
0
    strbuf_addch(sb, 0);
48
0
    for (i = 0; i < 3; i++)
49
0
      strbuf_addf(sb, "%o%c", ui->mode[i], 0);
50
0
    for (i = 0; i < 3; i++) {
51
0
      if (!ui->mode[i])
52
0
        continue;
53
0
      strbuf_add(sb, ui->oid[i].hash, algop->rawsz);
54
0
    }
55
0
  }
56
0
}
57
58
struct string_list *resolve_undo_read(const char *data, unsigned long size,
59
              const struct git_hash_algo *algop)
60
0
{
61
0
  struct string_list *resolve_undo;
62
0
  size_t len;
63
0
  char *endptr;
64
0
  int i;
65
0
  const unsigned rawsz = algop->rawsz;
66
67
0
  CALLOC_ARRAY(resolve_undo, 1);
68
0
  resolve_undo->strdup_strings = 1;
69
70
0
  while (size) {
71
0
    struct string_list_item *lost;
72
0
    struct resolve_undo_info *ui;
73
74
0
    len = strlen(data) + 1;
75
0
    if (size <= len)
76
0
      goto error;
77
0
    lost = string_list_insert(resolve_undo, data);
78
0
    if (!lost->util)
79
0
      lost->util = xcalloc(1, sizeof(*ui));
80
0
    ui = lost->util;
81
0
    size -= len;
82
0
    data += len;
83
84
0
    for (i = 0; i < 3; i++) {
85
0
      ui->mode[i] = strtoul(data, &endptr, 8);
86
0
      if (!endptr || endptr == data || *endptr)
87
0
        goto error;
88
0
      len = (endptr + 1) - (char*)data;
89
0
      if (size <= len)
90
0
        goto error;
91
0
      size -= len;
92
0
      data += len;
93
0
    }
94
95
0
    for (i = 0; i < 3; i++) {
96
0
      if (!ui->mode[i])
97
0
        continue;
98
0
      if (size < rawsz)
99
0
        goto error;
100
0
      oidread(&ui->oid[i], (const unsigned char *)data, algop);
101
0
      size -= rawsz;
102
0
      data += rawsz;
103
0
    }
104
0
  }
105
0
  return resolve_undo;
106
107
0
error:
108
0
  string_list_clear(resolve_undo, 1);
109
0
  error("Index records invalid resolve-undo information");
110
0
  return NULL;
111
0
}
112
113
void resolve_undo_clear_index(struct index_state *istate)
114
60
{
115
60
  struct string_list *resolve_undo = istate->resolve_undo;
116
60
  if (!resolve_undo)
117
60
    return;
118
0
  string_list_clear(resolve_undo, 1);
119
0
  free(resolve_undo);
120
0
  istate->resolve_undo = NULL;
121
0
  istate->cache_changed |= RESOLVE_UNDO_CHANGED;
122
0
}
123
124
int unmerge_index_entry(struct index_state *istate, const char *path,
125
      struct resolve_undo_info *ru, unsigned ce_flags)
126
0
{
127
0
  int i = index_name_pos(istate, path, strlen(path));
128
129
0
  if (i < 0) {
130
    /* unmerged? */
131
0
    i = -i - 1;
132
0
    if (i < istate->cache_nr &&
133
0
        !strcmp(istate->cache[i]->name, path))
134
      /* yes, it is already unmerged */
135
0
      return 0;
136
    /* fallthru: resolved to removal */
137
0
  } else {
138
    /* merged - remove it to replace it with unmerged entries */
139
0
    remove_index_entry_at(istate, i);
140
0
  }
141
142
0
  for (i = 0; i < 3; i++) {
143
0
    struct cache_entry *ce;
144
0
    if (!ru->mode[i])
145
0
      continue;
146
0
    ce = make_cache_entry(istate, ru->mode[i], &ru->oid[i],
147
0
              path, i + 1, 0);
148
0
    ce->ce_flags |= ce_flags;
149
0
    if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD))
150
0
      return error("cannot unmerge '%s'", path);
151
0
  }
152
0
  return 0;
153
0
}
154
155
void unmerge_index(struct index_state *istate, const struct pathspec *pathspec,
156
       unsigned ce_flags)
157
0
{
158
0
  struct string_list_item *item;
159
160
0
  if (!istate->resolve_undo)
161
0
    return;
162
163
  /* TODO: audit for interaction with sparse-index. */
164
0
  ensure_full_index(istate);
165
166
0
  for_each_string_list_item(item, istate->resolve_undo) {
167
0
    const char *path = item->string;
168
0
    struct resolve_undo_info *ru = item->util;
169
0
    if (!item->util)
170
0
      continue;
171
0
    if (!match_pathspec(istate, pathspec,
172
0
            item->string, strlen(item->string),
173
0
            0, NULL, 0))
174
0
      continue;
175
0
    unmerge_index_entry(istate, path, ru, ce_flags);
176
0
    free(ru);
177
    item->util = NULL;
178
0
  }
179
0
}