2 * Copyright (C) the libgit2 contributors. All rights reserved.
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
8 #include "git2/errors.h"
17 int git_refspec__parse(git_refspec
*refspec
, const char *input
, bool is_fetch
)
19 // Ported from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/remote.c#L518-636
23 const char *lhs
, *rhs
;
26 assert(refspec
&& input
);
28 memset(refspec
, 0x0, sizeof(git_refspec
));
29 refspec
->push
= !is_fetch
;
37 rhs
= strrchr(lhs
, ':');
40 * Before going on, special case ":" (or "+:") as a refspec
43 if (!is_fetch
&& rhs
== lhs
&& rhs
[1] == '\0') {
44 refspec
->matching
= 1;
49 size_t rlen
= strlen(++rhs
);
50 is_glob
= (1 <= rlen
&& strchr(rhs
, '*'));
51 refspec
->dst
= git__strndup(rhs
, rlen
);
54 llen
= (rhs
? (size_t)(rhs
- lhs
- 1) : strlen(lhs
));
55 if (1 <= llen
&& memchr(lhs
, '*', llen
)) {
56 if ((rhs
&& !is_glob
) || (!rhs
&& is_fetch
))
59 } else if (rhs
&& is_glob
)
62 refspec
->pattern
= is_glob
;
63 refspec
->src
= git__strndup(lhs
, llen
);
64 flags
= GIT_REF_FORMAT_ALLOW_ONELEVEL
| GIT_REF_FORMAT_REFSPEC_SHORTHAND
65 | (is_glob
? GIT_REF_FORMAT_REFSPEC_PATTERN
: 0);
70 * - empty is allowed; it means HEAD.
71 * - otherwise it must be a valid looking ref.
75 else if (!git_reference__is_valid_name(refspec
->src
, flags
))
79 * - missing is ok, and is same as empty.
80 * - empty is ok; it means not to store.
81 * - otherwise it must be a valid looking ref.
85 else if (!*refspec
->dst
)
87 else if (!git_reference__is_valid_name(refspec
->dst
, flags
))
92 * - empty is allowed; it means delete.
93 * - when wildcarded, it must be a valid looking ref.
94 * - otherwise, it must be an extended SHA-1, but
95 * there is no existing way to validate this.
100 if (!git_reference__is_valid_name(refspec
->src
, flags
))
104 ; /* anything goes, for now */
108 * - missing is allowed, but LHS then must be a
110 * - empty is not allowed.
111 * - otherwise it must be a valid looking ref.
114 if (!git_reference__is_valid_name(refspec
->src
, flags
))
116 } else if (!*refspec
->dst
) {
119 if (!git_reference__is_valid_name(refspec
->dst
, flags
))
124 refspec
->string
= git__strdup(input
);
125 GITERR_CHECK_ALLOC(refspec
->string
);
133 void git_refspec__free(git_refspec
*refspec
)
138 git__free(refspec
->src
);
139 git__free(refspec
->dst
);
140 git__free(refspec
->string
);
143 const char *git_refspec_src(const git_refspec
*refspec
)
145 return refspec
== NULL
? NULL
: refspec
->src
;
148 const char *git_refspec_dst(const git_refspec
*refspec
)
150 return refspec
== NULL
? NULL
: refspec
->dst
;
153 const char *git_refspec_string(const git_refspec
*refspec
)
155 return refspec
== NULL
? NULL
: refspec
->string
;
158 int git_refspec_force(const git_refspec
*refspec
)
162 return refspec
->force
;
165 int git_refspec_src_matches(const git_refspec
*refspec
, const char *refname
)
167 if (refspec
== NULL
|| refspec
->src
== NULL
)
170 return (p_fnmatch(refspec
->src
, refname
, 0) == 0);
173 int git_refspec_dst_matches(const git_refspec
*refspec
, const char *refname
)
175 if (refspec
== NULL
|| refspec
->dst
== NULL
)
178 return (p_fnmatch(refspec
->dst
, refname
, 0) == 0);
181 static int refspec_transform(
182 git_buf
*out
, const char *from
, const char *to
, const char *name
)
184 size_t to_len
= to
? strlen(to
) : 0;
185 size_t from_len
= from
? strlen(from
) : 0;
186 size_t name_len
= name
? strlen(name
) : 0;
188 git_buf_sanitize(out
);
190 if (git_buf_set(out
, to
, to_len
) < 0)
194 /* No '*' at the end of 'to' means that refspec is mapped to one
195 * specific branch, so no actual transformation is needed.
197 if (out
->ptr
[to_len
- 1] != '*')
199 git_buf_shorten(out
, 1); /* remove trailing '*' copied from 'to' */
202 if (from_len
> 0) /* ignore trailing '*' from 'from' */
204 if (from_len
> name_len
)
207 return git_buf_put(out
, name
+ from_len
, name_len
- from_len
);
210 int git_refspec_transform(git_buf
*out
, const git_refspec
*spec
, const char *name
)
212 return refspec_transform(out
, spec
->src
, spec
->dst
, name
);
215 int git_refspec_rtransform(git_buf
*out
, const git_refspec
*spec
, const char *name
)
217 return refspec_transform(out
, spec
->dst
, spec
->src
, name
);
220 int git_refspec__serialize(git_buf
*out
, const git_refspec
*refspec
)
223 git_buf_putc(out
, '+');
225 git_buf_printf(out
, "%s:%s",
226 refspec
->src
!= NULL
? refspec
->src
: "",
227 refspec
->dst
!= NULL
? refspec
->dst
: "");
229 return git_buf_oom(out
) == false;
232 int git_refspec_is_wildcard(const git_refspec
*spec
)
234 assert(spec
&& spec
->src
);
236 return (spec
->src
[strlen(spec
->src
) - 1] == '*');
239 git_direction
git_refspec_direction(const git_refspec
*spec
)
246 int git_refspec__dwim_one(git_vector
*out
, git_refspec
*spec
, git_vector
*refs
)
248 git_buf buf
= GIT_BUF_INIT
;
252 const char* formatters
[] = {
254 GIT_REFS_TAGS_DIR
"%s",
255 GIT_REFS_HEADS_DIR
"%s",
259 git_refspec
*cur
= git__calloc(1, sizeof(git_refspec
));
260 GITERR_CHECK_ALLOC(cur
);
262 cur
->force
= spec
->force
;
263 cur
->push
= spec
->push
;
264 cur
->pattern
= spec
->pattern
;
265 cur
->matching
= spec
->matching
;
266 cur
->string
= git__strdup(spec
->string
);
268 /* shorthand on the lhs */
269 if (git__prefixcmp(spec
->src
, GIT_REFS_DIR
)) {
270 for (j
= 0; formatters
[j
]; j
++) {
272 if (git_buf_printf(&buf
, formatters
[j
], spec
->src
) < 0)
275 key
.name
= (char *) git_buf_cstr(&buf
);
276 if (!git_vector_search(&pos
, refs
, &key
)) {
277 /* we found something to match the shorthand, set src to that */
278 cur
->src
= git_buf_detach(&buf
);
283 /* No shorthands found, copy over the name */
284 if (cur
->src
== NULL
&& spec
->src
!= NULL
) {
285 cur
->src
= git__strdup(spec
->src
);
286 GITERR_CHECK_ALLOC(cur
->src
);
289 if (spec
->dst
&& git__prefixcmp(spec
->dst
, GIT_REFS_DIR
)) {
290 /* if it starts with "remotes" then we just prepend "refs/" */
291 if (!git__prefixcmp(spec
->dst
, "remotes/")) {
292 git_buf_puts(&buf
, GIT_REFS_DIR
);
294 git_buf_puts(&buf
, GIT_REFS_HEADS_DIR
);
297 if (git_buf_puts(&buf
, spec
->dst
) < 0)
300 cur
->dst
= git_buf_detach(&buf
);
305 if (cur
->dst
== NULL
&& spec
->dst
!= NULL
) {
306 cur
->dst
= git__strdup(spec
->dst
);
307 GITERR_CHECK_ALLOC(cur
->dst
);
310 return git_vector_insert(out
, cur
);