#include "ll-merge.h"
#include "resolve-undo.h"
#include "submodule.h"
+#include "argv-array.h"
static const char * const checkout_usage[] = {
"git checkout [options] <branch>",
report_tracking(new);
}
-struct rev_list_args {
- int argc;
- int alloc;
- const char **argv;
-};
-
-static void add_one_rev_list_arg(struct rev_list_args *args, const char *s)
-{
- ALLOC_GROW(args->argv, args->argc + 1, args->alloc);
- args->argv[args->argc++] = s;
-}
-
static int add_one_ref_to_rev_list_arg(const char *refname,
const unsigned char *sha1,
int flags,
void *cb_data)
{
- add_one_rev_list_arg(cb_data, refname);
+ argv_array_push(cb_data, refname);
return 0;
}
*/
static void orphaned_commit_warning(struct commit *commit)
{
- struct rev_list_args args = { 0, 0, NULL };
+ struct argv_array args = ARGV_ARRAY_INIT;
struct rev_info revs;
- add_one_rev_list_arg(&args, "(internal)");
- add_one_rev_list_arg(&args, sha1_to_hex(commit->object.sha1));
- add_one_rev_list_arg(&args, "--not");
+ argv_array_push(&args, "(internal)");
+ argv_array_push(&args, sha1_to_hex(commit->object.sha1));
+ argv_array_push(&args, "--not");
for_each_ref(add_one_ref_to_rev_list_arg, &args);
- add_one_rev_list_arg(&args, "--");
- add_one_rev_list_arg(&args, NULL);
+ argv_array_push(&args, "--");
init_revisions(&revs, NULL);
if (setup_revisions(args.argc - 1, args.argv, &revs, NULL) != 1)
else
describe_detached_head(_("Previous HEAD position was"), commit);
+ argv_array_clear(&args);
clear_commit_marks(commit, -1);
for_each_ref(clear_commit_marks_from_one_ref, NULL);
}
new->name = arg;
setup_branch_path(new);
- if (check_ref_format(new->path) == CHECK_REF_FORMAT_OK &&
+ if (!check_refname_format(new->path, 0) &&
resolve_ref(new->path, branch_rev, 1, NULL))
hashcpy(rev, branch_rev);
else
if (opts.new_branch) {
struct strbuf buf = STRBUF_INIT;
- opts.branch_exists = validate_new_branchname(opts.new_branch, &buf, !!opts.new_branch_force);
+ opts.branch_exists = validate_new_branchname(opts.new_branch, &buf,
+ !!opts.new_branch_force, 0);
strbuf_release(&buf);
}
static int fetch_unpack_limit = -1;
static int unpack_limit = 100;
static int prefer_ofs_delta = 1;
-static int no_done = 0;
+static int no_done;
+static int fetch_fsck_objects = -1;
+static int transfer_fsck_objects = -1;
static struct fetch_pack_args args = {
/* .uploadpack = */ "git-upload-pack",
};
for (ref = *refs; ref; ref = next) {
next = ref->next;
if (!memcmp(ref->name, "refs/", 5) &&
- check_ref_format(ref->name + 5))
+ check_refname_format(ref->name + 5, 0))
; /* trash */
else if (args.fetch_all &&
(!args.depth || prefixcmp(ref->name, "refs/tags/") )) {
}
if (*hdr_arg)
*av++ = hdr_arg;
+ if (fetch_fsck_objects >= 0
+ ? fetch_fsck_objects
+ : transfer_fsck_objects >= 0
+ ? transfer_fsck_objects
+ : 0)
+ *av++ = "--strict";
*av++ = NULL;
cmd.in = demux.out;
return 0;
}
+ if (!strcmp(var, "fetch.fsckobjects")) {
+ fetch_fsck_objects = git_config_bool(var, value);
+ return 0;
+ }
+
+ if (!strcmp(var, "transfer.fsckobjects")) {
+ transfer_fsck_objects = git_config_bool(var, value);
+ return 0;
+ }
+
return git_default_config(var, value, cb);
}
#include "transport.h"
#include "string-list.h"
#include "sha1-array.h"
+#include "connected.h"
static const char receive_pack_usage[] = "git receive-pack <git-dir>";
static int deny_non_fast_forwards;
static enum deny_action deny_current_branch = DENY_UNCONFIGURED;
static enum deny_action deny_delete_current = DENY_UNCONFIGURED;
-static int receive_fsck_objects;
+static int receive_fsck_objects = -1;
+static int transfer_fsck_objects = -1;
static int receive_unpack_limit = -1;
static int transfer_unpack_limit = -1;
static int unpack_limit = 100;
return 0;
}
+ if (strcmp(var, "transfer.fsckobjects") == 0) {
+ transfer_fsck_objects = git_config_bool(var, value);
+ return 0;
+ }
+
if (!strcmp(var, "receive.denycurrentbranch")) {
deny_current_branch = parse_deny_action(var, value);
return 0;
return 0;
}
-static int run_receive_hook(struct command *commands, const char *hook_name)
+typedef int (*feed_fn)(void *, const char **, size_t *);
+static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_state)
{
- static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
- struct command *cmd;
struct child_process proc;
struct async muxer;
const char *argv[2];
- int have_input = 0, code;
-
- for (cmd = commands; !have_input && cmd; cmd = cmd->next) {
- if (!cmd->error_string)
- have_input = 1;
- }
+ int code;
- if (!have_input || access(hook_name, X_OK) < 0)
+ if (access(hook_name, X_OK) < 0)
return 0;
argv[0] = hook_name;
return code;
}
- for (cmd = commands; cmd; cmd = cmd->next) {
- if (!cmd->error_string) {
- size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n",
- sha1_to_hex(cmd->old_sha1),
- sha1_to_hex(cmd->new_sha1),
- cmd->ref_name);
- if (write_in_full(proc.in, buf, n) != n)
- break;
- }
+ while (1) {
+ const char *buf;
+ size_t n;
+ if (feed(feed_state, &buf, &n))
+ break;
+ if (write_in_full(proc.in, buf, n) != n)
+ break;
}
close(proc.in);
if (use_sideband)
return finish_command(&proc);
}
+struct receive_hook_feed_state {
+ struct command *cmd;
+ struct strbuf buf;
+};
+
+static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
+{
+ struct receive_hook_feed_state *state = state_;
+ struct command *cmd = state->cmd;
+
+ while (cmd && cmd->error_string)
+ cmd = cmd->next;
+ if (!cmd)
+ return -1; /* EOF */
+ strbuf_reset(&state->buf);
+ strbuf_addf(&state->buf, "%s %s %s\n",
+ sha1_to_hex(cmd->old_sha1), sha1_to_hex(cmd->new_sha1),
+ cmd->ref_name);
+ state->cmd = cmd->next;
+ if (bufp) {
+ *bufp = state->buf.buf;
+ *sizep = state->buf.len;
+ }
+ return 0;
+}
+
+static int run_receive_hook(struct command *commands, const char *hook_name)
+{
+ struct receive_hook_feed_state state;
+ int status;
+
+ strbuf_init(&state.buf, 0);
+ state.cmd = commands;
+ if (feed_receive_hook(&state, NULL, NULL))
+ return 0;
+ state.cmd = commands;
+ status = run_and_feed_hook(hook_name, feed_receive_hook, &state);
+ strbuf_release(&state.buf);
+ return status;
+}
+
static int run_update_hook(struct command *cmd)
{
static const char update_hook[] = "hooks/update";
struct ref_lock *lock;
/* only refs/... are allowed */
- if (prefixcmp(name, "refs/") || check_ref_format(name + 5)) {
+ if (prefixcmp(name, "refs/") || check_refname_format(name + 5, 0)) {
rp_error("refusing to create funny ref '%s' remotely", name);
return "funny refname";
}
string_list_clear(&ref_list, 0);
}
+static int command_singleton_iterator(void *cb_data, unsigned char sha1[20])
+{
+ struct command **cmd_list = cb_data;
+ struct command *cmd = *cmd_list;
+
+ if (!cmd)
+ return -1; /* end of list */
+ *cmd_list = NULL; /* this returns only one */
+ hashcpy(sha1, cmd->new_sha1);
+ return 0;
+}
+
+static void set_connectivity_errors(struct command *commands)
+{
+ struct command *cmd;
+
+ for (cmd = commands; cmd; cmd = cmd->next) {
+ struct command *singleton = cmd;
+ if (!check_everything_connected(command_singleton_iterator,
+ 0, &singleton))
+ continue;
+ cmd->error_string = "missing necessary objects";
+ }
+}
+
+static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20])
+{
+ struct command **cmd_list = cb_data;
+ struct command *cmd = *cmd_list;
+
+ if (!cmd)
+ return -1; /* end of list */
+ *cmd_list = cmd->next;
+ hashcpy(sha1, cmd->new_sha1);
+ return 0;
+}
+
static void execute_commands(struct command *commands, const char *unpacker_error)
{
struct command *cmd;
return;
}
+ cmd = commands;
+ if (check_everything_connected(iterate_receive_command_list,
+ 0, &cmd))
+ set_connectivity_errors(commands);
+
if (run_receive_hook(commands, pre_receive_hook)) {
for (cmd = commands; cmd; cmd = cmd->next)
cmd->error_string = "pre-receive hook declined";
struct pack_header hdr;
const char *hdr_err;
char hdr_arg[38];
+ int fsck_objects = (receive_fsck_objects >= 0
+ ? receive_fsck_objects
+ : transfer_fsck_objects >= 0
+ ? transfer_fsck_objects
+ : 0);
hdr_err = parse_pack_header(&hdr);
if (hdr_err)
int code, i = 0;
const char *unpacker[4];
unpacker[i++] = "unpack-objects";
- if (receive_fsck_objects)
+ if (fsck_objects)
unpacker[i++] = "--strict";
unpacker[i++] = hdr_arg;
unpacker[i++] = NULL;
keeper[i++] = "index-pack";
keeper[i++] = "--stdin";
- if (receive_fsck_objects)
+ if (fsck_objects)
keeper[i++] = "--strict";
keeper[i++] = "--fix-thin";
keeper[i++] = hdr_arg;
static int strbuf_check_tag_ref(struct strbuf *sb, const char *name)
{
if (name[0] == '-')
- return CHECK_REF_FORMAT_ERROR;
+ return -1;
strbuf_reset(sb);
strbuf_addf(sb, "refs/tags/%s", name);
- return check_ref_format(sb->buf);
+ return check_refname_format(sb->buf, 0);
}
int cmd_tag(int argc, const char **argv, const char *prefix)
struct msg_arg msg = { 0, STRBUF_INIT };
struct commit_list *with_commit = NULL;
struct option options[] = {
- OPT_BOOLEAN('l', NULL, &list, "list tag names"),
+ OPT_BOOLEAN('l', "list", &list, "list tag names"),
{ OPTION_INTEGER, 'n', NULL, &lines, "n",
"print <n> lines of each tag message",
PARSE_OPT_OPTARG, NULL, 1 },
- OPT_BOOLEAN('d', NULL, &delete, "delete tags"),
- OPT_BOOLEAN('v', NULL, &verify, "verify tags"),
+ OPT_BOOLEAN('d', "delete", &delete, "delete tags"),
+ OPT_BOOLEAN('v', "verify", &verify, "verify tags"),
OPT_GROUP("Tag creation options"),
- OPT_BOOLEAN('a', NULL, &annotate,
+ OPT_BOOLEAN('a', "annotate", &annotate,
"annotated tag, needs a message"),
- OPT_CALLBACK('m', NULL, &msg, "message",
+ OPT_CALLBACK('m', "message", &msg, "message",
"tag message", parse_msg_arg),
- OPT_FILENAME('F', NULL, &msgfile, "read message from file"),
- OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"),
- OPT_STRING('u', NULL, &keyid, "key-id",
+ OPT_FILENAME('F', "file", &msgfile, "read message from file"),
+ OPT_BOOLEAN('s', "sign", &sign, "annotated and GPG-signed tag"),
+ OPT_STRING('u', "local-user", &keyid, "key-id",
"use another key to sign the tag"),
OPT__FORCE(&force, "replace the tag if exists"),
extern const char *strip_namespace(const char *namespaced_ref);
extern const char *get_git_work_tree(void);
extern const char *read_gitfile(const char *path);
+extern const char *resolve_gitdir(const char *suspect);
extern void set_git_work_tree(const char *tree);
#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
extern const char **get_pathspec(const char *prefix, const char **pathspec);
-extern const char *pathspec_prefix(const char *prefix, const char **pathspec);
extern void setup_work_tree(void);
extern const char *setup_git_directory_gently(int *);
extern const char *setup_git_directory(void);
{
return get_sha1_with_context_1(str, sha1, orc, 0, NULL);
}
+
+ /*
+ * Try to read a SHA1 in hexadecimal format from the 40 characters
+ * starting at hex. Write the 20-byte result to sha1 in binary form.
+ * Return 0 on success. Reading stops if a NUL is encountered in the
+ * input, so it is safe to pass this function an arbitrary
+ * null-terminated string.
+ */
extern int get_sha1_hex(const char *hex, unsigned char *sha1);
+
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
extern int read_ref(const char *filename, unsigned char *sha1);
- extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
+
+ /*
+ * Resolve a reference, recursively following symbolic refererences.
+ *
+ * Store the referred-to object's name in sha1 and return the name of
+ * the non-symbolic reference that ultimately pointed at it. The
+ * return value, if not NULL, is a pointer into either a static buffer
+ * or the input ref.
+ *
+ * If the reference cannot be resolved to an object, the behavior
+ * depends on the "reading" argument:
+ *
+ * - If reading is set, return NULL.
+ *
+ * - If reading is not set, clear sha1 and return the name of the last
+ * reference name in the chain, which will either be a non-symbolic
+ * reference or an undefined reference. If this is a prelude to
+ * "writing" to the ref, the return value is the name of the ref
+ * that will actually be created or changed.
+ *
+ * If flag is non-NULL, set the value that it points to the
+ * combination of REF_ISPACKED (if the reference was found among the
+ * packed references) and REF_ISSYMREF (if the initial reference was a
+ * symbolic reference).
+ *
+ * If ref is not a properly-formatted, normalized reference, return
+ * NULL. If more than MAXDEPTH recursive symbolic lookups are needed,
+ * give up and return NULL.
+ *
+ * errno is sometimes set on errors, but not always.
+ */
+ extern const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag);
+
extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
extern int interpret_branch_name(const char *str, struct strbuf *);
extern int git_config_maybe_bool(const char *, const char *);
extern int git_config_string(const char **, const char *, const char *);
extern int git_config_pathname(const char **, const char *, const char *);
+extern int git_config_set_in_file(const char *, const char *, const char *);
extern int git_config_set(const char *, const char *);
extern int git_config_parse_key(const char *, char **, int *);
extern int git_config_set_multivar(const char *, const char *, const char *, int);
+extern int git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
extern int git_config_rename_section(const char *, const char *);
extern const char *git_etc_gitconfig(void);
extern int check_repository_format_version(const char *var, const char *value, void *cb);
entry = xmalloc(sizeof(struct ref_list) + len);
hashcpy(entry->sha1, sha1);
hashclr(entry->peeled);
+ if (check_refname_format(name, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))
+ die("Reference has invalid format: '%s'", name);
memcpy(entry->name, name, len);
entry->flag = flag;
entry->next = list;
* when doing a full libification.
*/
static struct cached_refs {
+ struct cached_refs *next;
char did_loose;
char did_packed;
struct ref_list *loose;
struct ref_list *packed;
-} cached_refs, submodule_refs;
+ /* The submodule name, or "" for the main repo. */
+ char name[FLEX_ARRAY];
+} *cached_refs;
+
static struct ref_list *current_ref;
static struct ref_list *extra_refs;
}
}
-static void invalidate_cached_refs(void)
+static void clear_cached_refs(struct cached_refs *ca)
{
- struct cached_refs *ca = &cached_refs;
-
if (ca->did_loose && ca->loose)
free_ref_list(ca->loose);
if (ca->did_packed && ca->packed)
ca->did_loose = ca->did_packed = 0;
}
-static void read_packed_refs(FILE *f, struct cached_refs *cached_refs)
+static struct cached_refs *create_cached_refs(const char *submodule)
+{
+ int len;
+ struct cached_refs *refs;
+ if (!submodule)
+ submodule = "";
+ len = strlen(submodule) + 1;
+ refs = xmalloc(sizeof(struct cached_refs) + len);
+ refs->next = NULL;
+ refs->did_loose = refs->did_packed = 0;
+ refs->loose = refs->packed = NULL;
+ memcpy(refs->name, submodule, len);
+ return refs;
+}
+
+/*
+ * Return a pointer to a cached_refs for the specified submodule. For
+ * the main repository, use submodule==NULL. The returned structure
+ * will be allocated and initialized but not necessarily populated; it
+ * should not be freed.
+ */
+static struct cached_refs *get_cached_refs(const char *submodule)
+{
+ struct cached_refs *refs = cached_refs;
+ if (!submodule)
+ submodule = "";
+ while (refs) {
+ if (!strcmp(submodule, refs->name))
+ return refs;
+ refs = refs->next;
+ }
+
+ refs = create_cached_refs(submodule);
+ refs->next = cached_refs;
+ cached_refs = refs;
+ return refs;
+}
+
+static void invalidate_cached_refs(void)
+{
+ struct cached_refs *refs = cached_refs;
+ while (refs) {
+ clear_cached_refs(refs);
+ refs = refs->next;
+ }
+}
+
+static struct ref_list *read_packed_refs(FILE *f)
{
struct ref_list *list = NULL;
struct ref_list *last = NULL;
!get_sha1_hex(refline + 1, sha1))
hashcpy(last->peeled, sha1);
}
- cached_refs->packed = sort_ref_list(list);
+ return sort_ref_list(list);
}
void add_extra_ref(const char *name, const unsigned char *sha1, int flag)
static struct ref_list *get_packed_refs(const char *submodule)
{
- const char *packed_refs_file;
- struct cached_refs *refs;
+ struct cached_refs *refs = get_cached_refs(submodule);
- if (submodule) {
- packed_refs_file = git_path_submodule(submodule, "packed-refs");
- refs = &submodule_refs;
- free_ref_list(refs->packed);
- } else {
- packed_refs_file = git_path("packed-refs");
- refs = &cached_refs;
- }
+ if (!refs->did_packed) {
+ const char *packed_refs_file;
+ FILE *f;
- if (!refs->did_packed || submodule) {
- FILE *f = fopen(packed_refs_file, "r");
+ if (submodule)
+ packed_refs_file = git_path_submodule(submodule, "packed-refs");
+ else
+ packed_refs_file = git_path("packed-refs");
+ f = fopen(packed_refs_file, "r");
refs->packed = NULL;
if (f) {
- read_packed_refs(f, refs);
+ refs->packed = read_packed_refs(f);
fclose(f);
}
refs->did_packed = 1;
static struct ref_list *get_loose_refs(const char *submodule)
{
- if (submodule) {
- free_ref_list(submodule_refs.loose);
- submodule_refs.loose = get_ref_dir(submodule, "refs", NULL);
- return submodule_refs.loose;
- }
+ struct cached_refs *refs = get_cached_refs(submodule);
- if (!cached_refs.did_loose) {
- cached_refs.loose = get_ref_dir(NULL, "refs", NULL);
- cached_refs.did_loose = 1;
+ if (!refs->did_loose) {
+ refs->loose = get_ref_dir(submodule, "refs", NULL);
+ refs->did_loose = 1;
}
- return cached_refs.loose;
+ return refs->loose;
}
/* We allow "recursive" symbolic refs. Only within reason, though */
static int resolve_gitlink_packed_ref(char *name, int pathlen, const char *refname, unsigned char *result)
{
FILE *f;
- struct cached_refs refs;
+ struct ref_list *packed_refs;
struct ref_list *ref;
int retval;
f = fopen(name, "r");
if (!f)
return -1;
- read_packed_refs(f, &refs);
+ packed_refs = read_packed_refs(f);
fclose(f);
- ref = refs.packed;
+ ref = packed_refs;
retval = -1;
while (ref) {
if (!strcmp(ref->name, refname)) {
}
ref = ref->next;
}
- free_ref_list(refs.packed);
+ free_ref_list(packed_refs);
return retval;
}
}
/*
- * If the "reading" argument is set, this function finds out what _object_
- * the ref points at by "reading" the ref. The ref, if it is not symbolic,
- * has to exist, and if it is symbolic, it has to point at an existing ref,
- * because the "read" goes through the symref to the ref it points at.
- *
- * The access that is not "reading" may often be "writing", but does not
- * have to; it can be merely checking _where it leads to_. If it is a
- * prelude to "writing" to the ref, a write to a symref that points at
- * yet-to-be-born ref will create the real ref pointed by the symref.
- * reading=0 allows the caller to check where such a symref leads to.
+ * Try to read ref from the packed references. On success, set sha1
+ * and return 0; otherwise, return -1.
*/
+ static int get_packed_ref(const char *ref, unsigned char *sha1)
+ {
+ struct ref_list *list = get_packed_refs(NULL);
+ while (list) {
+ if (!strcmp(ref, list->name)) {
+ hashcpy(sha1, list->sha1);
+ return 0;
+ }
+ list = list->next;
+ }
+ return -1;
+ }
+
const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
{
int depth = MAXDEPTH;
ssize_t len;
char buffer[256];
static char ref_buffer[256];
+ char path[PATH_MAX];
if (flag)
*flag = 0;
+ if (check_refname_format(ref, REFNAME_ALLOW_ONELEVEL))
+ return NULL;
+
for (;;) {
- char path[PATH_MAX];
struct stat st;
char *buf;
int fd;
return NULL;
git_snpath(path, sizeof(path), "%s", ref);
- /* Special case: non-existing file. */
+
if (lstat(path, &st) < 0) {
- struct ref_list *list = get_packed_refs(NULL);
- while (list) {
- if (!strcmp(ref, list->name)) {
- hashcpy(sha1, list->sha1);
- if (flag)
- *flag |= REF_ISPACKED;
- return ref;
- }
- list = list->next;
+ if (errno != ENOENT)
+ return NULL;
+ /*
+ * The loose reference file does not exist;
+ * check for a packed reference.
+ */
+ if (!get_packed_ref(ref, sha1)) {
+ if (flag)
+ *flag |= REF_ISPACKED;
+ return ref;
}
- if (reading || errno != ENOENT)
+ /* The reference is not a packed reference, either. */
+ if (reading) {
return NULL;
- hashclr(sha1);
- return ref;
+ } else {
+ hashclr(sha1);
+ return ref;
+ }
}
/* Follow "normalized" - ie "refs/.." symlinks by hand */
if (S_ISLNK(st.st_mode)) {
len = readlink(path, buffer, sizeof(buffer)-1);
- if (len >= 5 && !memcmp("refs/", buffer, 5)) {
- buffer[len] = 0;
+ if (len < 0)
+ return NULL;
+ buffer[len] = 0;
+ if (!prefixcmp(buffer, "refs/") &&
+ !check_refname_format(buffer, 0)) {
strcpy(ref_buffer, buffer);
ref = ref_buffer;
if (flag)
return NULL;
len = read_in_full(fd, buffer, sizeof(buffer)-1);
close(fd);
+ if (len < 0)
+ return NULL;
+ while (len && isspace(buffer[len-1]))
+ len--;
+ buffer[len] = '\0';
/*
* Is it a symbolic ref?
*/
- if (len < 4 || memcmp("ref:", buffer, 4))
+ if (prefixcmp(buffer, "ref:"))
break;
buf = buffer + 4;
- len -= 4;
- while (len && isspace(*buf))
- buf++, len--;
- while (len && isspace(buf[len-1]))
- len--;
- buf[len] = 0;
- memcpy(ref_buffer, buf, len + 1);
- ref = ref_buffer;
+ while (isspace(*buf))
+ buf++;
+ if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
+ warning("symbolic reference in %s is formatted incorrectly",
+ path);
+ return NULL;
+ }
+ ref = strcpy(ref_buffer, buf);
if (flag)
*flag |= REF_ISSYMREF;
}
- if (len < 40 || get_sha1_hex(buffer, sha1))
+ /* Please note that FETCH_HEAD has a second line containing other data. */
+ if (get_sha1_hex(buffer, sha1) || (buffer[40] != '\0' && !isspace(buffer[40]))) {
+ warning("reference in %s is formatted incorrectly", path);
return NULL;
+ }
return ref;
}
* - it contains a "\" (backslash)
*/
+ /* Return true iff ch is not allowed in reference names. */
static inline int bad_ref_char(int ch)
{
if (((unsigned) ch) <= ' ' || ch == 0x7f ||
ch == '~' || ch == '^' || ch == ':' || ch == '\\')
return 1;
/* 2.13 Pattern Matching Notation */
- if (ch == '?' || ch == '[') /* Unsupported */
+ if (ch == '*' || ch == '?' || ch == '[') /* Unsupported */
return 1;
- if (ch == '*') /* Supported at the end */
- return 2;
return 0;
}
- int check_ref_format(const char *ref)
+ /*
+ * Try to read one refname component from the front of ref. Return
+ * the length of the component found, or -1 if the component is not
+ * legal.
+ */
+ static int check_refname_component(const char *ref, int flags)
{
- int ch, level, bad_type, last;
- int ret = CHECK_REF_FORMAT_OK;
- const char *cp = ref;
-
- level = 0;
- while (1) {
- while ((ch = *cp++) == '/')
- ; /* tolerate duplicated slashes */
- if (!ch)
- /* should not end with slashes */
- return CHECK_REF_FORMAT_ERROR;
-
- /* we are at the beginning of the path component */
- if (ch == '.')
- return CHECK_REF_FORMAT_ERROR;
- bad_type = bad_ref_char(ch);
- if (bad_type) {
- if (bad_type == 2 && (!*cp || *cp == '/') &&
- ret == CHECK_REF_FORMAT_OK)
- ret = CHECK_REF_FORMAT_WILDCARD;
- else
- return CHECK_REF_FORMAT_ERROR;
- }
+ const char *cp;
+ char last = '\0';
+ for (cp = ref; ; cp++) {
+ char ch = *cp;
+ if (ch == '\0' || ch == '/')
+ break;
+ if (bad_ref_char(ch))
+ return -1; /* Illegal character in refname. */
+ if (last == '.' && ch == '.')
+ return -1; /* Refname contains "..". */
+ if (last == '@' && ch == '{')
+ return -1; /* Refname contains "@{". */
last = ch;
- /* scan the rest of the path component */
- while ((ch = *cp++) != 0) {
- bad_type = bad_ref_char(ch);
- if (bad_type)
- return CHECK_REF_FORMAT_ERROR;
- if (ch == '/')
- break;
- if (last == '.' && ch == '.')
- return CHECK_REF_FORMAT_ERROR;
- if (last == '@' && ch == '{')
- return CHECK_REF_FORMAT_ERROR;
- last = ch;
- }
- level++;
- if (!ch) {
- if (ref <= cp - 2 && cp[-2] == '.')
- return CHECK_REF_FORMAT_ERROR;
- if (level < 2)
- return CHECK_REF_FORMAT_ONELEVEL;
- if (has_extension(ref, ".lock"))
- return CHECK_REF_FORMAT_ERROR;
- return ret;
+ }
+ if (cp == ref)
+ return -1; /* Component has zero length. */
+ if (ref[0] == '.') {
+ if (!(flags & REFNAME_DOT_COMPONENT))
+ return -1; /* Component starts with '.'. */
+ /*
+ * Even if leading dots are allowed, don't allow "."
+ * as a component (".." is prevented by a rule above).
+ */
+ if (ref[1] == '\0')
+ return -1; /* Component equals ".". */
+ }
+ if (cp - ref >= 5 && !memcmp(cp - 5, ".lock", 5))
+ return -1; /* Refname ends with ".lock". */
+ return cp - ref;
+ }
+
+ int check_refname_format(const char *ref, int flags)
+ {
+ int component_len, component_count = 0;
+
+ while (1) {
+ /* We are at the start of a path component. */
+ component_len = check_refname_component(ref, flags);
+ if (component_len < 0) {
+ if ((flags & REFNAME_REFSPEC_PATTERN) &&
+ ref[0] == '*' &&
+ (ref[1] == '\0' || ref[1] == '/')) {
+ /* Accept one wildcard as a full refname component. */
+ flags &= ~REFNAME_REFSPEC_PATTERN;
+ component_len = 1;
+ } else {
+ return -1;
+ }
}
+ component_count++;
+ if (ref[component_len] == '\0')
+ break;
+ /* Skip to next component. */
+ ref += component_len + 1;
}
+
+ if (ref[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;
}
const char *prettify_refname(const char *name)
struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)
{
char refpath[PATH_MAX];
- if (check_ref_format(ref))
+ if (check_refname_format(ref, 0))
return NULL;
strcpy(refpath, mkpath("refs/%s", ref));
return lock_ref_sha1_basic(refpath, old_sha1, 0, NULL);
struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags)
{
- switch (check_ref_format(ref)) {
- default:
+ if (check_refname_format(ref, REFNAME_ALLOW_ONELEVEL))
return NULL;
- case 0:
- case CHECK_REF_FORMAT_ONELEVEL:
- return lock_ref_sha1_basic(ref, old_sha1, flags, NULL);
- }
+ return lock_ref_sha1_basic(ref, old_sha1, flags, NULL);
}
static struct lock_file packlock;
{
if (name && !namelen)
namelen = strlen(name);
- if (!o) {
- unsigned char sha1[20];
- if (get_sha1_1(name, namelen, sha1))
- return NULL;
- o = parse_object(sha1);
- }
while (1) {
if (!o || (!o->parsed && !parse_object(o->sha1)))
return NULL;
{
strbuf_branchname(sb, name);
if (name[0] == '-')
- return CHECK_REF_FORMAT_ERROR;
+ return -1;
strbuf_splice(sb, 0, 0, "refs/heads/", 11);
- return check_ref_format(sb->buf);
+ return check_refname_format(sb->buf, 0);
}
/*
int nr_heads, struct ref **to_fetch)
{
struct bundle_transport_data *data = transport->data;
- return unbundle(&data->header, data->fd);
+ return unbundle(&data->header, data->fd,
+ transport->progress ? BUNDLE_VERBOSE : 0);
}
static int close_bundle(struct transport *transport)
continue;
remote = remote ? (remote + 1) : local;
- switch (check_ref_format(remote)) {
- case 0: /* ok */
- case CHECK_REF_FORMAT_ONELEVEL:
- /* ok but a single level -- that is fine for
- * a match pattern.
- */
- case CHECK_REF_FORMAT_WILDCARD:
- /* ok but ends with a pattern-match character */
- continue;
- }
- die("remote part of refspec is not a valid name in %s",
- heads[i]);
+ if (check_refname_format(remote,
+ REFNAME_ALLOW_ONELEVEL|REFNAME_REFSPEC_PATTERN))
+ die("remote part of refspec is not a valid name in %s",
+ heads[i]);
}
}