2015-11-09 13:34:01 +00:00
|
|
|
/*
|
|
|
|
* The backend-independent part of the reference module.
|
|
|
|
*/
|
|
|
|
|
2005-06-06 20:31:29 +00:00
|
|
|
#include "cache.h"
|
2014-10-01 10:28:42 +00:00
|
|
|
#include "lockfile.h"
|
2006-12-19 22:34:12 +00:00
|
|
|
#include "refs.h"
|
2015-11-10 11:42:36 +00:00
|
|
|
#include "refs/refs-internal.h"
|
2006-11-19 21:22:44 +00:00
|
|
|
#include "object.h"
|
|
|
|
#include "tag.h"
|
2014-12-12 08:57:02 +00:00
|
|
|
|
2012-04-10 05:30:13 +00:00
|
|
|
/*
|
2014-06-04 03:38:10 +00:00
|
|
|
* How to handle various characters in refnames:
|
|
|
|
* 0: An acceptable character for refs
|
2014-07-28 17:41:53 +00:00
|
|
|
* 1: End-of-component
|
|
|
|
* 2: ., look for a preceding . to reject .. in refs
|
|
|
|
* 3: {, look for a preceding @ to reject @{ in refs
|
2015-07-22 21:05:32 +00:00
|
|
|
* 4: A bad character: ASCII control characters, and
|
2015-07-22 21:05:33 +00:00
|
|
|
* ":", "?", "[", "\", "^", "~", SP, or TAB
|
|
|
|
* 5: *, reject unless REFNAME_REFSPEC_PATTERN is set
|
2014-06-04 03:38:10 +00:00
|
|
|
*/
|
|
|
|
static unsigned char refname_disposition[256] = {
|
2014-07-28 17:41:53 +00:00
|
|
|
1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
|
|
|
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
2015-07-22 21:05:33 +00:00
|
|
|
4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 2, 1,
|
2014-07-28 17:41:53 +00:00
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4,
|
2014-06-04 03:38:10 +00:00
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
2014-07-28 17:41:53 +00:00
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 4, 4
|
2014-06-04 03:38:10 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to read one refname component from the front of refname.
|
|
|
|
* Return the length of the component found, or -1 if the component is
|
|
|
|
* not legal. It is legal if it is something reasonable to have under
|
|
|
|
* ".git/refs/"; We do not like it if:
|
2012-04-10 05:30:13 +00:00
|
|
|
*
|
|
|
|
* - any path component of it begins with ".", or
|
|
|
|
* - it has double dots "..", or
|
2015-07-22 21:05:32 +00:00
|
|
|
* - it has ASCII control characters, or
|
2015-07-22 21:05:33 +00:00
|
|
|
* - it has ":", "?", "[", "\", "^", "~", SP, or TAB anywhere, or
|
|
|
|
* - it has "*" anywhere unless REFNAME_REFSPEC_PATTERN is set, or
|
2015-07-22 21:05:32 +00:00
|
|
|
* - it ends with a "/", or
|
|
|
|
* - it ends with ".lock", or
|
|
|
|
* - it contains a "@{" portion
|
2012-04-10 05:30:13 +00:00
|
|
|
*/
|
2015-07-22 21:05:33 +00:00
|
|
|
static int check_refname_component(const char *refname, int *flags)
|
2012-04-10 05:30:13 +00:00
|
|
|
{
|
|
|
|
const char *cp;
|
|
|
|
char last = '\0';
|
|
|
|
|
|
|
|
for (cp = refname; ; cp++) {
|
2014-06-04 03:38:10 +00:00
|
|
|
int ch = *cp & 255;
|
|
|
|
unsigned char disp = refname_disposition[ch];
|
|
|
|
switch (disp) {
|
2014-07-28 17:41:53 +00:00
|
|
|
case 1:
|
2014-06-04 03:38:10 +00:00
|
|
|
goto out;
|
2014-07-28 17:41:53 +00:00
|
|
|
case 2:
|
2014-06-04 03:38:10 +00:00
|
|
|
if (last == '.')
|
|
|
|
return -1; /* Refname contains "..". */
|
|
|
|
break;
|
2014-07-28 17:41:53 +00:00
|
|
|
case 3:
|
2014-06-04 03:38:10 +00:00
|
|
|
if (last == '@')
|
|
|
|
return -1; /* Refname contains "@{". */
|
2012-04-10 05:30:13 +00:00
|
|
|
break;
|
2014-07-28 17:41:53 +00:00
|
|
|
case 4:
|
2014-06-04 03:38:10 +00:00
|
|
|
return -1;
|
2015-07-22 21:05:33 +00:00
|
|
|
case 5:
|
|
|
|
if (!(*flags & REFNAME_REFSPEC_PATTERN))
|
|
|
|
return -1; /* refspec can't be a pattern */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unset the pattern flag so that we only accept
|
|
|
|
* a single asterisk for one side of refspec.
|
|
|
|
*/
|
|
|
|
*flags &= ~ REFNAME_REFSPEC_PATTERN;
|
|
|
|
break;
|
2014-06-04 03:38:10 +00:00
|
|
|
}
|
2012-04-10 05:30:13 +00:00
|
|
|
last = ch;
|
|
|
|
}
|
2014-06-04 03:38:10 +00:00
|
|
|
out:
|
2012-04-10 05:30:13 +00:00
|
|
|
if (cp == refname)
|
2012-04-10 05:30:22 +00:00
|
|
|
return 0; /* Component has zero length. */
|
2014-09-26 19:22:22 +00:00
|
|
|
if (refname[0] == '.')
|
|
|
|
return -1; /* Component starts with '.'. */
|
2014-10-01 10:28:15 +00:00
|
|
|
if (cp - refname >= LOCK_SUFFIX_LEN &&
|
|
|
|
!memcmp(cp - LOCK_SUFFIX_LEN, LOCK_SUFFIX, LOCK_SUFFIX_LEN))
|
2012-04-10 05:30:13 +00:00
|
|
|
return -1; /* Refname ends with ".lock". */
|
|
|
|
return cp - refname;
|
|
|
|
}
|
|
|
|
|
2014-07-28 17:41:53 +00:00
|
|
|
int check_refname_format(const char *refname, int flags)
|
2012-04-10 05:30:13 +00:00
|
|
|
{
|
|
|
|
int component_len, component_count = 0;
|
|
|
|
|
Add new @ shortcut for HEAD
Typing 'HEAD' is tedious, especially when we can use '@' instead.
The reason for choosing '@' is that it follows naturally from the
ref@op syntax (e.g. HEAD@{u}), except we have no ref, and no
operation, and when we don't have those, it makes sens to assume
'HEAD'.
So now we can use 'git show @~1', and all that goody goodness.
Until now '@' was a valid name, but it conflicts with this idea, so
let's make it invalid. Probably very few people, if any, used this name.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-09-02 06:34:30 +00:00
|
|
|
if (!strcmp(refname, "@"))
|
|
|
|
/* Refname is a single character '@'. */
|
|
|
|
return -1;
|
|
|
|
|
2012-04-10 05:30:13 +00:00
|
|
|
while (1) {
|
|
|
|
/* We are at the start of a path component. */
|
2015-07-22 21:05:33 +00:00
|
|
|
component_len = check_refname_component(refname, &flags);
|
|
|
|
if (component_len <= 0)
|
|
|
|
return -1;
|
|
|
|
|
2012-04-10 05:30:13 +00:00
|
|
|
component_count++;
|
|
|
|
if (refname[component_len] == '\0')
|
|
|
|
break;
|
|
|
|
/* Skip to next component. */
|
|
|
|
refname += component_len + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (refname[component_len - 1] == '.')
|
|
|
|
return -1; /* Refname ends with '.'. */
|
|
|
|
if (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2)
|
|
|
|
return -1; /* Refname has only one component. */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-11-10 11:42:36 +00:00
|
|
|
int refname_is_safe(const char *refname)
|
refs.c: allow listing and deleting badly named refs
We currently do not handle badly named refs well:
$ cp .git/refs/heads/master .git/refs/heads/master.....@\*@\\.
$ git branch
fatal: Reference has invalid format: 'refs/heads/master.....@*@\.'
$ git branch -D master.....@\*@\\.
error: branch 'master.....@*@\.' not found.
Users cannot recover from a badly named ref without manually finding
and deleting the loose ref file or appropriate line in packed-refs.
Making that easier will make it easier to tweak the ref naming rules
in the future, for example to forbid shell metacharacters like '`'
and '"', without putting people in a state that is hard to get out of.
So allow "branch --list" to show these refs and allow "branch -d/-D"
and "update-ref -d" to delete them. Other commands (for example to
rename refs) will continue to not handle these refs but can be changed
in later patches.
Details:
In resolving functions, refuse to resolve refs that don't pass the
git-check-ref-format(1) check unless the new RESOLVE_REF_ALLOW_BAD_NAME
flag is passed. Even with RESOLVE_REF_ALLOW_BAD_NAME, refuse to
resolve refs that escape the refs/ directory and do not match the
pattern [A-Z_]* (think "HEAD" and "MERGE_HEAD").
In locking functions, refuse to act on badly named refs unless they
are being deleted and either are in the refs/ directory or match [A-Z_]*.
Just like other invalid refs, flag resolved, badly named refs with the
REF_ISBROKEN flag, treat them as resolving to null_sha1, and skip them
in all iteration functions except for for_each_rawref.
Flag badly named refs (but not symrefs pointing to badly named refs)
with a REF_BAD_NAME flag to make it easier for future callers to
notice and handle them specially. For example, in a later patch
for-each-ref will use this flag to detect refs whose names can confuse
callers parsing for-each-ref output.
In the transaction API, refuse to create or update badly named refs,
but allow deleting them (unless they try to escape refs/ and don't match
[A-Z_]*).
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-09-03 18:45:43 +00:00
|
|
|
{
|
|
|
|
if (starts_with(refname, "refs/")) {
|
|
|
|
char *buf;
|
|
|
|
int result;
|
|
|
|
|
2016-02-22 22:44:28 +00:00
|
|
|
buf = xmallocz(strlen(refname));
|
refs.c: allow listing and deleting badly named refs
We currently do not handle badly named refs well:
$ cp .git/refs/heads/master .git/refs/heads/master.....@\*@\\.
$ git branch
fatal: Reference has invalid format: 'refs/heads/master.....@*@\.'
$ git branch -D master.....@\*@\\.
error: branch 'master.....@*@\.' not found.
Users cannot recover from a badly named ref without manually finding
and deleting the loose ref file or appropriate line in packed-refs.
Making that easier will make it easier to tweak the ref naming rules
in the future, for example to forbid shell metacharacters like '`'
and '"', without putting people in a state that is hard to get out of.
So allow "branch --list" to show these refs and allow "branch -d/-D"
and "update-ref -d" to delete them. Other commands (for example to
rename refs) will continue to not handle these refs but can be changed
in later patches.
Details:
In resolving functions, refuse to resolve refs that don't pass the
git-check-ref-format(1) check unless the new RESOLVE_REF_ALLOW_BAD_NAME
flag is passed. Even with RESOLVE_REF_ALLOW_BAD_NAME, refuse to
resolve refs that escape the refs/ directory and do not match the
pattern [A-Z_]* (think "HEAD" and "MERGE_HEAD").
In locking functions, refuse to act on badly named refs unless they
are being deleted and either are in the refs/ directory or match [A-Z_]*.
Just like other invalid refs, flag resolved, badly named refs with the
REF_ISBROKEN flag, treat them as resolving to null_sha1, and skip them
in all iteration functions except for for_each_rawref.
Flag badly named refs (but not symrefs pointing to badly named refs)
with a REF_BAD_NAME flag to make it easier for future callers to
notice and handle them specially. For example, in a later patch
for-each-ref will use this flag to detect refs whose names can confuse
callers parsing for-each-ref output.
In the transaction API, refuse to create or update badly named refs,
but allow deleting them (unless they try to escape refs/ and don't match
[A-Z_]*).
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-09-03 18:45:43 +00:00
|
|
|
/*
|
|
|
|
* Does the refname try to escape refs/?
|
|
|
|
* For example: refs/foo/../bar is safe but refs/foo/../../bar
|
|
|
|
* is not.
|
|
|
|
*/
|
|
|
|
result = !normalize_path_copy(buf, refname + strlen("refs/"));
|
|
|
|
free(buf);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
while (*refname) {
|
|
|
|
if (!isupper(*refname) && *refname != '_')
|
|
|
|
return 0;
|
|
|
|
refname++;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
char *resolve_refdup(const char *refname, int resolve_flags,
|
|
|
|
unsigned char *sha1, int *flags)
|
Start handling references internally as a sorted in-memory list
This also adds some very rudimentary support for the notion of packed
refs. HOWEVER! At this point it isn't used to actually look up a ref
yet, only for listing them (ie "for_each_ref()" and friends see the
packed refs, but none of the other single-ref lookup routines).
Note how we keep two separate lists: one for the loose refs, and one for
the packed refs we read. That's so that we can easily keep the two apart,
and read only one set or the other (and still always make sure that the
loose refs take precedence).
[ From this, it's not actually obvious why we'd keep the two separate
lists, but it's important to have the packed refs on their own list
later on, when I add support for looking up a single loose one.
For that case, we will want to read _just_ the packed refs in case the
single-ref lookup fails, yet we may end up needing the other list at
some point in the future, so keeping them separated is important ]
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-09-11 23:37:32 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
return xstrdup_or_null(resolve_ref_unsafe(refname, resolve_flags,
|
|
|
|
sha1, flags));
|
2011-12-12 05:38:22 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
/* The argument to filter_refs */
|
|
|
|
struct ref_filter {
|
|
|
|
const char *pattern;
|
|
|
|
each_ref_fn *fn;
|
|
|
|
void *cb_data;
|
|
|
|
};
|
2012-04-10 05:30:26 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
|
2012-04-10 05:30:21 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
if (resolve_ref_unsafe(refname, resolve_flags, sha1, flags))
|
|
|
|
return 0;
|
|
|
|
return -1;
|
2012-04-10 05:30:21 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
int read_ref(const char *refname, unsigned char *sha1)
|
2011-12-12 05:38:22 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
return read_ref_full(refname, RESOLVE_REF_READING, sha1, NULL);
|
2007-04-17 01:42:50 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
int ref_exists(const char *refname)
|
2012-04-10 05:30:13 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
unsigned char sha1[20];
|
|
|
|
return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, sha1, NULL);
|
2012-04-10 05:30:13 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
static int filter_refs(const char *refname, const struct object_id *oid,
|
|
|
|
int flags, void *data)
|
2012-04-10 05:30:26 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
struct ref_filter *filter = (struct ref_filter *)data;
|
|
|
|
|
|
|
|
if (wildmatch(filter->pattern, refname, 0, NULL))
|
|
|
|
return 0;
|
|
|
|
return filter->fn(refname, oid, flags, filter->cb_data);
|
2012-04-10 05:30:26 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
enum peel_status peel_object(const unsigned char *name, unsigned char *sha1)
|
2007-04-17 01:42:50 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
struct object *o = lookup_unknown_object(name);
|
2007-04-17 01:42:50 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
if (o->type == OBJ_NONE) {
|
|
|
|
int type = sha1_object_info(name, NULL);
|
|
|
|
if (type < 0 || !object_as_type(o, type, 0))
|
|
|
|
return PEEL_INVALID;
|
|
|
|
}
|
2012-04-10 05:30:13 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
if (o->type != OBJ_TAG)
|
|
|
|
return PEEL_NON_TAG;
|
2012-05-22 21:03:29 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
o = deref_tag_noverify(o);
|
|
|
|
if (!o)
|
|
|
|
return PEEL_INVALID;
|
|
|
|
|
2015-12-10 20:36:13 +00:00
|
|
|
hashcpy(sha1, o->oid.hash);
|
2015-11-09 13:34:01 +00:00
|
|
|
return PEEL_PEELED;
|
2012-05-22 21:03:29 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
struct warn_if_dangling_data {
|
|
|
|
FILE *fp;
|
|
|
|
const char *refname;
|
|
|
|
const struct string_list *refnames;
|
|
|
|
const char *msg_fmt;
|
|
|
|
};
|
2012-04-10 05:30:13 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
static int warn_if_dangling_symref(const char *refname, const struct object_id *oid,
|
|
|
|
int flags, void *cb_data)
|
|
|
|
{
|
|
|
|
struct warn_if_dangling_data *d = cb_data;
|
|
|
|
const char *resolves_to;
|
|
|
|
struct object_id junk;
|
2012-04-10 05:30:13 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
if (!(flags & REF_ISSYMREF))
|
|
|
|
return 0;
|
2012-04-10 05:30:13 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
resolves_to = resolve_ref_unsafe(refname, 0, junk.hash, NULL);
|
|
|
|
if (!resolves_to
|
|
|
|
|| (d->refname
|
|
|
|
? strcmp(resolves_to, d->refname)
|
|
|
|
: !string_list_has_string(d->refnames, resolves_to))) {
|
|
|
|
return 0;
|
|
|
|
}
|
2012-04-10 05:30:13 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
fprintf(d->fp, d->msg_fmt, refname);
|
|
|
|
fputc('\n', d->fp);
|
|
|
|
return 0;
|
2012-04-10 05:30:13 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
|
2012-04-24 22:45:11 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
struct warn_if_dangling_data data;
|
|
|
|
|
|
|
|
data.fp = fp;
|
|
|
|
data.refname = refname;
|
|
|
|
data.refnames = NULL;
|
|
|
|
data.msg_fmt = msg_fmt;
|
|
|
|
for_each_rawref(warn_if_dangling_symref, &data);
|
2012-04-24 22:45:11 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames)
|
2012-04-10 05:30:26 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
struct warn_if_dangling_data data;
|
2012-04-10 05:30:26 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
data.fp = fp;
|
|
|
|
data.refname = NULL;
|
|
|
|
data.refnames = refnames;
|
|
|
|
data.msg_fmt = msg_fmt;
|
|
|
|
for_each_rawref(warn_if_dangling_symref, &data);
|
2012-04-10 05:30:26 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
int for_each_tag_ref(each_ref_fn fn, void *cb_data)
|
2012-04-10 05:30:26 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
return for_each_ref_in("refs/tags/", fn, cb_data);
|
2012-04-10 05:30:26 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
|
2013-04-22 19:52:27 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
return for_each_ref_in_submodule(submodule, "refs/tags/", fn, cb_data);
|
2012-04-10 05:30:26 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
int for_each_branch_ref(each_ref_fn fn, void *cb_data)
|
2012-04-10 05:30:26 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
return for_each_ref_in("refs/heads/", fn, cb_data);
|
2012-04-10 05:30:26 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
|
2011-12-12 05:38:15 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
return for_each_ref_in_submodule(submodule, "refs/heads/", fn, cb_data);
|
2011-12-12 05:38:15 +00:00
|
|
|
}
|
2012-04-10 05:30:26 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
int for_each_remote_ref(each_ref_fn fn, void *cb_data)
|
2011-09-29 22:11:42 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
return for_each_ref_in("refs/remotes/", fn, cb_data);
|
2011-12-12 05:38:15 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
|
2011-09-29 22:11:42 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
return for_each_ref_in_submodule(submodule, "refs/remotes/", fn, cb_data);
|
2013-04-22 19:52:18 +00:00
|
|
|
}
|
2007-04-17 01:42:50 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
int head_ref_namespaced(each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
int ret = 0;
|
|
|
|
struct object_id oid;
|
|
|
|
int flag;
|
2007-04-17 01:42:50 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
strbuf_addf(&buf, "%sHEAD", get_git_namespace());
|
|
|
|
if (!read_ref_full(buf.buf, RESOLVE_REF_READING, oid.hash, &flag))
|
|
|
|
ret = fn(buf.buf, &oid, flag, cb_data);
|
|
|
|
strbuf_release(&buf);
|
2007-04-17 01:42:50 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
return ret;
|
2011-09-29 22:11:42 +00:00
|
|
|
}
|
2007-04-17 01:42:50 +00:00
|
|
|
|
2015-11-09 13:34:01 +00:00
|
|
|
int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
|
|
|
|
const char *prefix, void *cb_data)
|
2013-04-22 19:52:18 +00:00
|
|
|
{
|
2015-11-09 13:34:01 +00:00
|
|
|
struct strbuf real_pattern = STRBUF_INIT;
|
|
|
|
struct ref_filter filter;
|
|
|
|
int ret;
|
2010-01-20 09:48:25 +00:00
|
|
|
|
2013-11-30 20:55:40 +00:00
|
|
|
if (!prefix && !starts_with(pattern, "refs/"))
|
2010-01-20 09:48:25 +00:00
|
|
|
strbuf_addstr(&real_pattern, "refs/");
|
2010-01-20 09:48:26 +00:00
|
|
|
else if (prefix)
|
|
|
|
strbuf_addstr(&real_pattern, prefix);
|
2010-01-20 09:48:25 +00:00
|
|
|
strbuf_addstr(&real_pattern, pattern);
|
|
|
|
|
2010-03-12 17:04:26 +00:00
|
|
|
if (!has_glob_specials(pattern)) {
|
2010-02-04 05:23:18 +00:00
|
|
|
/* Append implied '/' '*' if not present. */
|
use strbuf_complete to conditionally append slash
When working with paths in strbufs, we frequently want to
ensure that a directory contains a trailing slash before
appending to it. We can shorten this code (and make the
intent more obvious) by calling strbuf_complete.
Most of these cases are trivially identical conversions, but
there are two things to note:
- in a few cases we did not check that the strbuf is
non-empty (which would lead to an out-of-bounds memory
access). These were generally not triggerable in
practice, either from earlier assertions, or typically
because we would have just fed the strbuf to opendir(),
which would choke on an empty path.
- in a few cases we indexed the buffer with "original_len"
or similar, rather than the current sb->len, and it is
not immediately obvious from the diff that they are the
same. In all of these cases, I manually verified that
the strbuf does not change between the assignment and
the strbuf_complete call.
This does not convert cases which look like:
if (sb->len && !is_dir_sep(sb->buf[sb->len - 1]))
strbuf_addch(sb, '/');
as those are obviously semantically different. Some of these
cases arguably should be doing that, but that is out of
scope for this change, which aims purely for cleanup with no
behavior change (and at least it will make such sites easier
to find and examine in the future, as we can grep for
strbuf_complete).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-09-24 21:08:35 +00:00
|
|
|
strbuf_complete(&real_pattern, '/');
|
2010-01-20 09:48:25 +00:00
|
|
|
/* No need to check for '*', there is none. */
|
|
|
|
strbuf_addch(&real_pattern, '*');
|
|
|
|
}
|
|
|
|
|
|
|
|
filter.pattern = real_pattern.buf;
|
|
|
|
filter.fn = fn;
|
|
|
|
filter.cb_data = cb_data;
|
|
|
|
ret = for_each_ref(filter_refs, &filter);
|
|
|
|
|
|
|
|
strbuf_release(&real_pattern);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-01-20 09:48:26 +00:00
|
|
|
int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
|
|
|
|
{
|
|
|
|
return for_each_glob_ref_in(fn, pattern, NULL, cb_data);
|
|
|
|
}
|
|
|
|
|
2009-05-13 21:22:04 +00:00
|
|
|
const char *prettify_refname(const char *name)
|
2009-03-09 01:06:05 +00:00
|
|
|
{
|
|
|
|
return name + (
|
2013-11-30 20:55:40 +00:00
|
|
|
starts_with(name, "refs/heads/") ? 11 :
|
|
|
|
starts_with(name, "refs/tags/") ? 10 :
|
|
|
|
starts_with(name, "refs/remotes/") ? 13 :
|
2009-03-09 01:06:05 +00:00
|
|
|
0);
|
|
|
|
}
|
|
|
|
|
2014-01-14 03:16:07 +00:00
|
|
|
static const char *ref_rev_parse_rules[] = {
|
add refname_match()
We use at least two rulesets for matching abbreviated refnames with
full refnames (starting with 'refs/'). git-rev-parse and git-fetch
use slightly different rules.
This commit introduces a new function refname_match
(const char *abbrev_name, const char *full_name, const char **rules).
abbrev_name is expanded using the rules and matched against full_name.
If a match is found the function returns true. rules is a NULL-terminate
list of format patterns with "%.*s", for example:
const char *ref_rev_parse_rules[] = {
"%.*s",
"refs/%.*s",
"refs/tags/%.*s",
"refs/heads/%.*s",
"refs/remotes/%.*s",
"refs/remotes/%.*s/HEAD",
NULL
};
Asterisks are included in the format strings because this is the form
required in sha1_name.c. Sharing the list with the functions there is
a good idea to avoid duplicating the rules. Hopefully this
facilitates unified matching rules in the future.
This commit makes the rules used by rev-parse for resolving refs to
sha1s available for string comparison. Before this change, the rules
were buried in get_sha1*() and dwim_ref().
A follow-up commit will refactor the rules used by fetch.
refname_match() will be used for matching refspecs in git-send-pack.
Thanks to Daniel Barkalow <barkalow@iabervon.org> for pointing
out that ref_matches_abbrev in remote.c solves a similar problem
and care should be taken to avoid confusion.
Signed-off-by: Steffen Prohaska <prohaska@zib.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-11-11 14:01:46 +00:00
|
|
|
"%.*s",
|
|
|
|
"refs/%.*s",
|
|
|
|
"refs/tags/%.*s",
|
|
|
|
"refs/heads/%.*s",
|
|
|
|
"refs/remotes/%.*s",
|
|
|
|
"refs/remotes/%.*s/HEAD",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2014-01-14 03:16:07 +00:00
|
|
|
int refname_match(const char *abbrev_name, const char *full_name)
|
add refname_match()
We use at least two rulesets for matching abbreviated refnames with
full refnames (starting with 'refs/'). git-rev-parse and git-fetch
use slightly different rules.
This commit introduces a new function refname_match
(const char *abbrev_name, const char *full_name, const char **rules).
abbrev_name is expanded using the rules and matched against full_name.
If a match is found the function returns true. rules is a NULL-terminate
list of format patterns with "%.*s", for example:
const char *ref_rev_parse_rules[] = {
"%.*s",
"refs/%.*s",
"refs/tags/%.*s",
"refs/heads/%.*s",
"refs/remotes/%.*s",
"refs/remotes/%.*s/HEAD",
NULL
};
Asterisks are included in the format strings because this is the form
required in sha1_name.c. Sharing the list with the functions there is
a good idea to avoid duplicating the rules. Hopefully this
facilitates unified matching rules in the future.
This commit makes the rules used by rev-parse for resolving refs to
sha1s available for string comparison. Before this change, the rules
were buried in get_sha1*() and dwim_ref().
A follow-up commit will refactor the rules used by fetch.
refname_match() will be used for matching refspecs in git-send-pack.
Thanks to Daniel Barkalow <barkalow@iabervon.org> for pointing
out that ref_matches_abbrev in remote.c solves a similar problem
and care should be taken to avoid confusion.
Signed-off-by: Steffen Prohaska <prohaska@zib.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-11-11 14:01:46 +00:00
|
|
|
{
|
|
|
|
const char **p;
|
|
|
|
const int abbrev_name_len = strlen(abbrev_name);
|
|
|
|
|
2014-01-14 03:16:07 +00:00
|
|
|
for (p = ref_rev_parse_rules; *p; p++) {
|
add refname_match()
We use at least two rulesets for matching abbreviated refnames with
full refnames (starting with 'refs/'). git-rev-parse and git-fetch
use slightly different rules.
This commit introduces a new function refname_match
(const char *abbrev_name, const char *full_name, const char **rules).
abbrev_name is expanded using the rules and matched against full_name.
If a match is found the function returns true. rules is a NULL-terminate
list of format patterns with "%.*s", for example:
const char *ref_rev_parse_rules[] = {
"%.*s",
"refs/%.*s",
"refs/tags/%.*s",
"refs/heads/%.*s",
"refs/remotes/%.*s",
"refs/remotes/%.*s/HEAD",
NULL
};
Asterisks are included in the format strings because this is the form
required in sha1_name.c. Sharing the list with the functions there is
a good idea to avoid duplicating the rules. Hopefully this
facilitates unified matching rules in the future.
This commit makes the rules used by rev-parse for resolving refs to
sha1s available for string comparison. Before this change, the rules
were buried in get_sha1*() and dwim_ref().
A follow-up commit will refactor the rules used by fetch.
refname_match() will be used for matching refspecs in git-send-pack.
Thanks to Daniel Barkalow <barkalow@iabervon.org> for pointing
out that ref_matches_abbrev in remote.c solves a similar problem
and care should be taken to avoid confusion.
Signed-off-by: Steffen Prohaska <prohaska@zib.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2007-11-11 14:01:46 +00:00
|
|
|
if (!strcmp(full_name, mkpath(*p, abbrev_name_len, abbrev_name))) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-12 17:35:38 +00:00
|
|
|
/*
|
|
|
|
* *string and *len will only be substituted, and *string returned (for
|
|
|
|
* later free()ing) if the string passed in is a magic short-hand form
|
|
|
|
* to name a branch.
|
|
|
|
*/
|
|
|
|
static char *substitute_branch_name(const char **string, int *len)
|
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
2013-09-02 06:34:29 +00:00
|
|
|
int ret = interpret_branch_name(*string, *len, &buf);
|
2011-10-12 17:35:38 +00:00
|
|
|
|
|
|
|
if (ret == *len) {
|
|
|
|
size_t size;
|
|
|
|
*string = strbuf_detach(&buf, &size);
|
|
|
|
*len = size;
|
|
|
|
return (char *)*string;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
|
|
|
|
{
|
|
|
|
char *last_branch = substitute_branch_name(&str, &len);
|
|
|
|
const char **p, *r;
|
|
|
|
int refs_found = 0;
|
|
|
|
|
|
|
|
*ref = NULL;
|
|
|
|
for (p = ref_rev_parse_rules; *p; p++) {
|
|
|
|
char fullref[PATH_MAX];
|
|
|
|
unsigned char sha1_from_ref[20];
|
|
|
|
unsigned char *this_result;
|
|
|
|
int flag;
|
|
|
|
|
|
|
|
this_result = refs_found ? sha1_from_ref : sha1;
|
|
|
|
mksnpath(fullref, sizeof(fullref), *p, len, str);
|
2014-07-15 19:59:36 +00:00
|
|
|
r = resolve_ref_unsafe(fullref, RESOLVE_REF_READING,
|
|
|
|
this_result, &flag);
|
2011-10-12 17:35:38 +00:00
|
|
|
if (r) {
|
|
|
|
if (!refs_found++)
|
|
|
|
*ref = xstrdup(r);
|
|
|
|
if (!warn_ambiguous_refs)
|
|
|
|
break;
|
2011-10-19 20:55:49 +00:00
|
|
|
} else if ((flag & REF_ISSYMREF) && strcmp(fullref, "HEAD")) {
|
2011-10-12 17:35:38 +00:00
|
|
|
warning("ignoring dangling symref %s.", fullref);
|
2011-10-19 20:55:49 +00:00
|
|
|
} else if ((flag & REF_ISBROKEN) && strchr(fullref, '/')) {
|
|
|
|
warning("ignoring broken ref %s.", fullref);
|
|
|
|
}
|
2011-10-12 17:35:38 +00:00
|
|
|
}
|
|
|
|
free(last_branch);
|
|
|
|
return refs_found;
|
|
|
|
}
|
|
|
|
|
|
|
|
int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
|
|
|
|
{
|
|
|
|
char *last_branch = substitute_branch_name(&str, &len);
|
|
|
|
const char **p;
|
|
|
|
int logs_found = 0;
|
|
|
|
|
|
|
|
*log = NULL;
|
|
|
|
for (p = ref_rev_parse_rules; *p; p++) {
|
|
|
|
unsigned char hash[20];
|
|
|
|
char path[PATH_MAX];
|
|
|
|
const char *ref, *it;
|
|
|
|
|
|
|
|
mksnpath(path, sizeof(path), *p, len, str);
|
2014-07-15 19:59:36 +00:00
|
|
|
ref = resolve_ref_unsafe(path, RESOLVE_REF_READING,
|
|
|
|
hash, NULL);
|
2011-10-12 17:35:38 +00:00
|
|
|
if (!ref)
|
|
|
|
continue;
|
2014-05-06 22:45:52 +00:00
|
|
|
if (reflog_exists(path))
|
2011-10-12 17:35:38 +00:00
|
|
|
it = path;
|
2014-05-06 22:45:52 +00:00
|
|
|
else if (strcmp(ref, path) && reflog_exists(ref))
|
2011-10-12 17:35:38 +00:00
|
|
|
it = ref;
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
if (!logs_found++) {
|
|
|
|
*log = xstrdup(it);
|
|
|
|
hashcpy(sha1, hash);
|
|
|
|
}
|
2015-11-09 13:34:01 +00:00
|
|
|
if (!warn_ambiguous_refs)
|
|
|
|
break;
|
2006-09-30 22:02:00 +00:00
|
|
|
}
|
2015-11-09 13:34:01 +00:00
|
|
|
free(last_branch);
|
|
|
|
return logs_found;
|
2013-09-04 15:22:41 +00:00
|
|
|
}
|
|
|
|
|
2015-07-31 06:06:18 +00:00
|
|
|
static int is_per_worktree_ref(const char *refname)
|
|
|
|
{
|
2015-09-01 02:13:11 +00:00
|
|
|
return !strcmp(refname, "HEAD") ||
|
|
|
|
starts_with(refname, "refs/bisect/");
|
2015-07-31 06:06:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int is_pseudoref_syntax(const char *refname)
|
|
|
|
{
|
|
|
|
const char *c;
|
|
|
|
|
|
|
|
for (c = refname; *c; c++) {
|
|
|
|
if (!isupper(*c) && *c != '-' && *c != '_')
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum ref_type ref_type(const char *refname)
|
|
|
|
{
|
|
|
|
if (is_per_worktree_ref(refname))
|
|
|
|
return REF_TYPE_PER_WORKTREE;
|
|
|
|
if (is_pseudoref_syntax(refname))
|
|
|
|
return REF_TYPE_PSEUDOREF;
|
|
|
|
return REF_TYPE_NORMAL;
|
|
|
|
}
|
|
|
|
|
2015-07-31 06:06:19 +00:00
|
|
|
static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
|
|
|
|
const unsigned char *old_sha1, struct strbuf *err)
|
|
|
|
{
|
|
|
|
const char *filename;
|
|
|
|
int fd;
|
|
|
|
static struct lock_file lock;
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
|
|
|
|
|
|
|
|
filename = git_path("%s", pseudoref);
|
|
|
|
fd = hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
|
|
|
|
if (fd < 0) {
|
|
|
|
strbuf_addf(err, "Could not open '%s' for writing: %s",
|
|
|
|
filename, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old_sha1) {
|
|
|
|
unsigned char actual_old_sha1[20];
|
2015-07-15 22:05:28 +00:00
|
|
|
|
|
|
|
if (read_ref(pseudoref, actual_old_sha1))
|
|
|
|
die("could not read ref '%s'", pseudoref);
|
2015-07-31 06:06:19 +00:00
|
|
|
if (hashcmp(actual_old_sha1, old_sha1)) {
|
|
|
|
strbuf_addf(err, "Unexpected sha1 when writing %s", pseudoref);
|
|
|
|
rollback_lock_file(&lock);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (write_in_full(fd, buf.buf, buf.len) != buf.len) {
|
|
|
|
strbuf_addf(err, "Could not write to '%s'", filename);
|
|
|
|
rollback_lock_file(&lock);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
commit_lock_file(&lock);
|
|
|
|
ret = 0;
|
|
|
|
done:
|
|
|
|
strbuf_release(&buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1)
|
|
|
|
{
|
|
|
|
static struct lock_file lock;
|
|
|
|
const char *filename;
|
|
|
|
|
|
|
|
filename = git_path("%s", pseudoref);
|
|
|
|
|
|
|
|
if (old_sha1 && !is_null_sha1(old_sha1)) {
|
|
|
|
int fd;
|
|
|
|
unsigned char actual_old_sha1[20];
|
|
|
|
|
|
|
|
fd = hold_lock_file_for_update(&lock, filename,
|
|
|
|
LOCK_DIE_ON_ERROR);
|
|
|
|
if (fd < 0)
|
|
|
|
die_errno(_("Could not open '%s' for writing"), filename);
|
2015-07-15 22:05:28 +00:00
|
|
|
if (read_ref(pseudoref, actual_old_sha1))
|
|
|
|
die("could not read ref '%s'", pseudoref);
|
2015-07-31 06:06:19 +00:00
|
|
|
if (hashcmp(actual_old_sha1, old_sha1)) {
|
|
|
|
warning("Unexpected sha1 when deleting %s", pseudoref);
|
|
|
|
rollback_lock_file(&lock);
|
|
|
|
return -1;
|
2015-07-21 21:04:50 +00:00
|
|
|
}
|
2015-07-31 06:06:19 +00:00
|
|
|
|
|
|
|
unlink(filename);
|
|
|
|
rollback_lock_file(&lock);
|
|
|
|
} else {
|
|
|
|
unlink(filename);
|
2006-05-17 09:55:02 +00:00
|
|
|
}
|
2015-07-21 21:04:50 +00:00
|
|
|
|
2006-05-17 09:55:02 +00:00
|
|
|
return 0;
|
2005-06-06 20:31:29 +00:00
|
|
|
}
|
2006-05-17 09:56:09 +00:00
|
|
|
|
2015-06-22 14:02:52 +00:00
|
|
|
int delete_ref(const char *refname, const unsigned char *old_sha1,
|
|
|
|
unsigned int flags)
|
2007-01-26 22:26:09 +00:00
|
|
|
{
|
2014-04-30 16:22:45 +00:00
|
|
|
struct ref_transaction *transaction;
|
2015-07-21 21:04:50 +00:00
|
|
|
struct strbuf err = STRBUF_INIT;
|
2007-01-26 22:26:10 +00:00
|
|
|
|
2015-07-31 06:06:19 +00:00
|
|
|
if (ref_type(refname) == REF_TYPE_PSEUDOREF)
|
2015-08-25 21:57:08 +00:00
|
|
|
return delete_pseudoref(refname, old_sha1);
|
2007-02-08 07:41:43 +00:00
|
|
|
|
2014-04-30 16:22:45 +00:00
|
|
|
transaction = ref_transaction_begin(&err);
|
|
|
|
if (!transaction ||
|
2015-06-22 14:02:54 +00:00
|
|
|
ref_transaction_delete(transaction, refname, old_sha1,
|
2015-02-17 17:00:16 +00:00
|
|
|
flags, NULL, &err) ||
|
2014-04-30 19:22:42 +00:00
|
|
|
ref_transaction_commit(transaction, &err)) {
|
2014-04-30 16:22:45 +00:00
|
|
|
error("%s", err.buf);
|
|
|
|
ref_transaction_free(transaction);
|
|
|
|
strbuf_release(&err);
|
2006-09-30 22:02:00 +00:00
|
|
|
return 1;
|
2007-01-26 22:26:09 +00:00
|
|
|
}
|
2015-11-09 13:34:01 +00:00
|
|
|
ref_transaction_free(transaction);
|
|
|
|
strbuf_release(&err);
|
2008-01-16 19:14:30 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2007-01-26 22:26:09 +00:00
|
|
|
|
2015-11-10 11:42:36 +00:00
|
|
|
int copy_reflog_msg(char *buf, const char *msg)
|
2007-07-29 00:17:17 +00:00
|
|
|
{
|
|
|
|
char *cp = buf;
|
|
|
|
char c;
|
|
|
|
int wasspace = 1;
|
2007-01-26 22:26:10 +00:00
|
|
|
|
2007-07-29 00:17:17 +00:00
|
|
|
*cp++ = '\t';
|
|
|
|
while ((c = *msg++)) {
|
|
|
|
if (wasspace && isspace(c))
|
|
|
|
continue;
|
|
|
|
wasspace = isspace(c);
|
|
|
|
if (wasspace)
|
|
|
|
c = ' ';
|
|
|
|
*cp++ = c;
|
2015-07-21 21:04:50 +00:00
|
|
|
}
|
2007-07-29 00:17:17 +00:00
|
|
|
while (buf < cp && isspace(cp[-1]))
|
|
|
|
cp--;
|
|
|
|
*cp++ = '\n';
|
|
|
|
return cp - buf;
|
|
|
|
}
|
2007-01-26 22:26:10 +00:00
|
|
|
|
2015-11-10 11:42:36 +00:00
|
|
|
int should_autocreate_reflog(const char *refname)
|
2015-07-21 21:04:51 +00:00
|
|
|
{
|
|
|
|
if (!log_all_ref_updates)
|
|
|
|
return 0;
|
|
|
|
return starts_with(refname, "refs/heads/") ||
|
|
|
|
starts_with(refname, "refs/remotes/") ||
|
|
|
|
starts_with(refname, "refs/notes/") ||
|
|
|
|
!strcmp(refname, "HEAD");
|
|
|
|
}
|
|
|
|
|
2014-07-15 23:02:38 +00:00
|
|
|
int is_branch(const char *refname)
|
2008-01-15 23:50:17 +00:00
|
|
|
{
|
2013-11-30 20:55:40 +00:00
|
|
|
return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
|
2007-01-26 22:26:09 +00:00
|
|
|
}
|
|
|
|
|
2014-06-03 16:09:59 +00:00
|
|
|
struct read_ref_at_cb {
|
|
|
|
const char *refname;
|
|
|
|
unsigned long at_time;
|
|
|
|
int cnt;
|
|
|
|
int reccnt;
|
|
|
|
unsigned char *sha1;
|
|
|
|
int found_it;
|
|
|
|
|
|
|
|
unsigned char osha1[20];
|
|
|
|
unsigned char nsha1[20];
|
|
|
|
int tz;
|
|
|
|
unsigned long date;
|
|
|
|
char **msg;
|
|
|
|
unsigned long *cutoff_time;
|
|
|
|
int *cutoff_tz;
|
|
|
|
int *cutoff_cnt;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1,
|
|
|
|
const char *email, unsigned long timestamp, int tz,
|
|
|
|
const char *message, void *cb_data)
|
|
|
|
{
|
|
|
|
struct read_ref_at_cb *cb = cb_data;
|
|
|
|
|
|
|
|
cb->reccnt++;
|
|
|
|
cb->tz = tz;
|
|
|
|
cb->date = timestamp;
|
|
|
|
|
|
|
|
if (timestamp <= cb->at_time || cb->cnt == 0) {
|
|
|
|
if (cb->msg)
|
|
|
|
*cb->msg = xstrdup(message);
|
|
|
|
if (cb->cutoff_time)
|
|
|
|
*cb->cutoff_time = timestamp;
|
|
|
|
if (cb->cutoff_tz)
|
|
|
|
*cb->cutoff_tz = tz;
|
|
|
|
if (cb->cutoff_cnt)
|
|
|
|
*cb->cutoff_cnt = cb->reccnt - 1;
|
|
|
|
/*
|
|
|
|
* we have not yet updated cb->[n|o]sha1 so they still
|
|
|
|
* hold the values for the previous record.
|
|
|
|
*/
|
|
|
|
if (!is_null_sha1(cb->osha1)) {
|
|
|
|
hashcpy(cb->sha1, nsha1);
|
|
|
|
if (hashcmp(cb->osha1, nsha1))
|
|
|
|
warning("Log for ref %s has gap after %s.",
|
convert "enum date_mode" into a struct
In preparation for adding date modes that may carry extra
information beyond the mode itself, this patch converts the
date_mode enum into a struct.
Most of the conversion is fairly straightforward; we pass
the struct as a pointer and dereference the type field where
necessary. Locations that declare a date_mode can use a "{}"
constructor. However, the tricky case is where we use the
enum labels as constants, like:
show_date(t, tz, DATE_NORMAL);
Ideally we could say:
show_date(t, tz, &{ DATE_NORMAL });
but of course C does not allow that. Likewise, we cannot
cast the constant to a struct, because we need to pass an
actual address. Our options are basically:
1. Manually add a "struct date_mode d = { DATE_NORMAL }"
definition to each caller, and pass "&d". This makes
the callers uglier, because they sometimes do not even
have their own scope (e.g., they are inside a switch
statement).
2. Provide a pre-made global "date_normal" struct that can
be passed by address. We'd also need "date_rfc2822",
"date_iso8601", and so forth. But at least the ugliness
is defined in one place.
3. Provide a wrapper that generates the correct struct on
the fly. The big downside is that we end up pointing to
a single global, which makes our wrapper non-reentrant.
But show_date is already not reentrant, so it does not
matter.
This patch implements 3, along with a minor macro to keep
the size of the callers sane.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-25 16:55:02 +00:00
|
|
|
cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
|
2014-06-03 16:09:59 +00:00
|
|
|
}
|
|
|
|
else if (cb->date == cb->at_time)
|
|
|
|
hashcpy(cb->sha1, nsha1);
|
|
|
|
else if (hashcmp(nsha1, cb->sha1))
|
|
|
|
warning("Log for ref %s unexpectedly ended on %s.",
|
|
|
|
cb->refname, show_date(cb->date, cb->tz,
|
convert "enum date_mode" into a struct
In preparation for adding date modes that may carry extra
information beyond the mode itself, this patch converts the
date_mode enum into a struct.
Most of the conversion is fairly straightforward; we pass
the struct as a pointer and dereference the type field where
necessary. Locations that declare a date_mode can use a "{}"
constructor. However, the tricky case is where we use the
enum labels as constants, like:
show_date(t, tz, DATE_NORMAL);
Ideally we could say:
show_date(t, tz, &{ DATE_NORMAL });
but of course C does not allow that. Likewise, we cannot
cast the constant to a struct, because we need to pass an
actual address. Our options are basically:
1. Manually add a "struct date_mode d = { DATE_NORMAL }"
definition to each caller, and pass "&d". This makes
the callers uglier, because they sometimes do not even
have their own scope (e.g., they are inside a switch
statement).
2. Provide a pre-made global "date_normal" struct that can
be passed by address. We'd also need "date_rfc2822",
"date_iso8601", and so forth. But at least the ugliness
is defined in one place.
3. Provide a wrapper that generates the correct struct on
the fly. The big downside is that we end up pointing to
a single global, which makes our wrapper non-reentrant.
But show_date is already not reentrant, so it does not
matter.
This patch implements 3, along with a minor macro to keep
the size of the callers sane.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-06-25 16:55:02 +00:00
|
|
|
DATE_MODE(RFC2822)));
|
2014-06-03 16:09:59 +00:00
|
|
|
hashcpy(cb->osha1, osha1);
|
|
|
|
hashcpy(cb->nsha1, nsha1);
|
|
|
|
cb->found_it = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
hashcpy(cb->osha1, osha1);
|
|
|
|
hashcpy(cb->nsha1, nsha1);
|
|
|
|
if (cb->cnt > 0)
|
|
|
|
cb->cnt--;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1,
|
|
|
|
const char *email, unsigned long timestamp,
|
|
|
|
int tz, const char *message, void *cb_data)
|
|
|
|
{
|
|
|
|
struct read_ref_at_cb *cb = cb_data;
|
|
|
|
|
|
|
|
if (cb->msg)
|
|
|
|
*cb->msg = xstrdup(message);
|
|
|
|
if (cb->cutoff_time)
|
|
|
|
*cb->cutoff_time = timestamp;
|
|
|
|
if (cb->cutoff_tz)
|
|
|
|
*cb->cutoff_tz = tz;
|
|
|
|
if (cb->cutoff_cnt)
|
|
|
|
*cb->cutoff_cnt = cb->reccnt;
|
|
|
|
hashcpy(cb->sha1, osha1);
|
|
|
|
if (is_null_sha1(cb->sha1))
|
|
|
|
hashcpy(cb->sha1, nsha1);
|
|
|
|
/* We just want the first entry */
|
|
|
|
return 1;
|
2007-01-19 09:19:05 +00:00
|
|
|
}
|
|
|
|
|
2014-09-19 03:45:37 +00:00
|
|
|
int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time, int cnt,
|
2011-12-12 05:38:09 +00:00
|
|
|
unsigned char *sha1, char **msg,
|
|
|
|
unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
|
2006-05-17 09:56:09 +00:00
|
|
|
{
|
2014-06-03 16:09:59 +00:00
|
|
|
struct read_ref_at_cb cb;
|
2006-05-17 09:56:09 +00:00
|
|
|
|
2014-06-03 16:09:59 +00:00
|
|
|
memset(&cb, 0, sizeof(cb));
|
|
|
|
cb.refname = refname;
|
|
|
|
cb.at_time = at_time;
|
|
|
|
cb.cnt = cnt;
|
|
|
|
cb.msg = msg;
|
|
|
|
cb.cutoff_time = cutoff_time;
|
|
|
|
cb.cutoff_tz = cutoff_tz;
|
|
|
|
cb.cutoff_cnt = cutoff_cnt;
|
|
|
|
cb.sha1 = sha1;
|
|
|
|
|
|
|
|
for_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb);
|
|
|
|
|
2014-09-19 03:45:37 +00:00
|
|
|
if (!cb.reccnt) {
|
|
|
|
if (flags & GET_SHA1_QUIETLY)
|
|
|
|
exit(128);
|
|
|
|
else
|
|
|
|
die("Log for %s is empty.", refname);
|
|
|
|
}
|
2014-06-03 16:09:59 +00:00
|
|
|
if (cb.found_it)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for_each_reflog_ent(refname, read_ref_at_ent_oldest, &cb);
|
2006-05-17 09:56:09 +00:00
|
|
|
|
2007-01-19 09:19:05 +00:00
|
|
|
return 1;
|
2006-05-17 09:56:09 +00:00
|
|
|
}
|
2006-12-18 09:18:16 +00:00
|
|
|
|
2014-05-19 17:42:34 +00:00
|
|
|
struct ref_transaction *ref_transaction_begin(struct strbuf *err)
|
2014-04-07 13:48:10 +00:00
|
|
|
{
|
2014-08-28 23:42:37 +00:00
|
|
|
assert(err);
|
|
|
|
|
2014-04-07 13:48:10 +00:00
|
|
|
return xcalloc(1, sizeof(struct ref_transaction));
|
|
|
|
}
|
|
|
|
|
2014-06-20 14:42:42 +00:00
|
|
|
void ref_transaction_free(struct ref_transaction *transaction)
|
2014-04-07 13:48:10 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2014-06-20 14:42:45 +00:00
|
|
|
if (!transaction)
|
|
|
|
return;
|
|
|
|
|
2014-04-30 19:22:42 +00:00
|
|
|
for (i = 0; i < transaction->nr; i++) {
|
|
|
|
free(transaction->updates[i]->msg);
|
2014-04-07 13:48:14 +00:00
|
|
|
free(transaction->updates[i]);
|
2014-04-30 19:22:42 +00:00
|
|
|
}
|
2014-04-07 13:48:10 +00:00
|
|
|
free(transaction->updates);
|
|
|
|
free(transaction);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ref_update *add_update(struct ref_transaction *transaction,
|
|
|
|
const char *refname)
|
|
|
|
{
|
2016-02-22 22:44:32 +00:00
|
|
|
struct ref_update *update;
|
|
|
|
FLEX_ALLOC_STR(update, refname, refname);
|
2014-04-07 13:48:10 +00:00
|
|
|
ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
|
|
|
|
transaction->updates[transaction->nr++] = update;
|
|
|
|
return update;
|
|
|
|
}
|
|
|
|
|
2014-06-20 14:43:00 +00:00
|
|
|
int ref_transaction_update(struct ref_transaction *transaction,
|
|
|
|
const char *refname,
|
|
|
|
const unsigned char *new_sha1,
|
|
|
|
const unsigned char *old_sha1,
|
2015-02-17 17:00:15 +00:00
|
|
|
unsigned int flags, const char *msg,
|
2014-06-20 14:43:00 +00:00
|
|
|
struct strbuf *err)
|
2014-04-07 13:48:10 +00:00
|
|
|
{
|
2014-06-20 14:43:00 +00:00
|
|
|
struct ref_update *update;
|
2014-04-07 13:48:10 +00:00
|
|
|
|
2014-08-28 23:42:37 +00:00
|
|
|
assert(err);
|
|
|
|
|
2014-04-29 19:06:19 +00:00
|
|
|
if (transaction->state != REF_TRANSACTION_OPEN)
|
|
|
|
die("BUG: update called for transaction that is not open");
|
|
|
|
|
2015-02-17 17:00:21 +00:00
|
|
|
if (new_sha1 && !is_null_sha1(new_sha1) &&
|
refs.c: allow listing and deleting badly named refs
We currently do not handle badly named refs well:
$ cp .git/refs/heads/master .git/refs/heads/master.....@\*@\\.
$ git branch
fatal: Reference has invalid format: 'refs/heads/master.....@*@\.'
$ git branch -D master.....@\*@\\.
error: branch 'master.....@*@\.' not found.
Users cannot recover from a badly named ref without manually finding
and deleting the loose ref file or appropriate line in packed-refs.
Making that easier will make it easier to tweak the ref naming rules
in the future, for example to forbid shell metacharacters like '`'
and '"', without putting people in a state that is hard to get out of.
So allow "branch --list" to show these refs and allow "branch -d/-D"
and "update-ref -d" to delete them. Other commands (for example to
rename refs) will continue to not handle these refs but can be changed
in later patches.
Details:
In resolving functions, refuse to resolve refs that don't pass the
git-check-ref-format(1) check unless the new RESOLVE_REF_ALLOW_BAD_NAME
flag is passed. Even with RESOLVE_REF_ALLOW_BAD_NAME, refuse to
resolve refs that escape the refs/ directory and do not match the
pattern [A-Z_]* (think "HEAD" and "MERGE_HEAD").
In locking functions, refuse to act on badly named refs unless they
are being deleted and either are in the refs/ directory or match [A-Z_]*.
Just like other invalid refs, flag resolved, badly named refs with the
REF_ISBROKEN flag, treat them as resolving to null_sha1, and skip them
in all iteration functions except for for_each_rawref.
Flag badly named refs (but not symrefs pointing to badly named refs)
with a REF_BAD_NAME flag to make it easier for future callers to
notice and handle them specially. For example, in a later patch
for-each-ref will use this flag to detect refs whose names can confuse
callers parsing for-each-ref output.
In the transaction API, refuse to create or update badly named refs,
but allow deleting them (unless they try to escape refs/ and don't match
[A-Z_]*).
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-09-03 18:45:43 +00:00
|
|
|
check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
|
|
|
|
strbuf_addf(err, "refusing to update ref with bad name %s",
|
|
|
|
refname);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-06-20 14:43:00 +00:00
|
|
|
update = add_update(transaction, refname);
|
2015-02-17 17:00:21 +00:00
|
|
|
if (new_sha1) {
|
|
|
|
hashcpy(update->new_sha1, new_sha1);
|
|
|
|
flags |= REF_HAVE_NEW;
|
|
|
|
}
|
2015-02-17 17:00:15 +00:00
|
|
|
if (old_sha1) {
|
2014-04-07 13:48:10 +00:00
|
|
|
hashcpy(update->old_sha1, old_sha1);
|
2015-02-17 17:00:14 +00:00
|
|
|
flags |= REF_HAVE_OLD;
|
|
|
|
}
|
|
|
|
update->flags = flags;
|
2014-04-30 19:22:42 +00:00
|
|
|
if (msg)
|
|
|
|
update->msg = xstrdup(msg);
|
2014-06-20 14:43:00 +00:00
|
|
|
return 0;
|
2014-04-07 13:48:10 +00:00
|
|
|
}
|
|
|
|
|
2014-04-16 22:26:44 +00:00
|
|
|
int ref_transaction_create(struct ref_transaction *transaction,
|
|
|
|
const char *refname,
|
|
|
|
const unsigned char *new_sha1,
|
2015-02-17 17:00:13 +00:00
|
|
|
unsigned int flags, const char *msg,
|
2014-04-16 22:26:44 +00:00
|
|
|
struct strbuf *err)
|
2014-04-07 13:48:10 +00:00
|
|
|
{
|
2015-02-17 17:00:19 +00:00
|
|
|
if (!new_sha1 || is_null_sha1(new_sha1))
|
|
|
|
die("BUG: create called without valid new_sha1");
|
2014-12-04 23:08:13 +00:00
|
|
|
return ref_transaction_update(transaction, refname, new_sha1,
|
2015-02-17 17:00:15 +00:00
|
|
|
null_sha1, flags, msg, err);
|
2014-04-07 13:48:10 +00:00
|
|
|
}
|
|
|
|
|
2014-04-16 22:27:45 +00:00
|
|
|
int ref_transaction_delete(struct ref_transaction *transaction,
|
|
|
|
const char *refname,
|
|
|
|
const unsigned char *old_sha1,
|
2015-02-17 17:00:16 +00:00
|
|
|
unsigned int flags, const char *msg,
|
2014-04-16 22:27:45 +00:00
|
|
|
struct strbuf *err)
|
2014-04-07 13:48:10 +00:00
|
|
|
{
|
2015-02-17 17:00:20 +00:00
|
|
|
if (old_sha1 && is_null_sha1(old_sha1))
|
|
|
|
die("BUG: delete called with old_sha1 set to zeros");
|
2015-02-17 17:00:15 +00:00
|
|
|
return ref_transaction_update(transaction, refname,
|
2015-02-17 17:00:16 +00:00
|
|
|
null_sha1, old_sha1,
|
2015-02-17 17:00:15 +00:00
|
|
|
flags, msg, err);
|
2014-04-07 13:48:10 +00:00
|
|
|
}
|
|
|
|
|
2015-02-17 17:00:21 +00:00
|
|
|
int ref_transaction_verify(struct ref_transaction *transaction,
|
|
|
|
const char *refname,
|
|
|
|
const unsigned char *old_sha1,
|
|
|
|
unsigned int flags,
|
|
|
|
struct strbuf *err)
|
|
|
|
{
|
|
|
|
if (!old_sha1)
|
|
|
|
die("BUG: verify called with old_sha1 set to NULL");
|
|
|
|
return ref_transaction_update(transaction, refname,
|
|
|
|
NULL, old_sha1,
|
|
|
|
flags, NULL, err);
|
|
|
|
}
|
|
|
|
|
2015-02-17 17:00:22 +00:00
|
|
|
int update_ref(const char *msg, const char *refname,
|
|
|
|
const unsigned char *new_sha1, const unsigned char *old_sha1,
|
2015-02-17 17:00:13 +00:00
|
|
|
unsigned int flags, enum action_on_err onerr)
|
2013-09-04 15:22:40 +00:00
|
|
|
{
|
2015-07-31 06:06:19 +00:00
|
|
|
struct ref_transaction *t = NULL;
|
2014-04-24 23:36:55 +00:00
|
|
|
struct strbuf err = STRBUF_INIT;
|
2015-07-31 06:06:19 +00:00
|
|
|
int ret = 0;
|
2014-04-24 23:36:55 +00:00
|
|
|
|
2015-07-31 06:06:19 +00:00
|
|
|
if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
|
|
|
|
ret = write_pseudoref(refname, new_sha1, old_sha1, &err);
|
|
|
|
} else {
|
|
|
|
t = ref_transaction_begin(&err);
|
|
|
|
if (!t ||
|
|
|
|
ref_transaction_update(t, refname, new_sha1, old_sha1,
|
|
|
|
flags, msg, &err) ||
|
|
|
|
ref_transaction_commit(t, &err)) {
|
|
|
|
ret = 1;
|
|
|
|
ref_transaction_free(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ret) {
|
2014-04-24 23:36:55 +00:00
|
|
|
const char *str = "update_ref failed for ref '%s': %s";
|
|
|
|
|
|
|
|
switch (onerr) {
|
|
|
|
case UPDATE_REFS_MSG_ON_ERR:
|
|
|
|
error(str, refname, err.buf);
|
|
|
|
break;
|
|
|
|
case UPDATE_REFS_DIE_ON_ERR:
|
|
|
|
die(str, refname, err.buf);
|
|
|
|
break;
|
|
|
|
case UPDATE_REFS_QUIET_ON_ERR:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
strbuf_release(&err);
|
2013-09-04 15:22:40 +00:00
|
|
|
return 1;
|
2014-04-24 23:36:55 +00:00
|
|
|
}
|
|
|
|
strbuf_release(&err);
|
2015-07-31 06:06:19 +00:00
|
|
|
if (t)
|
|
|
|
ref_transaction_free(t);
|
2014-04-24 23:36:55 +00:00
|
|
|
return 0;
|
2013-09-04 15:22:40 +00:00
|
|
|
}
|
|
|
|
|
2011-12-12 05:38:09 +00:00
|
|
|
char *shorten_unambiguous_ref(const char *refname, int strict)
|
2009-04-07 07:14:20 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
static char **scanf_fmts;
|
|
|
|
static int nr_rules;
|
|
|
|
char *short_name;
|
|
|
|
|
|
|
|
if (!nr_rules) {
|
2014-01-08 14:43:39 +00:00
|
|
|
/*
|
|
|
|
* Pre-generate scanf formats from ref_rev_parse_rules[].
|
|
|
|
* Generate a format suitable for scanf from a
|
|
|
|
* ref_rev_parse_rules rule by interpolating "%s" at the
|
|
|
|
* location of the "%.*s".
|
|
|
|
*/
|
2009-04-07 07:14:20 +00:00
|
|
|
size_t total_len = 0;
|
2014-01-08 14:43:38 +00:00
|
|
|
size_t offset = 0;
|
2009-04-07 07:14:20 +00:00
|
|
|
|
|
|
|
/* the rule list is NULL terminated, count them first */
|
2013-10-24 08:45:13 +00:00
|
|
|
for (nr_rules = 0; ref_rev_parse_rules[nr_rules]; nr_rules++)
|
2014-01-08 14:43:40 +00:00
|
|
|
/* -2 for strlen("%.*s") - strlen("%s"); +1 for NUL */
|
|
|
|
total_len += strlen(ref_rev_parse_rules[nr_rules]) - 2 + 1;
|
2009-04-07 07:14:20 +00:00
|
|
|
|
2016-02-22 22:44:35 +00:00
|
|
|
scanf_fmts = xmalloc(st_add(st_mult(nr_rules, sizeof(char *)), total_len));
|
2009-04-07 07:14:20 +00:00
|
|
|
|
2014-01-08 14:43:38 +00:00
|
|
|
offset = 0;
|
2009-04-07 07:14:20 +00:00
|
|
|
for (i = 0; i < nr_rules; i++) {
|
2014-01-08 14:43:39 +00:00
|
|
|
assert(offset < total_len);
|
2014-01-08 14:43:38 +00:00
|
|
|
scanf_fmts[i] = (char *)&scanf_fmts[nr_rules] + offset;
|
2014-01-08 14:43:39 +00:00
|
|
|
offset += snprintf(scanf_fmts[i], total_len - offset,
|
|
|
|
ref_rev_parse_rules[i], 2, "%s") + 1;
|
2009-04-07 07:14:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* bail out if there are no rules */
|
|
|
|
if (!nr_rules)
|
2011-12-12 05:38:09 +00:00
|
|
|
return xstrdup(refname);
|
2009-04-07 07:14:20 +00:00
|
|
|
|
2011-12-12 05:38:09 +00:00
|
|
|
/* buffer for scanf result, at most refname must fit */
|
|
|
|
short_name = xstrdup(refname);
|
2009-04-07 07:14:20 +00:00
|
|
|
|
|
|
|
/* skip first rule, it will always match */
|
|
|
|
for (i = nr_rules - 1; i > 0 ; --i) {
|
|
|
|
int j;
|
2009-04-13 10:25:46 +00:00
|
|
|
int rules_to_fail = i;
|
2009-04-07 07:14:20 +00:00
|
|
|
int short_name_len;
|
|
|
|
|
2011-12-12 05:38:09 +00:00
|
|
|
if (1 != sscanf(refname, scanf_fmts[i], short_name))
|
2009-04-07 07:14:20 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
short_name_len = strlen(short_name);
|
|
|
|
|
2009-04-13 10:25:46 +00:00
|
|
|
/*
|
|
|
|
* in strict mode, all (except the matched one) rules
|
|
|
|
* must fail to resolve to a valid non-ambiguous ref
|
|
|
|
*/
|
|
|
|
if (strict)
|
|
|
|
rules_to_fail = nr_rules;
|
|
|
|
|
2009-04-07 07:14:20 +00:00
|
|
|
/*
|
|
|
|
* check if the short name resolves to a valid ref,
|
|
|
|
* but use only rules prior to the matched one
|
|
|
|
*/
|
2009-04-13 10:25:46 +00:00
|
|
|
for (j = 0; j < rules_to_fail; j++) {
|
2009-04-07 07:14:20 +00:00
|
|
|
const char *rule = ref_rev_parse_rules[j];
|
|
|
|
char refname[PATH_MAX];
|
|
|
|
|
2009-04-13 10:25:46 +00:00
|
|
|
/* skip matched rule */
|
|
|
|
if (i == j)
|
|
|
|
continue;
|
|
|
|
|
2009-04-07 07:14:20 +00:00
|
|
|
/*
|
|
|
|
* the short name is ambiguous, if it resolves
|
|
|
|
* (with this previous rule) to a valid ref
|
|
|
|
* read_ref() returns 0 on success
|
|
|
|
*/
|
|
|
|
mksnpath(refname, sizeof(refname),
|
|
|
|
rule, short_name_len, short_name);
|
2011-11-13 10:22:14 +00:00
|
|
|
if (ref_exists(refname))
|
2009-04-07 07:14:20 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* short name is non-ambiguous if all previous rules
|
|
|
|
* haven't resolved to a valid ref
|
|
|
|
*/
|
2009-04-13 10:25:46 +00:00
|
|
|
if (j == rules_to_fail)
|
2009-04-07 07:14:20 +00:00
|
|
|
return short_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(short_name);
|
2011-12-12 05:38:09 +00:00
|
|
|
return xstrdup(refname);
|
2009-04-07 07:14:20 +00:00
|
|
|
}
|
upload/receive-pack: allow hiding ref hierarchies
A repository may have refs that are only used for its internal
bookkeeping purposes that should not be exposed to the others that
come over the network.
Teach upload-pack to omit some refs from its initial advertisement
by paying attention to the uploadpack.hiderefs multi-valued
configuration variable. Do the same to receive-pack via the
receive.hiderefs variable. As a convenient short-hand, allow using
transfer.hiderefs to set the value to both of these variables.
Any ref that is under the hierarchies listed on the value of these
variable is excluded from responses to requests made by "ls-remote",
"fetch", etc. (for upload-pack) and "push" (for receive-pack).
Because these hidden refs do not count as OUR_REF, an attempt to
fetch objects at the tip of them will be rejected, and because these
refs do not get advertised, "git push :" will not see local branches
that have the same name as them as "matching" ones to be sent.
An attempt to update/delete these hidden refs with an explicit
refspec, e.g. "git push origin :refs/hidden/22", is rejected. This
is not a new restriction. To the pusher, it would appear that there
is no such ref, so its push request will conclude with "Now that I
sent you all the data, it is time for you to update the refs. I saw
that the ref did not exist when I started pushing, and I want the
result to point at this commit". The receiving end will apply the
compare-and-swap rule to this request and rejects the push with
"Well, your update request conflicts with somebody else; I see there
is such a ref.", which is the right thing to do. Otherwise a push to
a hidden ref will always be "the last one wins", which is not a good
default.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-19 00:08:30 +00:00
|
|
|
|
|
|
|
static struct string_list *hide_refs;
|
|
|
|
|
|
|
|
int parse_hide_refs_config(const char *var, const char *value, const char *section)
|
|
|
|
{
|
|
|
|
if (!strcmp("transfer.hiderefs", var) ||
|
|
|
|
/* NEEDSWORK: use parse_config_key() once both are merged */
|
2013-11-30 20:55:40 +00:00
|
|
|
(starts_with(var, section) && var[strlen(section)] == '.' &&
|
upload/receive-pack: allow hiding ref hierarchies
A repository may have refs that are only used for its internal
bookkeeping purposes that should not be exposed to the others that
come over the network.
Teach upload-pack to omit some refs from its initial advertisement
by paying attention to the uploadpack.hiderefs multi-valued
configuration variable. Do the same to receive-pack via the
receive.hiderefs variable. As a convenient short-hand, allow using
transfer.hiderefs to set the value to both of these variables.
Any ref that is under the hierarchies listed on the value of these
variable is excluded from responses to requests made by "ls-remote",
"fetch", etc. (for upload-pack) and "push" (for receive-pack).
Because these hidden refs do not count as OUR_REF, an attempt to
fetch objects at the tip of them will be rejected, and because these
refs do not get advertised, "git push :" will not see local branches
that have the same name as them as "matching" ones to be sent.
An attempt to update/delete these hidden refs with an explicit
refspec, e.g. "git push origin :refs/hidden/22", is rejected. This
is not a new restriction. To the pusher, it would appear that there
is no such ref, so its push request will conclude with "Now that I
sent you all the data, it is time for you to update the refs. I saw
that the ref did not exist when I started pushing, and I want the
result to point at this commit". The receiving end will apply the
compare-and-swap rule to this request and rejects the push with
"Well, your update request conflicts with somebody else; I see there
is such a ref.", which is the right thing to do. Otherwise a push to
a hidden ref will always be "the last one wins", which is not a good
default.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-19 00:08:30 +00:00
|
|
|
!strcmp(var + strlen(section), ".hiderefs"))) {
|
|
|
|
char *ref;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(var);
|
|
|
|
ref = xstrdup(value);
|
|
|
|
len = strlen(ref);
|
|
|
|
while (len && ref[len - 1] == '/')
|
|
|
|
ref[--len] = '\0';
|
|
|
|
if (!hide_refs) {
|
|
|
|
hide_refs = xcalloc(1, sizeof(*hide_refs));
|
|
|
|
hide_refs->strdup_strings = 1;
|
|
|
|
}
|
|
|
|
string_list_append(hide_refs, ref);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-11-03 07:58:16 +00:00
|
|
|
int ref_is_hidden(const char *refname, const char *refname_full)
|
upload/receive-pack: allow hiding ref hierarchies
A repository may have refs that are only used for its internal
bookkeeping purposes that should not be exposed to the others that
come over the network.
Teach upload-pack to omit some refs from its initial advertisement
by paying attention to the uploadpack.hiderefs multi-valued
configuration variable. Do the same to receive-pack via the
receive.hiderefs variable. As a convenient short-hand, allow using
transfer.hiderefs to set the value to both of these variables.
Any ref that is under the hierarchies listed on the value of these
variable is excluded from responses to requests made by "ls-remote",
"fetch", etc. (for upload-pack) and "push" (for receive-pack).
Because these hidden refs do not count as OUR_REF, an attempt to
fetch objects at the tip of them will be rejected, and because these
refs do not get advertised, "git push :" will not see local branches
that have the same name as them as "matching" ones to be sent.
An attempt to update/delete these hidden refs with an explicit
refspec, e.g. "git push origin :refs/hidden/22", is rejected. This
is not a new restriction. To the pusher, it would appear that there
is no such ref, so its push request will conclude with "Now that I
sent you all the data, it is time for you to update the refs. I saw
that the ref did not exist when I started pushing, and I want the
result to point at this commit". The receiving end will apply the
compare-and-swap rule to this request and rejects the push with
"Well, your update request conflicts with somebody else; I see there
is such a ref.", which is the right thing to do. Otherwise a push to
a hidden ref will always be "the last one wins", which is not a good
default.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-19 00:08:30 +00:00
|
|
|
{
|
refs: support negative transfer.hideRefs
If you hide a hierarchy of refs using the transfer.hideRefs
config, there is no way to later override that config to
"unhide" it. This patch implements a "negative" hide which
causes matches to immediately be marked as unhidden, even if
another match would hide it. We take care to apply the
matches in reverse-order from how they are fed to us by the
config machinery, as that lets our usual "last one wins"
config precedence work (and entries in .git/config, for
example, will override /etc/gitconfig).
So you can now do:
$ git config --system transfer.hideRefs refs/secret
$ git config transfer.hideRefs '!refs/secret/not-so-secret'
to hide refs/secret in all repos, except for one public bit
in one specific repo. Or you can even do:
$ git clone \
-u "git -c transfer.hiderefs="!refs/foo" upload-pack" \
remote:repo.git
to clone remote:repo.git, overriding any hiding it has
configured.
There are two alternatives that were considered and
rejected:
1. A generic config mechanism for removing an item from a
list. E.g.: (e.g., "[transfer] hideRefs -= refs/foo").
This is nice because it could apply to other
multi-valued config, as well. But it is not nearly as
flexible. There is no way to say:
[transfer]
hideRefs = refs/secret
hideRefs = refs/secret/not-so-secret
Having explicit negative specifications means we can
override previous entries, even if they are not the
same literal string.
2. Adding another variable to override some parts of
hideRefs (e.g., "exposeRefs").
This solves the problem from alternative (1), but it
cannot easily obey the normal config precedence,
because it would use two separate lists. For example:
[transfer]
hideRefs = refs/secret
exposeRefs = refs/secret/not-so-secret
hideRefs = refs/secret/not-so-secret/no-really-its-secret
With two lists, we have to apply the "expose" rules
first, and only then apply the "hide" rules. But that
does not match what the above config intends.
Of course we could internally parse that to a single
list, respecting the ordering, which saves us having to
invent the new "!" syntax. But using a single name
communicates to the user that the ordering _is_
important. And "!" is well-known for negation, and
should not appear at the beginning of a ref (it is
actually valid in a ref-name, but all entries here
should be fully-qualified, starting with "refs/").
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-07-28 20:23:26 +00:00
|
|
|
int i;
|
upload/receive-pack: allow hiding ref hierarchies
A repository may have refs that are only used for its internal
bookkeeping purposes that should not be exposed to the others that
come over the network.
Teach upload-pack to omit some refs from its initial advertisement
by paying attention to the uploadpack.hiderefs multi-valued
configuration variable. Do the same to receive-pack via the
receive.hiderefs variable. As a convenient short-hand, allow using
transfer.hiderefs to set the value to both of these variables.
Any ref that is under the hierarchies listed on the value of these
variable is excluded from responses to requests made by "ls-remote",
"fetch", etc. (for upload-pack) and "push" (for receive-pack).
Because these hidden refs do not count as OUR_REF, an attempt to
fetch objects at the tip of them will be rejected, and because these
refs do not get advertised, "git push :" will not see local branches
that have the same name as them as "matching" ones to be sent.
An attempt to update/delete these hidden refs with an explicit
refspec, e.g. "git push origin :refs/hidden/22", is rejected. This
is not a new restriction. To the pusher, it would appear that there
is no such ref, so its push request will conclude with "Now that I
sent you all the data, it is time for you to update the refs. I saw
that the ref did not exist when I started pushing, and I want the
result to point at this commit". The receiving end will apply the
compare-and-swap rule to this request and rejects the push with
"Well, your update request conflicts with somebody else; I see there
is such a ref.", which is the right thing to do. Otherwise a push to
a hidden ref will always be "the last one wins", which is not a good
default.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-19 00:08:30 +00:00
|
|
|
|
|
|
|
if (!hide_refs)
|
|
|
|
return 0;
|
refs: support negative transfer.hideRefs
If you hide a hierarchy of refs using the transfer.hideRefs
config, there is no way to later override that config to
"unhide" it. This patch implements a "negative" hide which
causes matches to immediately be marked as unhidden, even if
another match would hide it. We take care to apply the
matches in reverse-order from how they are fed to us by the
config machinery, as that lets our usual "last one wins"
config precedence work (and entries in .git/config, for
example, will override /etc/gitconfig).
So you can now do:
$ git config --system transfer.hideRefs refs/secret
$ git config transfer.hideRefs '!refs/secret/not-so-secret'
to hide refs/secret in all repos, except for one public bit
in one specific repo. Or you can even do:
$ git clone \
-u "git -c transfer.hiderefs="!refs/foo" upload-pack" \
remote:repo.git
to clone remote:repo.git, overriding any hiding it has
configured.
There are two alternatives that were considered and
rejected:
1. A generic config mechanism for removing an item from a
list. E.g.: (e.g., "[transfer] hideRefs -= refs/foo").
This is nice because it could apply to other
multi-valued config, as well. But it is not nearly as
flexible. There is no way to say:
[transfer]
hideRefs = refs/secret
hideRefs = refs/secret/not-so-secret
Having explicit negative specifications means we can
override previous entries, even if they are not the
same literal string.
2. Adding another variable to override some parts of
hideRefs (e.g., "exposeRefs").
This solves the problem from alternative (1), but it
cannot easily obey the normal config precedence,
because it would use two separate lists. For example:
[transfer]
hideRefs = refs/secret
exposeRefs = refs/secret/not-so-secret
hideRefs = refs/secret/not-so-secret/no-really-its-secret
With two lists, we have to apply the "expose" rules
first, and only then apply the "hide" rules. But that
does not match what the above config intends.
Of course we could internally parse that to a single
list, respecting the ordering, which saves us having to
invent the new "!" syntax. But using a single name
communicates to the user that the ordering _is_
important. And "!" is well-known for negation, and
should not appear at the beginning of a ref (it is
actually valid in a ref-name, but all entries here
should be fully-qualified, starting with "refs/").
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-07-28 20:23:26 +00:00
|
|
|
for (i = hide_refs->nr - 1; i >= 0; i--) {
|
|
|
|
const char *match = hide_refs->items[i].string;
|
2015-11-03 07:58:16 +00:00
|
|
|
const char *subject;
|
refs: support negative transfer.hideRefs
If you hide a hierarchy of refs using the transfer.hideRefs
config, there is no way to later override that config to
"unhide" it. This patch implements a "negative" hide which
causes matches to immediately be marked as unhidden, even if
another match would hide it. We take care to apply the
matches in reverse-order from how they are fed to us by the
config machinery, as that lets our usual "last one wins"
config precedence work (and entries in .git/config, for
example, will override /etc/gitconfig).
So you can now do:
$ git config --system transfer.hideRefs refs/secret
$ git config transfer.hideRefs '!refs/secret/not-so-secret'
to hide refs/secret in all repos, except for one public bit
in one specific repo. Or you can even do:
$ git clone \
-u "git -c transfer.hiderefs="!refs/foo" upload-pack" \
remote:repo.git
to clone remote:repo.git, overriding any hiding it has
configured.
There are two alternatives that were considered and
rejected:
1. A generic config mechanism for removing an item from a
list. E.g.: (e.g., "[transfer] hideRefs -= refs/foo").
This is nice because it could apply to other
multi-valued config, as well. But it is not nearly as
flexible. There is no way to say:
[transfer]
hideRefs = refs/secret
hideRefs = refs/secret/not-so-secret
Having explicit negative specifications means we can
override previous entries, even if they are not the
same literal string.
2. Adding another variable to override some parts of
hideRefs (e.g., "exposeRefs").
This solves the problem from alternative (1), but it
cannot easily obey the normal config precedence,
because it would use two separate lists. For example:
[transfer]
hideRefs = refs/secret
exposeRefs = refs/secret/not-so-secret
hideRefs = refs/secret/not-so-secret/no-really-its-secret
With two lists, we have to apply the "expose" rules
first, and only then apply the "hide" rules. But that
does not match what the above config intends.
Of course we could internally parse that to a single
list, respecting the ordering, which saves us having to
invent the new "!" syntax. But using a single name
communicates to the user that the ordering _is_
important. And "!" is well-known for negation, and
should not appear at the beginning of a ref (it is
actually valid in a ref-name, but all entries here
should be fully-qualified, starting with "refs/").
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-07-28 20:23:26 +00:00
|
|
|
int neg = 0;
|
upload/receive-pack: allow hiding ref hierarchies
A repository may have refs that are only used for its internal
bookkeeping purposes that should not be exposed to the others that
come over the network.
Teach upload-pack to omit some refs from its initial advertisement
by paying attention to the uploadpack.hiderefs multi-valued
configuration variable. Do the same to receive-pack via the
receive.hiderefs variable. As a convenient short-hand, allow using
transfer.hiderefs to set the value to both of these variables.
Any ref that is under the hierarchies listed on the value of these
variable is excluded from responses to requests made by "ls-remote",
"fetch", etc. (for upload-pack) and "push" (for receive-pack).
Because these hidden refs do not count as OUR_REF, an attempt to
fetch objects at the tip of them will be rejected, and because these
refs do not get advertised, "git push :" will not see local branches
that have the same name as them as "matching" ones to be sent.
An attempt to update/delete these hidden refs with an explicit
refspec, e.g. "git push origin :refs/hidden/22", is rejected. This
is not a new restriction. To the pusher, it would appear that there
is no such ref, so its push request will conclude with "Now that I
sent you all the data, it is time for you to update the refs. I saw
that the ref did not exist when I started pushing, and I want the
result to point at this commit". The receiving end will apply the
compare-and-swap rule to this request and rejects the push with
"Well, your update request conflicts with somebody else; I see there
is such a ref.", which is the right thing to do. Otherwise a push to
a hidden ref will always be "the last one wins", which is not a good
default.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-19 00:08:30 +00:00
|
|
|
int len;
|
refs: support negative transfer.hideRefs
If you hide a hierarchy of refs using the transfer.hideRefs
config, there is no way to later override that config to
"unhide" it. This patch implements a "negative" hide which
causes matches to immediately be marked as unhidden, even if
another match would hide it. We take care to apply the
matches in reverse-order from how they are fed to us by the
config machinery, as that lets our usual "last one wins"
config precedence work (and entries in .git/config, for
example, will override /etc/gitconfig).
So you can now do:
$ git config --system transfer.hideRefs refs/secret
$ git config transfer.hideRefs '!refs/secret/not-so-secret'
to hide refs/secret in all repos, except for one public bit
in one specific repo. Or you can even do:
$ git clone \
-u "git -c transfer.hiderefs="!refs/foo" upload-pack" \
remote:repo.git
to clone remote:repo.git, overriding any hiding it has
configured.
There are two alternatives that were considered and
rejected:
1. A generic config mechanism for removing an item from a
list. E.g.: (e.g., "[transfer] hideRefs -= refs/foo").
This is nice because it could apply to other
multi-valued config, as well. But it is not nearly as
flexible. There is no way to say:
[transfer]
hideRefs = refs/secret
hideRefs = refs/secret/not-so-secret
Having explicit negative specifications means we can
override previous entries, even if they are not the
same literal string.
2. Adding another variable to override some parts of
hideRefs (e.g., "exposeRefs").
This solves the problem from alternative (1), but it
cannot easily obey the normal config precedence,
because it would use two separate lists. For example:
[transfer]
hideRefs = refs/secret
exposeRefs = refs/secret/not-so-secret
hideRefs = refs/secret/not-so-secret/no-really-its-secret
With two lists, we have to apply the "expose" rules
first, and only then apply the "hide" rules. But that
does not match what the above config intends.
Of course we could internally parse that to a single
list, respecting the ordering, which saves us having to
invent the new "!" syntax. But using a single name
communicates to the user that the ordering _is_
important. And "!" is well-known for negation, and
should not appear at the beginning of a ref (it is
actually valid in a ref-name, but all entries here
should be fully-qualified, starting with "refs/").
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-07-28 20:23:26 +00:00
|
|
|
|
|
|
|
if (*match == '!') {
|
|
|
|
neg = 1;
|
|
|
|
match++;
|
|
|
|
}
|
|
|
|
|
2015-11-03 07:58:16 +00:00
|
|
|
if (*match == '^') {
|
|
|
|
subject = refname_full;
|
|
|
|
match++;
|
|
|
|
} else {
|
|
|
|
subject = refname;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* refname can be NULL when namespaces are used. */
|
|
|
|
if (!subject || !starts_with(subject, match))
|
upload/receive-pack: allow hiding ref hierarchies
A repository may have refs that are only used for its internal
bookkeeping purposes that should not be exposed to the others that
come over the network.
Teach upload-pack to omit some refs from its initial advertisement
by paying attention to the uploadpack.hiderefs multi-valued
configuration variable. Do the same to receive-pack via the
receive.hiderefs variable. As a convenient short-hand, allow using
transfer.hiderefs to set the value to both of these variables.
Any ref that is under the hierarchies listed on the value of these
variable is excluded from responses to requests made by "ls-remote",
"fetch", etc. (for upload-pack) and "push" (for receive-pack).
Because these hidden refs do not count as OUR_REF, an attempt to
fetch objects at the tip of them will be rejected, and because these
refs do not get advertised, "git push :" will not see local branches
that have the same name as them as "matching" ones to be sent.
An attempt to update/delete these hidden refs with an explicit
refspec, e.g. "git push origin :refs/hidden/22", is rejected. This
is not a new restriction. To the pusher, it would appear that there
is no such ref, so its push request will conclude with "Now that I
sent you all the data, it is time for you to update the refs. I saw
that the ref did not exist when I started pushing, and I want the
result to point at this commit". The receiving end will apply the
compare-and-swap rule to this request and rejects the push with
"Well, your update request conflicts with somebody else; I see there
is such a ref.", which is the right thing to do. Otherwise a push to
a hidden ref will always be "the last one wins", which is not a good
default.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-19 00:08:30 +00:00
|
|
|
continue;
|
refs: support negative transfer.hideRefs
If you hide a hierarchy of refs using the transfer.hideRefs
config, there is no way to later override that config to
"unhide" it. This patch implements a "negative" hide which
causes matches to immediately be marked as unhidden, even if
another match would hide it. We take care to apply the
matches in reverse-order from how they are fed to us by the
config machinery, as that lets our usual "last one wins"
config precedence work (and entries in .git/config, for
example, will override /etc/gitconfig).
So you can now do:
$ git config --system transfer.hideRefs refs/secret
$ git config transfer.hideRefs '!refs/secret/not-so-secret'
to hide refs/secret in all repos, except for one public bit
in one specific repo. Or you can even do:
$ git clone \
-u "git -c transfer.hiderefs="!refs/foo" upload-pack" \
remote:repo.git
to clone remote:repo.git, overriding any hiding it has
configured.
There are two alternatives that were considered and
rejected:
1. A generic config mechanism for removing an item from a
list. E.g.: (e.g., "[transfer] hideRefs -= refs/foo").
This is nice because it could apply to other
multi-valued config, as well. But it is not nearly as
flexible. There is no way to say:
[transfer]
hideRefs = refs/secret
hideRefs = refs/secret/not-so-secret
Having explicit negative specifications means we can
override previous entries, even if they are not the
same literal string.
2. Adding another variable to override some parts of
hideRefs (e.g., "exposeRefs").
This solves the problem from alternative (1), but it
cannot easily obey the normal config precedence,
because it would use two separate lists. For example:
[transfer]
hideRefs = refs/secret
exposeRefs = refs/secret/not-so-secret
hideRefs = refs/secret/not-so-secret/no-really-its-secret
With two lists, we have to apply the "expose" rules
first, and only then apply the "hide" rules. But that
does not match what the above config intends.
Of course we could internally parse that to a single
list, respecting the ordering, which saves us having to
invent the new "!" syntax. But using a single name
communicates to the user that the ordering _is_
important. And "!" is well-known for negation, and
should not appear at the beginning of a ref (it is
actually valid in a ref-name, but all entries here
should be fully-qualified, starting with "refs/").
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-07-28 20:23:26 +00:00
|
|
|
len = strlen(match);
|
2015-11-03 07:58:16 +00:00
|
|
|
if (!subject[len] || subject[len] == '/')
|
refs: support negative transfer.hideRefs
If you hide a hierarchy of refs using the transfer.hideRefs
config, there is no way to later override that config to
"unhide" it. This patch implements a "negative" hide which
causes matches to immediately be marked as unhidden, even if
another match would hide it. We take care to apply the
matches in reverse-order from how they are fed to us by the
config machinery, as that lets our usual "last one wins"
config precedence work (and entries in .git/config, for
example, will override /etc/gitconfig).
So you can now do:
$ git config --system transfer.hideRefs refs/secret
$ git config transfer.hideRefs '!refs/secret/not-so-secret'
to hide refs/secret in all repos, except for one public bit
in one specific repo. Or you can even do:
$ git clone \
-u "git -c transfer.hiderefs="!refs/foo" upload-pack" \
remote:repo.git
to clone remote:repo.git, overriding any hiding it has
configured.
There are two alternatives that were considered and
rejected:
1. A generic config mechanism for removing an item from a
list. E.g.: (e.g., "[transfer] hideRefs -= refs/foo").
This is nice because it could apply to other
multi-valued config, as well. But it is not nearly as
flexible. There is no way to say:
[transfer]
hideRefs = refs/secret
hideRefs = refs/secret/not-so-secret
Having explicit negative specifications means we can
override previous entries, even if they are not the
same literal string.
2. Adding another variable to override some parts of
hideRefs (e.g., "exposeRefs").
This solves the problem from alternative (1), but it
cannot easily obey the normal config precedence,
because it would use two separate lists. For example:
[transfer]
hideRefs = refs/secret
exposeRefs = refs/secret/not-so-secret
hideRefs = refs/secret/not-so-secret/no-really-its-secret
With two lists, we have to apply the "expose" rules
first, and only then apply the "hide" rules. But that
does not match what the above config intends.
Of course we could internally parse that to a single
list, respecting the ordering, which saves us having to
invent the new "!" syntax. But using a single name
communicates to the user that the ordering _is_
important. And "!" is well-known for negation, and
should not appear at the beginning of a ref (it is
actually valid in a ref-name, but all entries here
should be fully-qualified, starting with "refs/").
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2015-07-28 20:23:26 +00:00
|
|
|
return !neg;
|
upload/receive-pack: allow hiding ref hierarchies
A repository may have refs that are only used for its internal
bookkeeping purposes that should not be exposed to the others that
come over the network.
Teach upload-pack to omit some refs from its initial advertisement
by paying attention to the uploadpack.hiderefs multi-valued
configuration variable. Do the same to receive-pack via the
receive.hiderefs variable. As a convenient short-hand, allow using
transfer.hiderefs to set the value to both of these variables.
Any ref that is under the hierarchies listed on the value of these
variable is excluded from responses to requests made by "ls-remote",
"fetch", etc. (for upload-pack) and "push" (for receive-pack).
Because these hidden refs do not count as OUR_REF, an attempt to
fetch objects at the tip of them will be rejected, and because these
refs do not get advertised, "git push :" will not see local branches
that have the same name as them as "matching" ones to be sent.
An attempt to update/delete these hidden refs with an explicit
refspec, e.g. "git push origin :refs/hidden/22", is rejected. This
is not a new restriction. To the pusher, it would appear that there
is no such ref, so its push request will conclude with "Now that I
sent you all the data, it is time for you to update the refs. I saw
that the ref did not exist when I started pushing, and I want the
result to point at this commit". The receiving end will apply the
compare-and-swap rule to this request and rejects the push with
"Well, your update request conflicts with somebody else; I see there
is such a ref.", which is the right thing to do. Otherwise a push to
a hidden ref will always be "the last one wins", which is not a good
default.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-19 00:08:30 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2014-12-12 08:56:59 +00:00
|
|
|
|
2015-11-10 11:42:40 +00:00
|
|
|
const char *find_descendant_ref(const char *dirname,
|
|
|
|
const struct string_list *extras,
|
|
|
|
const struct string_list *skip)
|
2014-12-12 08:56:59 +00:00
|
|
|
{
|
2015-11-10 11:42:40 +00:00
|
|
|
int pos;
|
2014-12-12 08:56:59 +00:00
|
|
|
|
2015-11-10 11:42:40 +00:00
|
|
|
if (!extras)
|
|
|
|
return NULL;
|
2014-12-12 08:56:59 +00:00
|
|
|
|
|
|
|
/*
|
2015-11-10 11:42:40 +00:00
|
|
|
* Look at the place where dirname would be inserted into
|
|
|
|
* extras. If there is an entry at that position that starts
|
|
|
|
* with dirname (remember, dirname includes the trailing
|
|
|
|
* slash) and is not in skip, then we have a conflict.
|
2014-12-12 08:56:59 +00:00
|
|
|
*/
|
2015-11-10 11:42:40 +00:00
|
|
|
for (pos = string_list_find_insert_index(extras, dirname, 0);
|
|
|
|
pos < extras->nr; pos++) {
|
|
|
|
const char *extra_refname = extras->items[pos].string;
|
2014-12-12 08:56:59 +00:00
|
|
|
|
2015-11-10 11:42:40 +00:00
|
|
|
if (!starts_with(extra_refname, dirname))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (!skip || !string_list_has_string(skip, extra_refname))
|
|
|
|
return extra_refname;
|
2014-12-12 08:56:59 +00:00
|
|
|
}
|
2015-11-10 11:42:40 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-12-12 08:56:59 +00:00
|
|
|
|
2015-11-10 11:42:40 +00:00
|
|
|
int rename_ref_available(const char *oldname, const char *newname)
|
|
|
|
{
|
|
|
|
struct string_list skip = STRING_LIST_INIT_NODUP;
|
|
|
|
struct strbuf err = STRBUF_INIT;
|
|
|
|
int ret;
|
2014-12-12 08:56:59 +00:00
|
|
|
|
2015-11-10 11:42:40 +00:00
|
|
|
string_list_insert(&skip, oldname);
|
|
|
|
ret = !verify_refname_available(newname, NULL, &skip, &err);
|
|
|
|
if (!ret)
|
|
|
|
error("%s", err.buf);
|
|
|
|
|
|
|
|
string_list_clear(&skip, 0);
|
|
|
|
strbuf_release(&err);
|
|
|
|
return ret;
|
2014-12-12 08:56:59 +00:00
|
|
|
}
|
2016-04-07 19:02:48 +00:00
|
|
|
|
|
|
|
int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
struct object_id oid;
|
|
|
|
int flag;
|
|
|
|
|
|
|
|
if (submodule) {
|
|
|
|
if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0)
|
|
|
|
return fn("HEAD", &oid, 0, cb_data);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag))
|
|
|
|
return fn("HEAD", &oid, flag, cb_data);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int head_ref(each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
return head_ref_submodule(NULL, fn, cb_data);
|
|
|
|
}
|
2016-04-07 19:02:49 +00:00
|
|
|
|
|
|
|
int for_each_ref(each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
return do_for_each_ref(NULL, "", fn, 0, 0, cb_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
return do_for_each_ref(submodule, "", fn, 0, 0, cb_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
return do_for_each_ref(NULL, prefix, fn, strlen(prefix), 0, cb_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken)
|
|
|
|
{
|
|
|
|
unsigned int flag = 0;
|
|
|
|
|
|
|
|
if (broken)
|
|
|
|
flag = DO_FOR_EACH_INCLUDE_BROKEN;
|
|
|
|
return do_for_each_ref(NULL, prefix, fn, 0, flag, cb_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
int for_each_ref_in_submodule(const char *submodule, const char *prefix,
|
|
|
|
each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
return do_for_each_ref(submodule, prefix, fn, strlen(prefix), 0, cb_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
int for_each_replace_ref(each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
return do_for_each_ref(NULL, git_replace_ref_base, fn,
|
|
|
|
strlen(git_replace_ref_base), 0, cb_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
int ret;
|
|
|
|
strbuf_addf(&buf, "%srefs/", get_git_namespace());
|
|
|
|
ret = do_for_each_ref(NULL, buf.buf, fn, 0, 0, cb_data);
|
|
|
|
strbuf_release(&buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int for_each_rawref(each_ref_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
return do_for_each_ref(NULL, "", fn, 0,
|
|
|
|
DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
|
|
|
|
}
|
2016-04-07 19:03:10 +00:00
|
|
|
|
|
|
|
/* This function needs to return a meaningful errno on failure */
|
|
|
|
const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
|
|
|
|
unsigned char *sha1, int *flags)
|
|
|
|
{
|
|
|
|
static struct strbuf sb_refname = STRBUF_INIT;
|
|
|
|
int unused_flags;
|
|
|
|
int symref_count;
|
|
|
|
|
|
|
|
if (!flags)
|
|
|
|
flags = &unused_flags;
|
|
|
|
|
|
|
|
*flags = 0;
|
|
|
|
|
|
|
|
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
|
|
|
|
if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
|
|
|
|
!refname_is_safe(refname)) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* dwim_ref() uses REF_ISBROKEN to distinguish between
|
|
|
|
* missing refs and refs that were present but invalid,
|
|
|
|
* to complain about the latter to stderr.
|
|
|
|
*
|
|
|
|
* We don't know whether the ref exists, so don't set
|
|
|
|
* REF_ISBROKEN yet.
|
|
|
|
*/
|
|
|
|
*flags |= REF_BAD_NAME;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (symref_count = 0; symref_count < SYMREF_MAXDEPTH; symref_count++) {
|
|
|
|
unsigned int read_flags = 0;
|
|
|
|
|
|
|
|
if (read_raw_ref(refname, sha1, &sb_refname, &read_flags)) {
|
|
|
|
*flags |= read_flags;
|
|
|
|
if (errno != ENOENT || (resolve_flags & RESOLVE_REF_READING))
|
|
|
|
return NULL;
|
|
|
|
hashclr(sha1);
|
|
|
|
if (*flags & REF_BAD_NAME)
|
|
|
|
*flags |= REF_ISBROKEN;
|
|
|
|
return refname;
|
|
|
|
}
|
|
|
|
|
|
|
|
*flags |= read_flags;
|
|
|
|
|
|
|
|
if (!(read_flags & REF_ISSYMREF)) {
|
|
|
|
if (*flags & REF_BAD_NAME) {
|
|
|
|
hashclr(sha1);
|
|
|
|
*flags |= REF_ISBROKEN;
|
|
|
|
}
|
|
|
|
return refname;
|
|
|
|
}
|
|
|
|
|
|
|
|
refname = sb_refname.buf;
|
|
|
|
if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
|
|
|
|
hashclr(sha1);
|
|
|
|
return refname;
|
|
|
|
}
|
|
|
|
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
|
|
|
|
if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
|
|
|
|
!refname_is_safe(refname)) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*flags |= REF_ISBROKEN | REF_BAD_NAME;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
errno = ELOOP;
|
|
|
|
return NULL;
|
|
|
|
}
|