if (kind == REF_LOCAL_BRANCH) {
struct branch *branch = branch_get(name);
+ const char *upstream = branch_get_upstream(branch, NULL);
unsigned char sha1[20];
- if (branch &&
- branch->merge &&
- branch->merge[0] &&
- branch->merge[0]->dst &&
+ if (upstream &&
(reference_name = reference_name_to_free =
- resolve_refdup(branch->merge[0]->dst, RESOLVE_REF_READING,
+ resolve_refdup(upstream, RESOLVE_REF_READING,
sha1, NULL)) != NULL)
reference_rev = lookup_commit_reference(sha1);
}
return 0;
}
- static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+ static int append_ref(const char *refname, const struct object_id *oid, int flags, void *cb_data)
{
struct append_ref_cb *cb = (struct append_ref_cb *)(cb_data);
struct ref_list *ref_list = cb->ref_list;
commit = NULL;
if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
- commit = lookup_commit_reference_gently(sha1, 1);
+ commit = lookup_commit_reference_gently(oid->hash, 1);
if (!commit) {
cb->ret = error(_("branch '%s' does not point at a commit"), refname);
return 0;
int ours, theirs;
char *ref = NULL;
struct branch *branch = branch_get(branch_name);
+ const char *upstream;
struct strbuf fancy = STRBUF_INIT;
int upstream_is_gone = 0;
int added_decoration = 1;
- switch (stat_tracking_info(branch, &ours, &theirs)) {
- case 0:
- /* no base */
- return;
- case -1:
- /* with "gone" base */
+ if (stat_tracking_info(branch, &ours, &theirs, &upstream) < 0) {
+ if (!upstream)
+ return;
upstream_is_gone = 1;
- break;
- default:
- /* with base */
- break;
}
if (show_upstream_ref) {
- ref = shorten_unambiguous_ref(branch->merge[0]->dst, 0);
+ ref = shorten_unambiguous_ref(upstream, 0);
if (want_color(branch_use_color))
strbuf_addf(&fancy, "%s%s%s",
branch_get_color(BRANCH_COLOR_UPSTREAM),
{ "contents:body" },
{ "contents:signature" },
{ "upstream" },
+ { "push" },
{ "symref" },
{ "flag" },
{ "HEAD" },
else if (starts_with(name, "symref"))
refname = ref->symref ? ref->symref : "";
else if (starts_with(name, "upstream")) {
+ const char *branch_name;
/* only local branches may have an upstream */
- if (!starts_with(ref->refname, "refs/heads/"))
+ if (!skip_prefix(ref->refname, "refs/heads/",
+ &branch_name))
continue;
- branch = branch_get(ref->refname + 11);
+ branch = branch_get(branch_name);
- if (!branch || !branch->merge || !branch->merge[0] ||
- !branch->merge[0]->dst)
+ refname = branch_get_upstream(branch, NULL);
+ if (!refname)
+ continue;
+ } else if (starts_with(name, "push")) {
+ const char *branch_name;
+ if (!skip_prefix(ref->refname, "refs/heads/",
+ &branch_name))
+ continue;
+ branch = branch_get(branch_name);
+
+ refname = branch_get_push(branch, NULL);
+ if (!refname)
continue;
- refname = branch->merge[0]->dst;
} else if (starts_with(name, "color:")) {
char color[COLOR_MAXLEN] = "";
refname = shorten_unambiguous_ref(refname,
warn_ambiguous_refs);
else if (!strcmp(formatp, "track") &&
- starts_with(name, "upstream")) {
+ (starts_with(name, "upstream") ||
+ starts_with(name, "push"))) {
char buf[40];
if (stat_tracking_info(branch, &num_ours,
- &num_theirs) != 1)
+ &num_theirs, NULL))
continue;
if (!num_ours && !num_theirs)
}
continue;
} else if (!strcmp(formatp, "trackshort") &&
- starts_with(name, "upstream")) {
+ (starts_with(name, "upstream") ||
+ starts_with(name, "push"))) {
assert(branch);
if (stat_tracking_info(branch, &num_ours,
- &num_theirs) != 1)
+ &num_theirs, NULL))
continue;
if (!num_ours && !num_theirs)
* A call-back given to for_each_ref(). Filter refs and keep them for
* later object processing.
*/
- static int grab_single_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+ static int grab_single_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
struct grab_ref_cbdata *cb = cb_data;
struct refinfo *ref;
*/
ref = xcalloc(1, sizeof(*ref));
ref->refname = xstrdup(refname);
- hashcpy(ref->objectname, sha1);
+ hashcpy(ref->objectname, oid->hash);
ref->flag = flag;
cnt = cb->grab_cnt;
#define MAX_IN_VAIN 256
static struct prio_queue rev_list = { compare_commits_by_commit_date };
-static int non_common_revs, multi_ack, use_sideband, allow_tip_sha1_in_want;
+static int non_common_revs, multi_ack, use_sideband;
+/* Allow specifying sha1 if it is a ref tip. */
+#define ALLOW_TIP_SHA1 01
+/* Allow request of a sha1 if it is reachable from a ref (possibly hidden ref). */
+#define ALLOW_REACHABLE_SHA1 02
+static unsigned int allow_unadvertised_object_request;
static void rev_list_push(struct commit *commit, int mark)
{
}
}
- static int rev_list_insert_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+ static int rev_list_insert_ref(const char *refname, const unsigned char *sha1)
{
struct object *o = deref_tag(parse_object(sha1), refname, 0);
return 0;
}
- static int clear_marks(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+ static int rev_list_insert_ref_oid(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
- struct object *o = deref_tag(parse_object(sha1), refname, 0);
+ return rev_list_insert_ref(refname, oid->hash);
+ }
+
+ static int clear_marks(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
+ {
+ struct object *o = deref_tag(parse_object(oid->hash), refname, 0);
if (o && o->type == OBJ_COMMIT)
clear_commit_marks((struct commit *)o,
static void insert_one_alternate_ref(const struct ref *ref, void *unused)
{
- rev_list_insert_ref(NULL, ref->old_sha1, 0, NULL);
+ rev_list_insert_ref(NULL, ref->old_sha1);
}
#define INITIAL_FLUSH 16
for_each_ref(clear_marks, NULL);
marked = 1;
- for_each_ref(rev_list_insert_ref, NULL);
+ for_each_ref(rev_list_insert_ref_oid, NULL);
for_each_alternate_ref(insert_one_alternate_ref, NULL);
fetching = 0;
static struct commit_list *complete;
- static int mark_complete(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+ static int mark_complete(const unsigned char *sha1)
{
struct object *o = parse_object(sha1);
return 0;
}
+ static int mark_complete_oid(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
+ {
+ return mark_complete(oid->hash);
+ }
+
static void mark_recent_complete_commits(struct fetch_pack_args *args,
unsigned long cutoff)
{
}
/* Append unmatched requests to the list */
- if (allow_tip_sha1_in_want) {
+ if ((allow_unadvertised_object_request &
+ (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) {
for (i = 0; i < nr_sought; i++) {
unsigned char sha1[20];
static void mark_alternate_complete(const struct ref *ref, void *unused)
{
- mark_complete(NULL, ref->old_sha1, 0, NULL);
+ mark_complete(ref->old_sha1);
}
static int everything_local(struct fetch_pack_args *args,
}
if (!args->depth) {
- for_each_ref(mark_complete, NULL);
+ for_each_ref(mark_complete_oid, NULL);
for_each_alternate_ref(mark_alternate_complete, NULL);
commit_list_sort_by_date(&complete);
if (cutoff)
if (server_supports("allow-tip-sha1-in-want")) {
if (args->verbose)
fprintf(stderr, "Server supports allow-tip-sha1-in-want\n");
- allow_tip_sha1_in_want = 1;
+ allow_unadvertised_object_request |= ALLOW_TIP_SHA1;
+ }
+ if (server_supports("allow-reachable-sha1-in-want")) {
+ if (args->verbose)
+ fprintf(stderr, "Server supports allow-reachable-sha1-in-want\n");
+ allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;
}
if (!server_supports("thin-pack"))
args->use_thin_pack = 0;
}
}
+static int cmd_group_cmp(const void *elem1, const void *elem2)
+{
+ const struct cmdname_help *e1 = elem1;
+ const struct cmdname_help *e2 = elem2;
+
+ if (e1->group < e2->group)
+ return -1;
+ if (e1->group > e2->group)
+ return 1;
+ return strcmp(e1->name, e2->name);
+}
+
void list_common_cmds_help(void)
{
int i, longest = 0;
+ int current_grp = -1;
for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
if (longest < strlen(common_cmds[i].name))
longest = strlen(common_cmds[i].name);
}
- puts(_("The most commonly used git commands are:"));
+ qsort(common_cmds, ARRAY_SIZE(common_cmds),
+ sizeof(common_cmds[0]), cmd_group_cmp);
+
+ puts(_("These are common Git commands used in various situations:"));
+
for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+ if (common_cmds[i].group != current_grp) {
+ printf("\n%s\n", _(common_cmd_groups[common_cmds[i].group]));
+ current_grp = common_cmds[i].group;
+ }
+
printf(" %s ", common_cmds[i].name);
mput_char(' ', longest - strlen(common_cmds[i].name));
puts(_(common_cmds[i].help));
struct string_list *similar_refs;
};
- static int append_similar_ref(const char *refname, const unsigned char *sha1,
+ static int append_similar_ref(const char *refname, const struct object_id *oid,
int flags, void *cb_data)
{
struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
static const char content_length[] = "Content-Length";
static const char last_modified[] = "Last-Modified";
static int getanyfile = 1;
+static unsigned long max_request_buffer = 10 * 1024 * 1024;
static struct string_list *query_params;
struct rpc_service {
const char *name;
const char *config_name;
+ unsigned buffer_input : 1;
signed enabled : 2;
};
static struct rpc_service rpc_service[] = {
- { "upload-pack", "uploadpack", 1 },
- { "receive-pack", "receivepack", -1 },
+ { "upload-pack", "uploadpack", 1, 1 },
+ { "receive-pack", "receivepack", 0, -1 },
};
static struct string_list *get_parameters(void)
struct strbuf var = STRBUF_INIT;
git_config_get_bool("http.getanyfile", &getanyfile);
+ git_config_get_ulong("http.maxrequestbuffer", &max_request_buffer);
for (i = 0; i < ARRAY_SIZE(rpc_service); i++) {
struct rpc_service *svc = &rpc_service[i];
return svc;
}
-static void inflate_request(const char *prog_name, int out)
+/*
+ * This is basically strbuf_read(), except that if we
+ * hit max_request_buffer we die (we'd rather reject a
+ * maliciously large request than chew up infinite memory).
+ */
+static ssize_t read_request(int fd, unsigned char **out)
+{
+ size_t len = 0, alloc = 8192;
+ unsigned char *buf = xmalloc(alloc);
+
+ if (max_request_buffer < alloc)
+ max_request_buffer = alloc;
+
+ while (1) {
+ ssize_t cnt;
+
+ cnt = read_in_full(fd, buf + len, alloc - len);
+ if (cnt < 0) {
+ free(buf);
+ return -1;
+ }
+
+ /* partial read from read_in_full means we hit EOF */
+ len += cnt;
+ if (len < alloc) {
+ *out = buf;
+ return len;
+ }
+
+ /* otherwise, grow and try again (if we can) */
+ if (alloc == max_request_buffer)
+ die("request was larger than our maximum size (%lu);"
+ " try setting GIT_HTTP_MAX_REQUEST_BUFFER",
+ max_request_buffer);
+
+ alloc = alloc_nr(alloc);
+ if (alloc > max_request_buffer)
+ alloc = max_request_buffer;
+ REALLOC_ARRAY(buf, alloc);
+ }
+}
+
+static void inflate_request(const char *prog_name, int out, int buffer_input)
{
git_zstream stream;
+ unsigned char *full_request = NULL;
unsigned char in_buf[8192];
unsigned char out_buf[8192];
unsigned long cnt = 0;
git_inflate_init_gzip_only(&stream);
while (1) {
- ssize_t n = xread(0, in_buf, sizeof(in_buf));
+ ssize_t n;
+
+ if (buffer_input) {
+ if (full_request)
+ n = 0; /* nothing left to read */
+ else
+ n = read_request(0, &full_request);
+ stream.next_in = full_request;
+ } else {
+ n = xread(0, in_buf, sizeof(in_buf));
+ stream.next_in = in_buf;
+ }
+
if (n <= 0)
die("request ended in the middle of the gzip stream");
-
- stream.next_in = in_buf;
stream.avail_in = n;
while (0 < stream.avail_in) {
done:
git_inflate_end(&stream);
close(out);
+ free(full_request);
+}
+
+static void copy_request(const char *prog_name, int out)
+{
+ unsigned char *buf;
+ ssize_t n = read_request(0, &buf);
+ if (n < 0)
+ die_errno("error reading request body");
+ if (write_in_full(out, buf, n) != n)
+ die("%s aborted reading request", prog_name);
+ close(out);
+ free(buf);
}
-static void run_service(const char **argv)
+static void run_service(const char **argv, int buffer_input)
{
const char *encoding = getenv("HTTP_CONTENT_ENCODING");
const char *user = getenv("REMOTE_USER");
"GIT_COMMITTER_EMAIL=%s@http.%s", user, host);
cld.argv = argv;
- if (gzipped_request)
+ if (buffer_input || gzipped_request)
cld.in = -1;
cld.git_cmd = 1;
if (start_command(&cld))
close(1);
if (gzipped_request)
- inflate_request(argv[0], cld.in);
+ inflate_request(argv[0], cld.in, buffer_input);
+ else if (buffer_input)
+ copy_request(argv[0], cld.in);
else
close(0);
exit(1);
}
- static int show_text_ref(const char *name, const unsigned char *sha1,
- int flag, void *cb_data)
+ static int show_text_ref(const char *name, const struct object_id *oid,
+ int flag, void *cb_data)
{
const char *name_nons = strip_namespace(name);
struct strbuf *buf = cb_data;
- struct object *o = parse_object(sha1);
+ struct object *o = parse_object(oid->hash);
if (!o)
return 0;
- strbuf_addf(buf, "%s\t%s\n", sha1_to_hex(sha1), name_nons);
+ strbuf_addf(buf, "%s\t%s\n", oid_to_hex(oid), name_nons);
if (o->type == OBJ_TAG) {
o = deref_tag(o, name, 0);
if (!o)
packet_flush(1);
argv[0] = svc->name;
- run_service(argv);
+ run_service(argv, 0);
} else {
select_getanyfile();
strbuf_release(&buf);
}
- static int show_head_ref(const char *refname, const unsigned char *sha1,
- int flag, void *cb_data)
+ static int show_head_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
struct strbuf *buf = cb_data;
if (flag & REF_ISSYMREF) {
- unsigned char unused[20];
+ struct object_id unused;
const char *target = resolve_ref_unsafe(refname,
RESOLVE_REF_READING,
- unused, NULL);
+ unused.hash, NULL);
const char *target_nons = strip_namespace(target);
strbuf_addf(buf, "ref: %s\n", target_nons);
} else {
- strbuf_addf(buf, "%s\n", sha1_to_hex(sha1));
+ strbuf_addf(buf, "%s\n", oid_to_hex(oid));
}
return 0;
end_headers();
argv[0] = svc->name;
- run_service(argv);
+ run_service(argv, svc->buffer_input);
strbuf_release(&buf);
}
+static int dead;
static NORETURN void die_webcgi(const char *err, va_list params)
{
- static int dead;
+ if (dead <= 1) {
+ vreportf("fatal: ", err, params);
- if (!dead) {
- dead = 1;
http_status(500, "Internal Server Error");
hdr_nocache();
end_headers();
-
- vreportf("fatal: ", err, params);
}
exit(0); /* we successfully reported a failure ;-) */
}
+static int die_webcgi_recursing(void)
+{
+ return dead++ > 1;
+}
+
static char* getdir(void)
{
struct strbuf buf = STRBUF_INIT;
git_extract_argv0_path(argv[0]);
set_die_routine(die_webcgi);
+ set_die_is_recursing_routine(die_webcgi_recursing);
if (!method)
die("No REQUEST_METHOD from server");
not_found("Repository not exported: '%s'", dir);
http_config();
+ max_request_buffer = git_env_ulong("GIT_HTTP_MAX_REQUEST_BUFFER",
+ max_request_buffer);
+
cmd->imp(cmd_arg);
return 0;
}
static int branches_nr;
static struct branch *current_branch;
-static const char *default_remote_name;
-static const char *branch_pushremote_name;
static const char *pushremote_name;
-static int explicit_default_remote_name;
static struct rewrites rewrites;
static struct rewrites rewrites_push;
return 0;
branch = make_branch(name, subkey - name);
if (!strcmp(subkey, ".remote")) {
- if (git_config_string(&branch->remote_name, key, value))
- return -1;
- if (branch == current_branch) {
- default_remote_name = branch->remote_name;
- explicit_default_remote_name = 1;
- }
+ return git_config_string(&branch->remote_name, key, value);
} else if (!strcmp(subkey, ".pushremote")) {
- if (branch == current_branch)
- if (git_config_string(&branch_pushremote_name, key, value))
- return -1;
+ return git_config_string(&branch->pushremote_name, key, value);
} else if (!strcmp(subkey, ".merge")) {
if (!value)
return config_error_nonbool(key);
static void read_config(void)
{
+ static int loaded;
unsigned char sha1[20];
const char *head_ref;
int flag;
- if (default_remote_name) /* did this already */
+
+ if (loaded)
return;
- default_remote_name = "origin";
+ loaded = 1;
+
current_branch = NULL;
head_ref = resolve_ref_unsafe("HEAD", 0, sha1, &flag);
if (head_ref && (flag & REF_ISSYMREF) &&
current_branch = make_branch(head_ref, 0);
}
git_config(handle_config, NULL);
- if (branch_pushremote_name) {
- free((char *)pushremote_name);
- pushremote_name = branch_pushremote_name;
- }
alias_all_urls();
}
return !strchr(name, '/'); /* no slash */
}
-static struct remote *remote_get_1(const char *name, const char *pushremote_name)
+const char *remote_for_branch(struct branch *branch, int *explicit)
+{
+ if (branch && branch->remote_name) {
+ if (explicit)
+ *explicit = 1;
+ return branch->remote_name;
+ }
+ if (explicit)
+ *explicit = 0;
+ return "origin";
+}
+
+const char *pushremote_for_branch(struct branch *branch, int *explicit)
+{
+ if (branch && branch->pushremote_name) {
+ if (explicit)
+ *explicit = 1;
+ return branch->pushremote_name;
+ }
+ if (pushremote_name) {
+ if (explicit)
+ *explicit = 1;
+ return pushremote_name;
+ }
+ return remote_for_branch(branch, explicit);
+}
+
+static struct remote *remote_get_1(const char *name,
+ const char *(*get_default)(struct branch *, int *))
{
struct remote *ret;
int name_given = 0;
+ read_config();
+
if (name)
name_given = 1;
- else {
- if (pushremote_name) {
- name = pushremote_name;
- name_given = 1;
- } else {
- name = default_remote_name;
- name_given = explicit_default_remote_name;
- }
- }
+ else
+ name = get_default(current_branch, &name_given);
ret = make_remote(name, 0);
if (valid_remote_nick(name)) {
struct remote *remote_get(const char *name)
{
- read_config();
- return remote_get_1(name, NULL);
+ return remote_get_1(name, remote_for_branch);
}
struct remote *pushremote_get(const char *name)
{
- read_config();
- return remote_get_1(name, pushremote_name);
+ return remote_get_1(name, pushremote_for_branch);
}
int remote_is_configured(const char *name)
static void set_merge(struct branch *ret)
{
+ struct remote *remote;
char *ref;
unsigned char sha1[20];
int i;
+ if (!ret)
+ return; /* no branch */
+ if (ret->merge)
+ return; /* already run */
+ if (!ret->remote_name || !ret->merge_nr) {
+ /*
+ * no merge config; let's make sure we don't confuse callers
+ * with a non-zero merge_nr but a NULL merge
+ */
+ ret->merge_nr = 0;
+ return;
+ }
+
+ remote = remote_get(ret->remote_name);
+
ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge));
for (i = 0; i < ret->merge_nr; i++) {
ret->merge[i] = xcalloc(1, sizeof(**ret->merge));
ret->merge[i]->src = xstrdup(ret->merge_name[i]);
- if (!remote_find_tracking(ret->remote, ret->merge[i]) ||
+ if (!remote_find_tracking(remote, ret->merge[i]) ||
strcmp(ret->remote_name, "."))
continue;
if (dwim_ref(ret->merge_name[i], strlen(ret->merge_name[i]),
ret = current_branch;
else
ret = make_branch(name, 0);
- if (ret && ret->remote_name) {
- ret->remote = remote_get(ret->remote_name);
- if (ret->merge_nr)
- set_merge(ret);
- }
+ set_merge(ret);
return ret;
}
return refname_match(branch->merge[i]->src, refname);
}
+__attribute((format (printf,2,3)))
+static const char *error_buf(struct strbuf *err, const char *fmt, ...)
+{
+ if (err) {
+ va_list ap;
+ va_start(ap, fmt);
+ strbuf_vaddf(err, fmt, ap);
+ va_end(ap);
+ }
+ return NULL;
+}
+
+const char *branch_get_upstream(struct branch *branch, struct strbuf *err)
+{
+ if (!branch)
+ return error_buf(err, _("HEAD does not point to a branch"));
+
+ if (!branch->merge || !branch->merge[0]) {
+ /*
+ * no merge config; is it because the user didn't define any,
+ * or because it is not a real branch, and get_branch
+ * auto-vivified it?
+ */
+ if (!ref_exists(branch->refname))
+ return error_buf(err, _("no such branch: '%s'"),
+ branch->name);
+ return error_buf(err,
+ _("no upstream configured for branch '%s'"),
+ branch->name);
+ }
+
+ if (!branch->merge[0]->dst)
+ return error_buf(err,
+ _("upstream branch '%s' not stored as a remote-tracking branch"),
+ branch->merge[0]->src);
+
+ return branch->merge[0]->dst;
+}
+
+static const char *tracking_for_push_dest(struct remote *remote,
+ const char *refname,
+ struct strbuf *err)
+{
+ char *ret;
+
+ ret = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);
+ if (!ret)
+ return error_buf(err,
+ _("push destination '%s' on remote '%s' has no local tracking branch"),
+ refname, remote->name);
+ return ret;
+}
+
+static const char *branch_get_push_1(struct branch *branch, struct strbuf *err)
+{
+ struct remote *remote;
+
+ if (!branch)
+ return error_buf(err, _("HEAD does not point to a branch"));
+
+ remote = remote_get(pushremote_for_branch(branch, NULL));
+ if (!remote)
+ return error_buf(err,
+ _("branch '%s' has no remote for pushing"),
+ branch->name);
+
+ if (remote->push_refspec_nr) {
+ char *dst;
+ const char *ret;
+
+ dst = apply_refspecs(remote->push, remote->push_refspec_nr,
+ branch->refname);
+ if (!dst)
+ return error_buf(err,
+ _("push refspecs for '%s' do not include '%s'"),
+ remote->name, branch->name);
+
+ ret = tracking_for_push_dest(remote, dst, err);
+ free(dst);
+ return ret;
+ }
+
+ if (remote->mirror)
+ return tracking_for_push_dest(remote, branch->refname, err);
+
+ switch (push_default) {
+ case PUSH_DEFAULT_NOTHING:
+ return error_buf(err, _("push has no destination (push.default is 'nothing')"));
+
+ case PUSH_DEFAULT_MATCHING:
+ case PUSH_DEFAULT_CURRENT:
+ return tracking_for_push_dest(remote, branch->refname, err);
+
+ case PUSH_DEFAULT_UPSTREAM:
+ return branch_get_upstream(branch, err);
+
+ case PUSH_DEFAULT_UNSPECIFIED:
+ case PUSH_DEFAULT_SIMPLE:
+ {
+ const char *up, *cur;
+
+ up = branch_get_upstream(branch, err);
+ if (!up)
+ return NULL;
+ cur = tracking_for_push_dest(remote, branch->refname, err);
+ if (!cur)
+ return NULL;
+ if (strcmp(cur, up))
+ return error_buf(err,
+ _("cannot resolve 'simple' push to a single destination"));
+ return cur;
+ }
+ }
+
+ die("BUG: unhandled push situation");
+}
+
+const char *branch_get_push(struct branch *branch, struct strbuf *err)
+{
+ if (!branch->push_tracking_ref)
+ branch->push_tracking_ref = branch_get_push_1(branch, err);
+ return branch->push_tracking_ref;
+}
+
static int ignore_symref_update(const char *refname)
{
unsigned char sha1[20];
/*
* Compare a branch with its upstream, and save their differences (number
- * of commits) in *num_ours and *num_theirs.
+ * of commits) in *num_ours and *num_theirs. The name of the upstream branch
+ * (or NULL if no upstream is defined) is returned via *upstream_name, if it
+ * is not itself NULL.
*
- * Return 0 if branch has no upstream (no base), -1 if upstream is missing
- * (with "gone" base), otherwise 1 (with base).
+ * Returns -1 if num_ours and num_theirs could not be filled in (e.g., no
+ * upstream defined, or ref does not exist), 0 otherwise.
*/
-int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
+int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
+ const char **upstream_name)
{
unsigned char sha1[20];
struct commit *ours, *theirs;
int rev_argc;
/* Cannot stat unless we are marked to build on top of somebody else. */
- if (!branch ||
- !branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
- return 0;
+ base = branch_get_upstream(branch, NULL);
+ if (upstream_name)
+ *upstream_name = base;
+ if (!base)
+ return -1;
/* Cannot stat if what we used to build on no longer exists */
- base = branch->merge[0]->dst;
if (read_ref(base, sha1))
return -1;
theirs = lookup_commit_reference(sha1);
/* are we the same? */
if (theirs == ours) {
*num_theirs = *num_ours = 0;
- return 1;
+ return 0;
}
/* Run "rev-list --left-right ours...theirs" internally... */
/* clear object flags smudged by the above traversal */
clear_commit_marks(ours, ALL_REV_FLAGS);
clear_commit_marks(theirs, ALL_REV_FLAGS);
- return 1;
+ return 0;
}
/*
int format_tracking_info(struct branch *branch, struct strbuf *sb)
{
int ours, theirs;
+ const char *full_base;
char *base;
int upstream_is_gone = 0;
- switch (stat_tracking_info(branch, &ours, &theirs)) {
- case 0:
- /* no base */
- return 0;
- case -1:
- /* with "gone" base */
+ if (stat_tracking_info(branch, &ours, &theirs, &full_base) < 0) {
+ if (!full_base)
+ return 0;
upstream_is_gone = 1;
- break;
- default:
- /* with base */
- break;
}
- base = shorten_unambiguous_ref(branch->merge[0]->dst, 0);
+ base = shorten_unambiguous_ref(full_base, 0);
if (upstream_is_gone) {
strbuf_addf(sb,
_("Your branch is based on '%s', but the upstream is gone.\n"),
return 1;
}
- static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+ static int one_local_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
struct ref ***local_tail = cb_data;
struct ref *ref;
len = strlen(refname) + 1;
ref = xcalloc(1, sizeof(*ref) + len);
- hashcpy(ref->new_sha1, sha1);
+ hashcpy(ref->new_sha1, oid->hash);
memcpy(ref->name, refname, len);
**local_tail = ref;
*local_tail = &ref->next;
struct ref *get_local_heads(void)
{
struct ref *local_refs = NULL, **local_tail = &local_refs;
+
for_each_ref(one_local_ref, &local_tail);
return local_refs;
}
int ref_count;
};
- static int get_stale_heads_cb(const char *refname,
- const unsigned char *sha1, int flags, void *cb_data)
+ static int get_stale_heads_cb(const char *refname, const struct object_id *oid,
+ int flags, void *cb_data)
{
struct stale_heads_info *info = cb_data;
struct string_list matches = STRING_LIST_INIT_DUP;
if (stale) {
struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
- hashcpy(ref->new_sha1, sha1);
+ hashcpy(ref->new_sha1, oid->hash);
}
clean_exit:
struct ref *ref, *stale_refs = NULL;
struct string_list ref_names = STRING_LIST_INIT_NODUP;
struct stale_heads_info info;
+
info.ref_names = &ref_names;
info.stale_refs_tail = &stale_refs;
info.refs = refs;
#include "tree-walk.h"
#include "refs.h"
#include "remote.h"
+#include "dir.h"
static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
return slash;
}
-static inline int upstream_mark(const char *string, int len)
+static inline int at_mark(const char *string, int len,
+ const char **suffix, int nr)
{
- const char *suffix[] = { "@{upstream}", "@{u}" };
int i;
- for (i = 0; i < ARRAY_SIZE(suffix); i++) {
+ for (i = 0; i < nr; i++) {
int suffix_len = strlen(suffix[i]);
if (suffix_len <= len
&& !memcmp(string, suffix[i], suffix_len))
return 0;
}
+static inline int upstream_mark(const char *string, int len)
+{
+ const char *suffix[] = { "@{upstream}", "@{u}" };
+ return at_mark(string, len, suffix, ARRAY_SIZE(suffix));
+}
+
+static inline int push_mark(const char *string, int len)
+{
+ const char *suffix[] = { "@{push}" };
+ return at_mark(string, len, suffix, ARRAY_SIZE(suffix));
+}
+
static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags);
static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf);
nth_prior = 1;
continue;
}
- if (!upstream_mark(str + at, len - at)) {
+ if (!upstream_mark(str + at, len - at) &&
+ !push_mark(str + at, len - at)) {
reflog_len = (len-1) - (at+2);
len = at;
}
/* Remember to update object flag allocation in object.h */
#define ONELINE_SEEN (1u<<20)
- static int handle_one_ref(const char *path,
- const unsigned char *sha1, int flag, void *cb_data)
+ static int handle_one_ref(const char *path, const struct object_id *oid,
+ int flag, void *cb_data)
{
struct commit_list **list = cb_data;
- struct object *object = parse_object(sha1);
+ struct object *object = parse_object(oid->hash);
if (!object)
return 0;
if (object->type == OBJ_TAG) {
free(s);
}
-static const char *get_upstream_branch(const char *branch_buf, int len)
-{
- char *branch = xstrndup(branch_buf, len);
- struct branch *upstream = branch_get(*branch ? branch : NULL);
-
- /*
- * Upstream can be NULL only if branch refers to HEAD and HEAD
- * points to something different than a branch.
- */
- if (!upstream)
- die(_("HEAD does not point to a branch"));
- if (!upstream->merge || !upstream->merge[0]->dst) {
- if (!ref_exists(upstream->refname))
- die(_("No such branch: '%s'"), branch);
- if (!upstream->merge) {
- die(_("No upstream configured for branch '%s'"),
- upstream->name);
- }
- die(
- _("Upstream branch '%s' not stored as a remote-tracking branch"),
- upstream->merge[0]->src);
- }
- free(branch);
-
- return upstream->merge[0]->dst;
-}
-
-static int interpret_upstream_mark(const char *name, int namelen,
- int at, struct strbuf *buf)
+static int interpret_branch_mark(const char *name, int namelen,
+ int at, struct strbuf *buf,
+ int (*get_mark)(const char *, int),
+ const char *(*get_data)(struct branch *,
+ struct strbuf *))
{
int len;
+ struct branch *branch;
+ struct strbuf err = STRBUF_INIT;
+ const char *value;
- len = upstream_mark(name + at, namelen - at);
+ len = get_mark(name + at, namelen - at);
if (!len)
return -1;
if (memchr(name, ':', at))
return -1;
- set_shortened_ref(buf, get_upstream_branch(name, at));
+ if (at) {
+ char *name_str = xmemdupz(name, at);
+ branch = branch_get(name_str);
+ free(name_str);
+ } else
+ branch = branch_get(NULL);
+
+ value = get_data(branch, &err);
+ if (!value)
+ die("%s", err.buf);
+
+ set_shortened_ref(buf, value);
return len + at;
}
if (len > 0)
return reinterpret(name, namelen, len, buf);
- len = interpret_upstream_mark(name, namelen, at - name, buf);
+ len = interpret_branch_mark(name, namelen, at - name, buf,
+ upstream_mark, branch_get_upstream);
+ if (len > 0)
+ return len;
+
+ len = interpret_branch_mark(name, namelen, at - name, buf,
+ push_mark, branch_get_push);
if (len > 0)
return len;
}
const char *object_name,
int object_name_len)
{
- struct stat st;
unsigned char sha1[20];
unsigned mode;
if (!prefix)
prefix = "";
- if (!lstat(filename, &st))
+ if (file_exists(filename))
die("Path '%s' exists on disk, but not in '%.*s'.",
filename, object_name_len, object_name);
if (errno == ENOENT || errno == ENOTDIR) {
const char *prefix,
const char *filename)
{
- struct stat st;
const struct cache_entry *ce;
int pos;
unsigned namelen = strlen(filename);
ce_stage(ce), filename);
}
- if (!lstat(filename, &st))
+ if (file_exists(filename))
die("Path '%s' exists on disk, but not in the index.", filename);
if (errno == ENOENT || errno == ENOTDIR)
die("Path '%s' does not exist (neither on disk nor in the index).",
int pos;
if (!only_to_die && namelen > 2 && name[1] == '/') {
struct commit_list *list = NULL;
+
for_each_ref(handle_one_ref, &list);
commit_list_sort_by_date(&list);
return get_sha1_oneline(name + 2, sha1, list);
new_filename = resolve_relative_path(filename);
if (new_filename)
filename = new_filename;
- ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
- if (ret && only_to_die) {
- diagnose_invalid_sha1_path(prefix, filename,
- tree_sha1,
- name, len);
+ if (flags & GET_SHA1_FOLLOW_SYMLINKS) {
+ ret = get_tree_entry_follow_symlinks(tree_sha1,
+ filename, sha1, &oc->symlink_path,
+ &oc->mode);
+ } else {
+ ret = get_tree_entry(tree_sha1, filename,
+ sha1, &oc->mode);
+ if (ret && only_to_die) {
+ diagnose_invalid_sha1_path(prefix,
+ filename,
+ tree_sha1,
+ name, len);
+ }
}
hashcpy(oc->tree, tree_sha1);
strlcpy(oc->path, filename, sizeof(oc->path));
int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc)
{
+ if (flags & GET_SHA1_FOLLOW_SYMLINKS && flags & GET_SHA1_ONLY_TO_DIE)
+ die("BUG: incompatible flags for get_sha1_with_context");
return get_sha1_with_context_1(str, flags, NULL, sha1, orc);
}
config_fetch_recurse_submodules = value;
}
- static int has_remote(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+ static int has_remote(const char *refname, const struct object_id *oid,
+ int flags, void *cb_data)
{
return 1;
}
}
}
- static int add_sha1_to_array(const char *ref, const unsigned char *sha1,
+ static int add_sha1_to_array(const char *ref, const struct object_id *oid,
int flags, void *data)
{
- sha1_array_append(data, sha1);
+ sha1_array_append(data, oid->hash);
return 0;
}
int ok_to_remove_submodule(const char *path)
{
- struct stat st;
ssize_t len;
struct child_process cp = CHILD_PROCESS_INIT;
const char *argv[] = {
struct strbuf buf = STRBUF_INIT;
int ok_to_remove = 1;
- if ((lstat(path, &st) < 0) || is_empty_dir(path))
+ if (!file_exists(path) || is_empty_dir(path))
return 1;
if (!submodule_uses_gitfile(path))
static int no_done;
static int use_thin_pack, use_ofs_delta, use_include_tag;
static int no_progress, daemon_mode;
-static int allow_tip_sha1_in_want;
+/* Allow specifying sha1 if it is a ref tip. */
+#define ALLOW_TIP_SHA1 01
+/* Allow request of a sha1 if it is reachable from a ref (possibly hidden ref). */
+#define ALLOW_REACHABLE_SHA1 02
+static unsigned int allow_unadvertised_object_request;
static int shallow_nr;
static struct object_array have_obj;
static struct object_array want_obj;
static int is_our_ref(struct object *o)
{
- return o->flags &
- ((allow_tip_sha1_in_want ? HIDDEN_REF : 0) | OUR_REF);
+ int allow_hidden_ref = (allow_unadvertised_object_request &
+ (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));
+ return o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF);
}
static void check_non_tip(void)
char namebuf[42]; /* ^ + SHA-1 + LF */
int i;
- /* In the normal in-process case non-tip request can never happen */
- if (!stateless_rpc)
+ /*
+ * In the normal in-process case without
+ * uploadpack.allowReachableSHA1InWant,
+ * non-tip requests can never happen.
+ */
+ if (!stateless_rpc && !(allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1))
goto error;
cmd.argv = argv;
}
/* return non-zero if the ref is hidden, otherwise 0 */
- static int mark_our_ref(const char *refname, const unsigned char *sha1)
+ static int mark_our_ref(const char *refname, const struct object_id *oid)
{
- struct object *o = lookup_unknown_object(sha1);
+ struct object *o = lookup_unknown_object(oid->hash);
if (ref_is_hidden(refname)) {
o->flags |= HIDDEN_REF;
return 0;
}
- static int check_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+ static int check_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
- mark_our_ref(refname, sha1);
+ mark_our_ref(refname, oid);
return 0;
}
strbuf_addf(buf, " symref=%s:%s", item->string, (char *)item->util);
}
- static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+ static int send_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
static const char *capabilities = "multi_ack thin-pack side-band"
" side-band-64k ofs-delta shallow no-progress"
" include-tag multi_ack_detailed";
const char *refname_nons = strip_namespace(refname);
- unsigned char peeled[20];
+ struct object_id peeled;
- if (mark_our_ref(refname, sha1))
+ if (mark_our_ref(refname, oid))
return 0;
if (capabilities) {
struct strbuf symref_info = STRBUF_INIT;
format_symref_info(&symref_info, cb_data);
- packet_write(1, "%s %s%c%s%s%s%s agent=%s\n",
+ packet_write(1, "%s %s%c%s%s%s%s%s agent=%s\n",
- sha1_to_hex(sha1), refname_nons,
+ oid_to_hex(oid), refname_nons,
0, capabilities,
- allow_tip_sha1_in_want ? " allow-tip-sha1-in-want" : "",
+ (allow_unadvertised_object_request & ALLOW_TIP_SHA1) ?
+ " allow-tip-sha1-in-want" : "",
+ (allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1) ?
+ " allow-reachable-sha1-in-want" : "",
stateless_rpc ? " no-done" : "",
symref_info.buf,
git_user_agent_sanitized());
strbuf_release(&symref_info);
} else {
- packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname_nons);
+ packet_write(1, "%s %s\n", oid_to_hex(oid), refname_nons);
}
capabilities = NULL;
- if (!peel_ref(refname, peeled))
- packet_write(1, "%s %s^{}\n", sha1_to_hex(peeled), refname_nons);
+ if (!peel_ref(refname, peeled.hash))
+ packet_write(1, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
return 0;
}
- static int find_symref(const char *refname, const unsigned char *sha1, int flag,
- void *cb_data)
+ static int find_symref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
{
const char *symref_target;
struct string_list_item *item;
- unsigned char unused[20];
+ struct object_id unused;
if ((flag & REF_ISSYMREF) == 0)
return 0;
- symref_target = resolve_ref_unsafe(refname, 0, unused, &flag);
+ symref_target = resolve_ref_unsafe(refname, 0, unused.hash, &flag);
if (!symref_target || (flag & REF_ISSYMREF) == 0)
die("'%s' is a symref but it is not?", refname);
item = string_list_append(cb_data, refname);
static int upload_pack_config(const char *var, const char *value, void *unused)
{
- if (!strcmp("uploadpack.allowtipsha1inwant", var))
- allow_tip_sha1_in_want = git_config_bool(var, value);
- else if (!strcmp("uploadpack.keepalive", var)) {
+ if (!strcmp("uploadpack.allowtipsha1inwant", var)) {
+ if (git_config_bool(var, value))
+ allow_unadvertised_object_request |= ALLOW_TIP_SHA1;
+ else
+ allow_unadvertised_object_request &= ~ALLOW_TIP_SHA1;
+ } else if (!strcmp("uploadpack.allowreachablesha1inwant", var)) {
+ if (git_config_bool(var, value))
+ allow_unadvertised_object_request |= ALLOW_REACHABLE_SHA1;
+ else
+ allow_unadvertised_object_request &= ~ALLOW_REACHABLE_SHA1;
+ } else if (!strcmp("uploadpack.keepalive", var)) {
keepalive = git_config_int(var, value);
if (!keepalive)
keepalive = -1;