Skip to content

Commit fe17a25

Browse files
peffgitster
authored andcommitted
refspec: store raw refspecs inside refspec_item
The refspec struct keeps two matched arrays: one for the refspec_item structs and one for the original raw refspec strings. The main reason for this is that there are other users of refspec_item that do not care about the raw strings. But it does make managing the refspec struct awkward, as we must keep the two arrays in sync. This has led to bugs in the past (both leaks and double-frees). Let's just store a copy of the raw refspec string directly in each refspec_item struct. This simplifies the handling at a small cost: 1. Direct callers of refspec_item_init() will now get an extra copy of the refspec string, even if they don't need it. This should be negligible, as the struct is already allocating two strings for the parsed src/dst values (and we tend to only do it sparingly anyway for things like the TAG_REFSPEC literal). 2. Users of refspec_appendf() will now generate a temporary string, copy it, and then free the result (versus handing off ownership of the temporary string). We could get around this by having a "nodup" variant of refspec_item_init(), but it doesn't seem worth the extra complexity for something that is not remotely a hot code path. Code which accesses refspec->raw now needs to look at refspec->item.raw. Other callers which just use refspec_item directly can remain the same. We'll free the allocated string in refspec_item_clear(), which they should be calling anyway to free src/dst. One subtle note: refspec_item_init() can return an error, in which case we'll still have set its "raw" field. But that is also true of the "src" and "dst" fields, so any caller which does not _clear() the failed item is already potentially leaking. In practice most code just calls die() on an error anyway, but you can see the exception in valid_fetch_refspec(), which does correctly call _clear() even on error. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent d36af33 commit fe17a25

File tree

5 files changed

+19
-31
lines changed

5 files changed

+19
-31
lines changed

builtin/fetch.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -454,14 +454,10 @@ static void filter_prefetch_refspec(struct refspec *rs)
454454
ref_namespace[NAMESPACE_TAGS].ref))) {
455455
int j;
456456

457-
free(rs->items[i].src);
458-
free(rs->items[i].dst);
459-
free(rs->raw[i]);
457+
refspec_item_clear(&rs->items[i]);
460458

461-
for (j = i + 1; j < rs->nr; j++) {
459+
for (j = i + 1; j < rs->nr; j++)
462460
rs->items[j - 1] = rs->items[j];
463-
rs->raw[j - 1] = rs->raw[j];
464-
}
465461
rs->nr--;
466462
i--;
467463
continue;

builtin/remote.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
377377
for (i = 0; i < states->remote->fetch.nr; i++)
378378
if (get_fetch_map(remote_refs, &states->remote->fetch.items[i], &tail, 1))
379379
die(_("Could not get fetch map for refspec %s"),
380-
states->remote->fetch.raw[i]);
380+
states->remote->fetch.items[i].raw);
381381

382382
for (ref = fetch_map; ref; ref = ref->next) {
383383
if (omit_name_by_refspec(ref->name, &states->remote->fetch))
@@ -634,11 +634,11 @@ static int migrate_file(struct remote *remote)
634634
strbuf_reset(&buf);
635635
strbuf_addf(&buf, "remote.%s.push", remote->name);
636636
for (i = 0; i < remote->push.nr; i++)
637-
git_config_set_multivar(buf.buf, remote->push.raw[i], "^$", 0);
637+
git_config_set_multivar(buf.buf, remote->push.items[i].raw, "^$", 0);
638638
strbuf_reset(&buf);
639639
strbuf_addf(&buf, "remote.%s.fetch", remote->name);
640640
for (i = 0; i < remote->fetch.nr; i++)
641-
git_config_set_multivar(buf.buf, remote->fetch.raw[i], "^$", 0);
641+
git_config_set_multivar(buf.buf, remote->fetch.items[i].raw, "^$", 0);
642642
if (remote->origin == REMOTE_REMOTES)
643643
unlink_or_warn(git_path("remotes/%s", remote->name));
644644
else if (remote->origin == REMOTE_BRANCHES)
@@ -768,7 +768,7 @@ static int mv(int argc, const char **argv, const char *prefix)
768768
char *ptr;
769769

770770
strbuf_reset(&buf2);
771-
strbuf_addstr(&buf2, oldremote->fetch.raw[i]);
771+
strbuf_addstr(&buf2, oldremote->fetch.items[i].raw);
772772
ptr = strstr(buf2.buf, old_remote_context.buf);
773773
if (ptr) {
774774
refspec_updated = 1;

refspec.c

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ static int parse_refspec(struct refspec_item *item, const char *refspec, int fet
153153
int refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)
154154
{
155155
memset(item, 0, sizeof(*item));
156+
item->raw = xstrdup(refspec);
156157
return parse_refspec(item, refspec, fetch);
157158
}
158159

@@ -167,6 +168,7 @@ void refspec_item_clear(struct refspec_item *item)
167168
{
168169
FREE_AND_NULL(item->src);
169170
FREE_AND_NULL(item->dst);
171+
FREE_AND_NULL(item->raw);
170172
item->force = 0;
171173
item->pattern = 0;
172174
item->matching = 0;
@@ -179,7 +181,7 @@ void refspec_init(struct refspec *rs, int fetch)
179181
rs->fetch = fetch;
180182
}
181183

182-
static void refspec_append_nodup(struct refspec *rs, char *refspec)
184+
void refspec_append(struct refspec *rs, const char *refspec)
183185
{
184186
struct refspec_item item;
185187

@@ -188,24 +190,20 @@ static void refspec_append_nodup(struct refspec *rs, char *refspec)
188190
ALLOC_GROW(rs->items, rs->nr + 1, rs->alloc);
189191
rs->items[rs->nr] = item;
190192

191-
ALLOC_GROW(rs->raw, rs->nr + 1, rs->raw_alloc);
192-
rs->raw[rs->nr] = refspec;
193-
194193
rs->nr++;
195194
}
196195

197-
void refspec_append(struct refspec *rs, const char *refspec)
198-
{
199-
refspec_append_nodup(rs, xstrdup(refspec));
200-
}
201-
202196
void refspec_appendf(struct refspec *rs, const char *fmt, ...)
203197
{
204198
va_list ap;
199+
char *buf;
205200

206201
va_start(ap, fmt);
207-
refspec_append_nodup(rs, xstrvfmt(fmt, ap));
202+
buf = xstrvfmt(fmt, ap);
208203
va_end(ap);
204+
205+
refspec_append(rs, buf);
206+
free(buf);
209207
}
210208

211209
void refspec_appendn(struct refspec *rs, const char **refspecs, int nr)
@@ -219,18 +217,13 @@ void refspec_clear(struct refspec *rs)
219217
{
220218
int i;
221219

222-
for (i = 0; i < rs->nr; i++) {
220+
for (i = 0; i < rs->nr; i++)
223221
refspec_item_clear(&rs->items[i]);
224-
free(rs->raw[i]);
225-
}
226222

227223
FREE_AND_NULL(rs->items);
228224
rs->alloc = 0;
229225
rs->nr = 0;
230226

231-
FREE_AND_NULL(rs->raw);
232-
rs->raw_alloc = 0;
233-
234227
rs->fetch = 0;
235228
}
236229

refspec.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ struct refspec_item {
2626

2727
char *src;
2828
char *dst;
29+
30+
char *raw;
2931
};
3032

3133
#define REFSPEC_FETCH 1
@@ -43,9 +45,6 @@ struct refspec {
4345
int alloc;
4446
int nr;
4547

46-
char **raw;
47-
int raw_alloc;
48-
4948
int fetch;
5049
};
5150

submodule.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,7 +1175,7 @@ static int push_submodule(const char *path,
11751175
int i;
11761176
strvec_push(&cp.args, remote->name);
11771177
for (i = 0; i < rs->nr; i++)
1178-
strvec_push(&cp.args, rs->raw[i]);
1178+
strvec_push(&cp.args, rs->items[i].raw);
11791179
}
11801180

11811181
prepare_submodule_repo_env(&cp.env);
@@ -1210,7 +1210,7 @@ static void submodule_push_check(const char *path, const char *head,
12101210
strvec_push(&cp.args, remote->name);
12111211

12121212
for (i = 0; i < rs->nr; i++)
1213-
strvec_push(&cp.args, rs->raw[i]);
1213+
strvec_push(&cp.args, rs->items[i].raw);
12141214

12151215
prepare_submodule_repo_env(&cp.env);
12161216
cp.git_cmd = 1;

0 commit comments

Comments
 (0)