2007-05-12 19:45:53 +04:00
|
|
|
#include "cache.h"
|
|
|
|
#include "remote.h"
|
|
|
|
#include "refs.h"
|
2008-07-02 11:51:18 +04:00
|
|
|
#include "commit.h"
|
|
|
|
#include "diff.h"
|
|
|
|
#include "revision.h"
|
2009-01-10 15:07:50 +03:00
|
|
|
#include "dir.h"
|
2009-02-25 11:32:12 +03:00
|
|
|
#include "tag.h"
|
2009-10-26 00:28:11 +03:00
|
|
|
#include "string-list.h"
|
2012-05-22 02:19:28 +04:00
|
|
|
#include "mergesort.h"
|
2007-05-12 19:45:53 +04:00
|
|
|
|
2012-02-23 02:43:41 +04:00
|
|
|
enum map_direction { FROM_SRC, FROM_DST };
|
|
|
|
|
2008-04-18 03:32:35 +04:00
|
|
|
static struct refspec s_tag_refspec = {
|
|
|
|
0,
|
|
|
|
1,
|
2008-05-26 00:38:44 +04:00
|
|
|
0,
|
2013-01-30 02:02:15 +04:00
|
|
|
0,
|
2009-03-07 09:11:36 +03:00
|
|
|
"refs/tags/*",
|
|
|
|
"refs/tags/*"
|
2008-04-18 03:32:35 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
const struct refspec *tag_refspec = &s_tag_refspec;
|
|
|
|
|
2008-02-25 09:25:04 +03:00
|
|
|
struct counted_string {
|
|
|
|
size_t len;
|
|
|
|
const char *s;
|
|
|
|
};
|
2008-02-20 21:43:53 +03:00
|
|
|
struct rewrite {
|
|
|
|
const char *base;
|
2008-02-25 09:25:04 +03:00
|
|
|
size_t baselen;
|
|
|
|
struct counted_string *instead_of;
|
2008-02-20 21:43:53 +03:00
|
|
|
int instead_of_nr;
|
|
|
|
int instead_of_alloc;
|
|
|
|
};
|
2009-09-07 12:56:00 +04:00
|
|
|
struct rewrites {
|
|
|
|
struct rewrite **rewrite;
|
|
|
|
int rewrite_alloc;
|
|
|
|
int rewrite_nr;
|
|
|
|
};
|
2008-02-20 21:43:53 +03:00
|
|
|
|
2007-05-12 19:45:53 +04:00
|
|
|
static struct remote **remotes;
|
2008-02-19 07:41:41 +03:00
|
|
|
static int remotes_alloc;
|
|
|
|
static int remotes_nr;
|
2014-07-29 18:43:39 +04:00
|
|
|
static struct hashmap remotes_hash;
|
2007-05-12 19:45:53 +04:00
|
|
|
|
2007-09-11 07:02:56 +04:00
|
|
|
static struct branch **branches;
|
2008-02-19 07:41:41 +03:00
|
|
|
static int branches_alloc;
|
|
|
|
static int branches_nr;
|
2007-09-11 07:02:56 +04:00
|
|
|
|
|
|
|
static struct branch *current_branch;
|
|
|
|
static const char *default_remote_name;
|
2014-02-24 12:59:03 +04:00
|
|
|
static const char *branch_pushremote_name;
|
remote.c: introduce a way to have different remotes for fetch/push
Currently, do_push() in push.c calls remote_get(), which gets the
configured remote for fetching and pushing. Replace this call with a
call to pushremote_get() instead, a new function that will return the
remote configured specifically for pushing. This function tries to
work with the string pushremote_name, before falling back to the
codepath of remote_get(). This patch has no visible impact, but
serves to enable future patches to introduce configuration variables
to set pushremote_name. For example, you can now do the following in
handle_config():
if (!strcmp(key, "remote.pushdefault"))
git_config_string(&pushremote_name, key, value);
Then, pushes will automatically go to the remote specified by
remote.pushdefault.
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
Reviewed-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-02 11:40:32 +04:00
|
|
|
static const char *pushremote_name;
|
Give error when no remote is configured
When there's no explicitly-named remote, we use the remote specified
for the current branch, which in turn defaults to "origin". But it
this case should require the remote to actually be configured, and not
fall back to the path "origin".
Possibly, the config file's "remote = something" should require the
something to be a configured remote instead of a bare repository URL,
but we actually test with a bare repository URL.
In fetch, we were giving the sensible error message when coming up
with a URL failed, but this wasn't actually reachable, so move that
error up and use it when appropriate.
In push, we need a new error message, because the old one (formerly
unreachable without a lot of help) used the repo name, which was NULL.
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-11 08:47:20 +03:00
|
|
|
static int explicit_default_remote_name;
|
2007-09-11 07:02:56 +04:00
|
|
|
|
2009-09-07 12:56:00 +04:00
|
|
|
static struct rewrites rewrites;
|
2009-09-07 12:56:33 +04:00
|
|
|
static struct rewrites rewrites_push;
|
2008-02-20 21:43:53 +03:00
|
|
|
|
2007-05-12 19:45:53 +04:00
|
|
|
#define BUF_SIZE (2048)
|
|
|
|
static char buffer[BUF_SIZE];
|
|
|
|
|
2009-11-18 04:42:23 +03:00
|
|
|
static int valid_remote(const struct remote *remote)
|
|
|
|
{
|
2009-11-18 04:42:25 +03:00
|
|
|
return (!!remote->url) || (!!remote->foreign_vcs);
|
2009-11-18 04:42:23 +03:00
|
|
|
}
|
|
|
|
|
2009-09-07 12:56:00 +04:00
|
|
|
static const char *alias_url(const char *url, struct rewrites *r)
|
2008-02-20 21:43:53 +03:00
|
|
|
{
|
|
|
|
int i, j;
|
2008-02-25 09:25:04 +03:00
|
|
|
char *ret;
|
|
|
|
struct counted_string *longest;
|
|
|
|
int longest_i;
|
|
|
|
|
|
|
|
longest = NULL;
|
|
|
|
longest_i = -1;
|
2009-09-07 12:56:00 +04:00
|
|
|
for (i = 0; i < r->rewrite_nr; i++) {
|
|
|
|
if (!r->rewrite[i])
|
2008-02-20 21:43:53 +03:00
|
|
|
continue;
|
2009-09-07 12:56:00 +04:00
|
|
|
for (j = 0; j < r->rewrite[i]->instead_of_nr; j++) {
|
2013-12-01 00:55:40 +04:00
|
|
|
if (starts_with(url, r->rewrite[i]->instead_of[j].s) &&
|
2008-02-25 09:25:04 +03:00
|
|
|
(!longest ||
|
2009-09-07 12:56:00 +04:00
|
|
|
longest->len < r->rewrite[i]->instead_of[j].len)) {
|
|
|
|
longest = &(r->rewrite[i]->instead_of[j]);
|
2008-02-25 09:25:04 +03:00
|
|
|
longest_i = i;
|
2008-02-20 21:43:53 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-02-25 09:25:04 +03:00
|
|
|
if (!longest)
|
|
|
|
return url;
|
|
|
|
|
2009-09-07 12:56:00 +04:00
|
|
|
ret = xmalloc(r->rewrite[longest_i]->baselen +
|
2008-02-25 09:25:04 +03:00
|
|
|
(strlen(url) - longest->len) + 1);
|
2009-09-07 12:56:00 +04:00
|
|
|
strcpy(ret, r->rewrite[longest_i]->base);
|
|
|
|
strcpy(ret + r->rewrite[longest_i]->baselen, url + longest->len);
|
2008-02-25 09:25:04 +03:00
|
|
|
return ret;
|
2008-02-20 21:43:53 +03:00
|
|
|
}
|
|
|
|
|
2007-05-12 19:45:53 +04:00
|
|
|
static void add_push_refspec(struct remote *remote, const char *ref)
|
|
|
|
{
|
2008-02-19 07:41:41 +03:00
|
|
|
ALLOC_GROW(remote->push_refspec,
|
|
|
|
remote->push_refspec_nr + 1,
|
|
|
|
remote->push_refspec_alloc);
|
|
|
|
remote->push_refspec[remote->push_refspec_nr++] = ref;
|
2007-05-12 19:45:53 +04:00
|
|
|
}
|
|
|
|
|
2007-05-12 19:46:03 +04:00
|
|
|
static void add_fetch_refspec(struct remote *remote, const char *ref)
|
|
|
|
{
|
2008-02-19 07:41:41 +03:00
|
|
|
ALLOC_GROW(remote->fetch_refspec,
|
|
|
|
remote->fetch_refspec_nr + 1,
|
|
|
|
remote->fetch_refspec_alloc);
|
|
|
|
remote->fetch_refspec[remote->fetch_refspec_nr++] = ref;
|
2007-05-12 19:46:03 +04:00
|
|
|
}
|
|
|
|
|
2007-09-19 08:49:27 +04:00
|
|
|
static void add_url(struct remote *remote, const char *url)
|
2007-05-12 19:45:53 +04:00
|
|
|
{
|
2008-02-19 07:41:41 +03:00
|
|
|
ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);
|
|
|
|
remote->url[remote->url_nr++] = url;
|
2007-05-12 19:45:53 +04:00
|
|
|
}
|
|
|
|
|
2009-06-09 20:01:34 +04:00
|
|
|
static void add_pushurl(struct remote *remote, const char *pushurl)
|
|
|
|
{
|
|
|
|
ALLOC_GROW(remote->pushurl, remote->pushurl_nr + 1, remote->pushurl_alloc);
|
|
|
|
remote->pushurl[remote->pushurl_nr++] = pushurl;
|
|
|
|
}
|
|
|
|
|
2009-09-07 12:56:33 +04:00
|
|
|
static void add_pushurl_alias(struct remote *remote, const char *url)
|
|
|
|
{
|
|
|
|
const char *pushurl = alias_url(url, &rewrites_push);
|
|
|
|
if (pushurl != url)
|
|
|
|
add_pushurl(remote, pushurl);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_url_alias(struct remote *remote, const char *url)
|
|
|
|
{
|
|
|
|
add_url(remote, alias_url(url, &rewrites));
|
|
|
|
add_pushurl_alias(remote, url);
|
|
|
|
}
|
|
|
|
|
2014-07-29 18:43:39 +04:00
|
|
|
struct remotes_hash_key {
|
|
|
|
const char *str;
|
|
|
|
int len;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int remotes_hash_cmp(const struct remote *a, const struct remote *b, const struct remotes_hash_key *key)
|
|
|
|
{
|
|
|
|
if (key)
|
|
|
|
return strncmp(a->name, key->str, key->len) || a->name[key->len];
|
|
|
|
else
|
|
|
|
return strcmp(a->name, b->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void init_remotes_hash(void)
|
|
|
|
{
|
|
|
|
if (!remotes_hash.cmpfn)
|
|
|
|
hashmap_init(&remotes_hash, (hashmap_cmp_fn)remotes_hash_cmp, 0);
|
|
|
|
}
|
|
|
|
|
2007-05-12 19:45:53 +04:00
|
|
|
static struct remote *make_remote(const char *name, int len)
|
|
|
|
{
|
2014-07-29 18:43:39 +04:00
|
|
|
struct remote *ret, *replaced;
|
|
|
|
struct remotes_hash_key lookup;
|
|
|
|
struct hashmap_entry lookup_entry;
|
2007-05-12 19:45:53 +04:00
|
|
|
|
2014-07-29 18:43:39 +04:00
|
|
|
if (!len)
|
|
|
|
len = strlen(name);
|
|
|
|
|
|
|
|
init_remotes_hash();
|
|
|
|
lookup.str = name;
|
|
|
|
lookup.len = len;
|
|
|
|
hashmap_entry_init(&lookup_entry, memhash(name, len));
|
|
|
|
|
|
|
|
if ((ret = hashmap_get(&remotes_hash, &lookup_entry, &lookup)) != NULL)
|
|
|
|
return ret;
|
2007-05-12 19:45:53 +04:00
|
|
|
|
2008-02-19 07:41:41 +03:00
|
|
|
ret = xcalloc(1, sizeof(struct remote));
|
2013-07-13 13:36:24 +04:00
|
|
|
ret->prune = -1; /* unspecified */
|
2008-02-19 07:41:41 +03:00
|
|
|
ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);
|
|
|
|
remotes[remotes_nr++] = ret;
|
2014-07-29 18:43:39 +04:00
|
|
|
ret->name = xstrndup(name, len);
|
|
|
|
|
|
|
|
hashmap_entry_init(ret, lookup_entry.hash);
|
|
|
|
replaced = hashmap_put(&remotes_hash, ret);
|
|
|
|
assert(replaced == NULL); /* no previous entry overwritten */
|
2008-02-19 07:41:41 +03:00
|
|
|
return ret;
|
2007-05-12 19:45:53 +04:00
|
|
|
}
|
|
|
|
|
2007-09-11 07:02:56 +04:00
|
|
|
static void add_merge(struct branch *branch, const char *name)
|
|
|
|
{
|
2008-02-19 07:41:41 +03:00
|
|
|
ALLOC_GROW(branch->merge_name, branch->merge_nr + 1,
|
|
|
|
branch->merge_alloc);
|
|
|
|
branch->merge_name[branch->merge_nr++] = name;
|
2007-09-11 07:02:56 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct branch *make_branch(const char *name, int len)
|
|
|
|
{
|
2008-02-19 07:41:41 +03:00
|
|
|
struct branch *ret;
|
|
|
|
int i;
|
2007-09-11 07:02:56 +04:00
|
|
|
|
2008-02-19 07:41:41 +03:00
|
|
|
for (i = 0; i < branches_nr; i++) {
|
|
|
|
if (len ? (!strncmp(name, branches[i]->name, len) &&
|
|
|
|
!branches[i]->name[len]) :
|
|
|
|
!strcmp(name, branches[i]->name))
|
|
|
|
return branches[i];
|
2007-09-11 07:02:56 +04:00
|
|
|
}
|
|
|
|
|
2008-02-19 07:41:41 +03:00
|
|
|
ALLOC_GROW(branches, branches_nr + 1, branches_alloc);
|
|
|
|
ret = xcalloc(1, sizeof(struct branch));
|
|
|
|
branches[branches_nr++] = ret;
|
2007-09-11 07:02:56 +04:00
|
|
|
if (len)
|
2008-02-19 07:41:41 +03:00
|
|
|
ret->name = xstrndup(name, len);
|
2007-09-11 07:02:56 +04:00
|
|
|
else
|
2008-02-19 07:41:41 +03:00
|
|
|
ret->name = xstrdup(name);
|
2014-06-19 00:02:13 +04:00
|
|
|
ret->refname = xstrfmt("refs/heads/%s", ret->name);
|
2007-09-11 07:02:56 +04:00
|
|
|
|
2008-02-19 07:41:41 +03:00
|
|
|
return ret;
|
2007-09-11 07:02:56 +04:00
|
|
|
}
|
|
|
|
|
2009-09-07 12:56:00 +04:00
|
|
|
static struct rewrite *make_rewrite(struct rewrites *r, const char *base, int len)
|
2008-02-20 21:43:53 +03:00
|
|
|
{
|
|
|
|
struct rewrite *ret;
|
|
|
|
int i;
|
|
|
|
|
2009-09-07 12:56:00 +04:00
|
|
|
for (i = 0; i < r->rewrite_nr; i++) {
|
2008-02-25 09:25:04 +03:00
|
|
|
if (len
|
2009-09-07 12:56:00 +04:00
|
|
|
? (len == r->rewrite[i]->baselen &&
|
|
|
|
!strncmp(base, r->rewrite[i]->base, len))
|
|
|
|
: !strcmp(base, r->rewrite[i]->base))
|
|
|
|
return r->rewrite[i];
|
2008-02-20 21:43:53 +03:00
|
|
|
}
|
|
|
|
|
2009-09-07 12:56:00 +04:00
|
|
|
ALLOC_GROW(r->rewrite, r->rewrite_nr + 1, r->rewrite_alloc);
|
2008-02-20 21:43:53 +03:00
|
|
|
ret = xcalloc(1, sizeof(struct rewrite));
|
2009-09-07 12:56:00 +04:00
|
|
|
r->rewrite[r->rewrite_nr++] = ret;
|
2008-02-25 09:25:04 +03:00
|
|
|
if (len) {
|
2008-02-20 21:43:53 +03:00
|
|
|
ret->base = xstrndup(base, len);
|
2008-02-25 09:25:04 +03:00
|
|
|
ret->baselen = len;
|
|
|
|
}
|
|
|
|
else {
|
2008-02-20 21:43:53 +03:00
|
|
|
ret->base = xstrdup(base);
|
2008-02-25 09:25:04 +03:00
|
|
|
ret->baselen = strlen(base);
|
|
|
|
}
|
2008-02-20 21:43:53 +03:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_instead_of(struct rewrite *rewrite, const char *instead_of)
|
|
|
|
{
|
|
|
|
ALLOC_GROW(rewrite->instead_of, rewrite->instead_of_nr + 1, rewrite->instead_of_alloc);
|
2008-02-25 09:25:04 +03:00
|
|
|
rewrite->instead_of[rewrite->instead_of_nr].s = instead_of;
|
|
|
|
rewrite->instead_of[rewrite->instead_of_nr].len = strlen(instead_of);
|
|
|
|
rewrite->instead_of_nr++;
|
2008-02-20 21:43:53 +03:00
|
|
|
}
|
|
|
|
|
2007-05-12 19:45:53 +04:00
|
|
|
static void read_remotes_file(struct remote *remote)
|
|
|
|
{
|
|
|
|
FILE *f = fopen(git_path("remotes/%s", remote->name), "r");
|
|
|
|
|
|
|
|
if (!f)
|
|
|
|
return;
|
2008-11-10 23:43:00 +03:00
|
|
|
remote->origin = REMOTE_REMOTES;
|
2007-05-12 19:45:53 +04:00
|
|
|
while (fgets(buffer, BUF_SIZE, f)) {
|
|
|
|
int value_list;
|
|
|
|
char *s, *p;
|
|
|
|
|
2013-12-01 00:55:40 +04:00
|
|
|
if (starts_with(buffer, "URL:")) {
|
2007-05-12 19:45:53 +04:00
|
|
|
value_list = 0;
|
|
|
|
s = buffer + 4;
|
2013-12-01 00:55:40 +04:00
|
|
|
} else if (starts_with(buffer, "Push:")) {
|
2007-05-12 19:45:53 +04:00
|
|
|
value_list = 1;
|
|
|
|
s = buffer + 5;
|
2013-12-01 00:55:40 +04:00
|
|
|
} else if (starts_with(buffer, "Pull:")) {
|
2007-05-12 19:46:03 +04:00
|
|
|
value_list = 2;
|
|
|
|
s = buffer + 5;
|
2007-05-12 19:45:53 +04:00
|
|
|
} else
|
|
|
|
continue;
|
|
|
|
|
|
|
|
while (isspace(*s))
|
|
|
|
s++;
|
|
|
|
if (!*s)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
p = s + strlen(s);
|
|
|
|
while (isspace(p[-1]))
|
|
|
|
*--p = 0;
|
|
|
|
|
|
|
|
switch (value_list) {
|
|
|
|
case 0:
|
2008-02-20 21:43:53 +03:00
|
|
|
add_url_alias(remote, xstrdup(s));
|
2007-05-12 19:45:53 +04:00
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
add_push_refspec(remote, xstrdup(s));
|
|
|
|
break;
|
2007-05-12 19:46:03 +04:00
|
|
|
case 2:
|
|
|
|
add_fetch_refspec(remote, xstrdup(s));
|
|
|
|
break;
|
2007-05-12 19:45:53 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void read_branches_file(struct remote *remote)
|
|
|
|
{
|
2007-09-11 07:02:56 +04:00
|
|
|
char *frag;
|
2008-10-09 23:12:12 +04:00
|
|
|
struct strbuf branch = STRBUF_INIT;
|
2013-06-22 11:58:12 +04:00
|
|
|
int n = 1000;
|
2007-05-12 19:45:53 +04:00
|
|
|
FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r");
|
|
|
|
char *s, *p;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (!f)
|
|
|
|
return;
|
|
|
|
s = fgets(buffer, BUF_SIZE, f);
|
|
|
|
fclose(f);
|
|
|
|
if (!s)
|
|
|
|
return;
|
|
|
|
while (isspace(*s))
|
|
|
|
s++;
|
|
|
|
if (!*s)
|
|
|
|
return;
|
2008-11-10 23:43:00 +03:00
|
|
|
remote->origin = REMOTE_BRANCHES;
|
2007-05-12 19:45:53 +04:00
|
|
|
p = s + strlen(s);
|
|
|
|
while (isspace(p[-1]))
|
|
|
|
*--p = 0;
|
|
|
|
len = p - s;
|
|
|
|
p = xmalloc(len + 1);
|
|
|
|
strcpy(p, s);
|
2008-03-26 02:35:28 +03:00
|
|
|
|
|
|
|
/*
|
2013-06-22 11:58:12 +04:00
|
|
|
* The branches file would have URL and optionally
|
2008-03-26 02:35:28 +03:00
|
|
|
* #branch specified. The "master" (or specified) branch is
|
|
|
|
* fetched and stored in the local branch of the same name.
|
|
|
|
*/
|
2007-09-11 07:02:56 +04:00
|
|
|
frag = strchr(p, '#');
|
|
|
|
if (frag) {
|
|
|
|
*(frag++) = '\0';
|
2008-03-26 02:35:28 +03:00
|
|
|
strbuf_addf(&branch, "refs/heads/%s", frag);
|
|
|
|
} else
|
|
|
|
strbuf_addstr(&branch, "refs/heads/master");
|
2013-06-22 11:58:12 +04:00
|
|
|
|
|
|
|
strbuf_addf(&branch, ":refs/heads/%s", remote->name);
|
2008-02-20 21:43:53 +03:00
|
|
|
add_url_alias(remote, p);
|
2009-06-18 21:28:43 +04:00
|
|
|
add_fetch_refspec(remote, strbuf_detach(&branch, NULL));
|
2008-11-11 00:47:11 +03:00
|
|
|
/*
|
|
|
|
* Cogito compatible push: push current HEAD to remote #branch
|
|
|
|
* (master if missing)
|
|
|
|
*/
|
|
|
|
strbuf_init(&branch, 0);
|
|
|
|
strbuf_addstr(&branch, "HEAD");
|
|
|
|
if (frag)
|
|
|
|
strbuf_addf(&branch, ":refs/heads/%s", frag);
|
|
|
|
else
|
|
|
|
strbuf_addstr(&branch, ":refs/heads/master");
|
2009-06-18 21:28:43 +04:00
|
|
|
add_push_refspec(remote, strbuf_detach(&branch, NULL));
|
2007-09-11 07:03:08 +04:00
|
|
|
remote->fetch_tags = 1; /* always auto-follow */
|
2007-05-12 19:45:53 +04:00
|
|
|
}
|
|
|
|
|
2008-05-14 21:46:53 +04:00
|
|
|
static int handle_config(const char *key, const char *value, void *cb)
|
2007-05-12 19:45:53 +04:00
|
|
|
{
|
|
|
|
const char *name;
|
|
|
|
const char *subkey;
|
|
|
|
struct remote *remote;
|
2007-09-11 07:02:56 +04:00
|
|
|
struct branch *branch;
|
2013-12-01 00:55:40 +04:00
|
|
|
if (starts_with(key, "branch.")) {
|
2007-09-11 07:02:56 +04:00
|
|
|
name = key + 7;
|
|
|
|
subkey = strrchr(name, '.');
|
|
|
|
if (!subkey)
|
|
|
|
return 0;
|
2007-12-15 07:34:56 +03:00
|
|
|
branch = make_branch(name, subkey - name);
|
2007-09-11 07:02:56 +04:00
|
|
|
if (!strcmp(subkey, ".remote")) {
|
2013-04-02 11:40:29 +04:00
|
|
|
if (git_config_string(&branch->remote_name, key, value))
|
|
|
|
return -1;
|
Give error when no remote is configured
When there's no explicitly-named remote, we use the remote specified
for the current branch, which in turn defaults to "origin". But it
this case should require the remote to actually be configured, and not
fall back to the path "origin".
Possibly, the config file's "remote = something" should require the
something to be a configured remote instead of a bare repository URL,
but we actually test with a bare repository URL.
In fetch, we were giving the sensible error message when coming up
with a URL failed, but this wasn't actually reachable, so move that
error up and use it when appropriate.
In push, we need a new error message, because the old one (formerly
unreachable without a lot of help) used the repo name, which was NULL.
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-11 08:47:20 +03:00
|
|
|
if (branch == current_branch) {
|
2007-09-11 07:02:56 +04:00
|
|
|
default_remote_name = branch->remote_name;
|
Give error when no remote is configured
When there's no explicitly-named remote, we use the remote specified
for the current branch, which in turn defaults to "origin". But it
this case should require the remote to actually be configured, and not
fall back to the path "origin".
Possibly, the config file's "remote = something" should require the
something to be a configured remote instead of a bare repository URL,
but we actually test with a bare repository URL.
In fetch, we were giving the sensible error message when coming up
with a URL failed, but this wasn't actually reachable, so move that
error up and use it when appropriate.
In push, we need a new error message, because the old one (formerly
unreachable without a lot of help) used the repo name, which was NULL.
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-11 08:47:20 +03:00
|
|
|
explicit_default_remote_name = 1;
|
|
|
|
}
|
2013-04-02 11:40:34 +04:00
|
|
|
} else if (!strcmp(subkey, ".pushremote")) {
|
|
|
|
if (branch == current_branch)
|
2014-02-24 12:59:03 +04:00
|
|
|
if (git_config_string(&branch_pushremote_name, key, value))
|
2013-04-02 11:40:34 +04:00
|
|
|
return -1;
|
2008-02-11 22:00:10 +03:00
|
|
|
} else if (!strcmp(subkey, ".merge")) {
|
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(key);
|
2007-09-11 07:02:56 +04:00
|
|
|
add_merge(branch, xstrdup(value));
|
2008-02-11 22:00:10 +03:00
|
|
|
}
|
2007-09-11 07:02:56 +04:00
|
|
|
return 0;
|
2007-05-12 19:45:53 +04:00
|
|
|
}
|
2013-12-01 00:55:40 +04:00
|
|
|
if (starts_with(key, "url.")) {
|
2008-02-20 21:43:53 +03:00
|
|
|
struct rewrite *rewrite;
|
2008-04-12 23:32:00 +04:00
|
|
|
name = key + 4;
|
2008-02-20 21:43:53 +03:00
|
|
|
subkey = strrchr(name, '.');
|
|
|
|
if (!subkey)
|
|
|
|
return 0;
|
|
|
|
if (!strcmp(subkey, ".insteadof")) {
|
2009-09-07 12:56:33 +04:00
|
|
|
rewrite = make_rewrite(&rewrites, name, subkey - name);
|
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(key);
|
|
|
|
add_instead_of(rewrite, xstrdup(value));
|
|
|
|
} else if (!strcmp(subkey, ".pushinsteadof")) {
|
|
|
|
rewrite = make_rewrite(&rewrites_push, name, subkey - name);
|
2008-02-20 21:43:53 +03:00
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(key);
|
|
|
|
add_instead_of(rewrite, xstrdup(value));
|
|
|
|
}
|
|
|
|
}
|
2013-04-02 11:40:33 +04:00
|
|
|
|
2013-12-01 00:55:40 +04:00
|
|
|
if (!starts_with(key, "remote."))
|
2007-05-12 19:45:53 +04:00
|
|
|
return 0;
|
|
|
|
name = key + 7;
|
2013-04-02 11:40:33 +04:00
|
|
|
|
|
|
|
/* Handle remote.* variables */
|
|
|
|
if (!strcmp(name, "pushdefault"))
|
|
|
|
return git_config_string(&pushremote_name, key, value);
|
|
|
|
|
|
|
|
/* Handle remote.<name>.* variables */
|
2008-10-15 00:30:21 +04:00
|
|
|
if (*name == '/') {
|
|
|
|
warning("Config remote shorthand cannot begin with '/': %s",
|
|
|
|
name);
|
|
|
|
return 0;
|
|
|
|
}
|
2007-05-12 19:45:53 +04:00
|
|
|
subkey = strrchr(name, '.');
|
|
|
|
if (!subkey)
|
2009-04-23 17:49:05 +04:00
|
|
|
return 0;
|
2007-05-12 19:45:53 +04:00
|
|
|
remote = make_remote(name, subkey - name);
|
2008-11-10 23:43:00 +03:00
|
|
|
remote->origin = REMOTE_CONFIG;
|
2008-04-17 15:17:20 +04:00
|
|
|
if (!strcmp(subkey, ".mirror"))
|
|
|
|
remote->mirror = git_config_bool(key, value);
|
|
|
|
else if (!strcmp(subkey, ".skipdefaultupdate"))
|
|
|
|
remote->skip_default_update = git_config_bool(key, value);
|
2009-11-09 23:11:06 +03:00
|
|
|
else if (!strcmp(subkey, ".skipfetchall"))
|
|
|
|
remote->skip_default_update = git_config_bool(key, value);
|
2013-07-13 13:36:24 +04:00
|
|
|
else if (!strcmp(subkey, ".prune"))
|
|
|
|
remote->prune = git_config_bool(key, value);
|
2008-04-17 15:17:20 +04:00
|
|
|
else if (!strcmp(subkey, ".url")) {
|
|
|
|
const char *v;
|
|
|
|
if (git_config_string(&v, key, value))
|
|
|
|
return -1;
|
|
|
|
add_url(remote, v);
|
2009-06-09 20:01:34 +04:00
|
|
|
} else if (!strcmp(subkey, ".pushurl")) {
|
|
|
|
const char *v;
|
|
|
|
if (git_config_string(&v, key, value))
|
|
|
|
return -1;
|
|
|
|
add_pushurl(remote, v);
|
2007-05-12 19:45:53 +04:00
|
|
|
} else if (!strcmp(subkey, ".push")) {
|
2008-04-17 15:17:20 +04:00
|
|
|
const char *v;
|
|
|
|
if (git_config_string(&v, key, value))
|
|
|
|
return -1;
|
|
|
|
add_push_refspec(remote, v);
|
2007-05-12 19:46:03 +04:00
|
|
|
} else if (!strcmp(subkey, ".fetch")) {
|
2008-04-17 15:17:20 +04:00
|
|
|
const char *v;
|
|
|
|
if (git_config_string(&v, key, value))
|
|
|
|
return -1;
|
|
|
|
add_fetch_refspec(remote, v);
|
2007-05-12 19:45:53 +04:00
|
|
|
} else if (!strcmp(subkey, ".receivepack")) {
|
2008-04-17 15:17:20 +04:00
|
|
|
const char *v;
|
|
|
|
if (git_config_string(&v, key, value))
|
|
|
|
return -1;
|
2007-05-12 19:45:53 +04:00
|
|
|
if (!remote->receivepack)
|
2008-04-17 15:17:20 +04:00
|
|
|
remote->receivepack = v;
|
2007-05-12 19:45:53 +04:00
|
|
|
else
|
|
|
|
error("more than one receivepack given, using the first");
|
2007-09-11 07:02:51 +04:00
|
|
|
} else if (!strcmp(subkey, ".uploadpack")) {
|
2008-04-17 15:17:20 +04:00
|
|
|
const char *v;
|
|
|
|
if (git_config_string(&v, key, value))
|
|
|
|
return -1;
|
2007-09-11 07:02:51 +04:00
|
|
|
if (!remote->uploadpack)
|
2008-04-17 15:17:20 +04:00
|
|
|
remote->uploadpack = v;
|
2007-09-11 07:02:51 +04:00
|
|
|
else
|
|
|
|
error("more than one uploadpack given, using the first");
|
2007-09-11 07:03:08 +04:00
|
|
|
} else if (!strcmp(subkey, ".tagopt")) {
|
|
|
|
if (!strcmp(value, "--no-tags"))
|
|
|
|
remote->fetch_tags = -1;
|
2010-04-20 03:31:25 +04:00
|
|
|
else if (!strcmp(value, "--tags"))
|
|
|
|
remote->fetch_tags = 2;
|
2007-12-04 00:48:54 +03:00
|
|
|
} else if (!strcmp(subkey, ".proxy")) {
|
2008-04-17 15:17:20 +04:00
|
|
|
return git_config_string((const char **)&remote->http_proxy,
|
|
|
|
key, value);
|
2009-11-18 04:42:25 +03:00
|
|
|
} else if (!strcmp(subkey, ".vcs")) {
|
|
|
|
return git_config_string(&remote->foreign_vcs, key, value);
|
2008-04-17 15:17:20 +04:00
|
|
|
}
|
2007-05-12 19:45:53 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-02-20 21:43:53 +03:00
|
|
|
static void alias_all_urls(void)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
for (i = 0; i < remotes_nr; i++) {
|
2009-09-07 12:56:33 +04:00
|
|
|
int add_pushurl_aliases;
|
2008-02-20 21:43:53 +03:00
|
|
|
if (!remotes[i])
|
|
|
|
continue;
|
2009-06-09 20:01:34 +04:00
|
|
|
for (j = 0; j < remotes[i]->pushurl_nr; j++) {
|
2009-09-07 12:56:00 +04:00
|
|
|
remotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[j], &rewrites);
|
2009-06-09 20:01:34 +04:00
|
|
|
}
|
2009-09-07 12:56:33 +04:00
|
|
|
add_pushurl_aliases = remotes[i]->pushurl_nr == 0;
|
|
|
|
for (j = 0; j < remotes[i]->url_nr; j++) {
|
|
|
|
if (add_pushurl_aliases)
|
|
|
|
add_pushurl_alias(remotes[i], remotes[i]->url[j]);
|
|
|
|
remotes[i]->url[j] = alias_url(remotes[i]->url[j], &rewrites);
|
|
|
|
}
|
2008-02-20 21:43:53 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-05-12 19:45:53 +04:00
|
|
|
static void read_config(void)
|
|
|
|
{
|
|
|
|
unsigned char sha1[20];
|
|
|
|
const char *head_ref;
|
|
|
|
int flag;
|
2010-06-04 13:32:11 +04:00
|
|
|
if (default_remote_name) /* did this already */
|
2007-05-12 19:45:53 +04:00
|
|
|
return;
|
2013-09-24 09:19:08 +04:00
|
|
|
default_remote_name = "origin";
|
2007-05-12 19:45:53 +04:00
|
|
|
current_branch = NULL;
|
2011-12-12 15:20:32 +04:00
|
|
|
head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag);
|
2007-05-12 19:45:53 +04:00
|
|
|
if (head_ref && (flag & REF_ISSYMREF) &&
|
2014-06-18 23:48:29 +04:00
|
|
|
skip_prefix(head_ref, "refs/heads/", &head_ref)) {
|
|
|
|
current_branch = make_branch(head_ref, 0);
|
2007-05-12 19:45:53 +04:00
|
|
|
}
|
2008-05-14 21:46:53 +04:00
|
|
|
git_config(handle_config, NULL);
|
2014-02-24 12:59:03 +04:00
|
|
|
if (branch_pushremote_name) {
|
|
|
|
free((char *)pushremote_name);
|
|
|
|
pushremote_name = branch_pushremote_name;
|
|
|
|
}
|
2008-02-20 21:43:53 +03:00
|
|
|
alias_all_urls();
|
2007-05-12 19:45:53 +04:00
|
|
|
}
|
|
|
|
|
2008-08-22 04:16:30 +04:00
|
|
|
/*
|
|
|
|
* This function frees a refspec array.
|
|
|
|
* Warning: code paths should be checked to ensure that the src
|
|
|
|
* and dst pointers are always freeable pointers as well
|
|
|
|
* as the refspec pointer itself.
|
|
|
|
*/
|
2008-09-25 13:41:00 +04:00
|
|
|
static void free_refspecs(struct refspec *refspec, int nr_refspec)
|
2008-08-22 04:16:30 +04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!refspec)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < nr_refspec; i++) {
|
|
|
|
free(refspec[i].src);
|
|
|
|
free(refspec[i].dst);
|
|
|
|
}
|
|
|
|
free(refspec);
|
|
|
|
}
|
|
|
|
|
2008-04-13 13:56:54 +04:00
|
|
|
static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
|
2007-05-12 19:45:59 +04:00
|
|
|
{
|
|
|
|
int i;
|
2014-05-26 19:33:55 +04:00
|
|
|
struct refspec *rs = xcalloc(nr_refspec, sizeof(*rs));
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
|
2007-05-12 19:45:59 +04:00
|
|
|
for (i = 0; i < nr_refspec; i++) {
|
2008-07-27 10:15:51 +04:00
|
|
|
size_t llen;
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
int is_glob;
|
|
|
|
const char *lhs, *rhs;
|
2011-09-16 01:10:25 +04:00
|
|
|
int flags;
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
|
2009-03-13 15:51:33 +03:00
|
|
|
is_glob = 0;
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
|
|
|
|
lhs = refspec[i];
|
|
|
|
if (*lhs == '+') {
|
2007-05-12 19:45:59 +04:00
|
|
|
rs[i].force = 1;
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
lhs++;
|
2007-05-12 19:45:59 +04:00
|
|
|
}
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
|
|
|
|
rhs = strrchr(lhs, ':');
|
2008-04-28 19:32:12 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Before going on, special case ":" (or "+:") as a refspec
|
2013-01-30 00:58:50 +04:00
|
|
|
* for pushing matching refs.
|
2008-04-28 19:32:12 +04:00
|
|
|
*/
|
|
|
|
if (!fetch && rhs == lhs && rhs[1] == '\0') {
|
|
|
|
rs[i].matching = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
if (rhs) {
|
2008-07-27 10:15:51 +04:00
|
|
|
size_t rlen = strlen(++rhs);
|
2009-03-07 09:11:39 +03:00
|
|
|
is_glob = (1 <= rlen && strchr(rhs, '*'));
|
2009-03-07 09:11:36 +03:00
|
|
|
rs[i].dst = xstrndup(rhs, rlen);
|
2007-05-12 19:45:59 +04:00
|
|
|
}
|
2008-03-18 05:05:23 +03:00
|
|
|
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
|
2009-03-07 09:11:39 +03:00
|
|
|
if (1 <= llen && memchr(lhs, '*', llen)) {
|
2008-03-26 07:15:52 +03:00
|
|
|
if ((rhs && !is_glob) || (!rhs && fetch))
|
|
|
|
goto invalid;
|
|
|
|
is_glob = 1;
|
|
|
|
} else if (rhs && is_glob) {
|
|
|
|
goto invalid;
|
2008-03-18 05:05:23 +03:00
|
|
|
}
|
2008-03-26 07:15:52 +03:00
|
|
|
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
rs[i].pattern = is_glob;
|
|
|
|
rs[i].src = xstrndup(lhs, llen);
|
2011-09-16 01:10:25 +04:00
|
|
|
flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
|
|
|
|
if (fetch) {
|
2013-01-30 02:02:15 +04:00
|
|
|
unsigned char unused[40];
|
|
|
|
|
2013-01-30 00:58:50 +04:00
|
|
|
/* LHS */
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
if (!*rs[i].src)
|
2013-01-30 00:58:50 +04:00
|
|
|
; /* empty is ok; it means "HEAD" */
|
2013-01-30 02:02:15 +04:00
|
|
|
else if (llen == 40 && !get_sha1_hex(rs[i].src, unused))
|
|
|
|
rs[i].exact_sha1 = 1; /* ok */
|
2013-01-30 00:58:50 +04:00
|
|
|
else if (!check_refname_format(rs[i].src, flags))
|
|
|
|
; /* valid looking ref is ok */
|
|
|
|
else
|
2011-09-16 01:10:25 +04:00
|
|
|
goto invalid;
|
2013-01-30 00:58:50 +04:00
|
|
|
/* RHS */
|
2011-09-16 01:10:25 +04:00
|
|
|
if (!rs[i].dst)
|
2013-01-30 00:58:50 +04:00
|
|
|
; /* missing is ok; it is the same as empty */
|
2011-09-16 01:10:25 +04:00
|
|
|
else if (!*rs[i].dst)
|
2013-01-30 00:58:50 +04:00
|
|
|
; /* empty is ok; it means "do not store" */
|
|
|
|
else if (!check_refname_format(rs[i].dst, flags))
|
|
|
|
; /* valid looking ref is ok */
|
|
|
|
else
|
2011-09-16 01:10:25 +04:00
|
|
|
goto invalid;
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* LHS
|
|
|
|
* - empty is allowed; it means delete.
|
|
|
|
* - when wildcarded, it must be a valid looking ref.
|
|
|
|
* - otherwise, it must be an extended SHA-1, but
|
|
|
|
* there is no existing way to validate this.
|
|
|
|
*/
|
|
|
|
if (!*rs[i].src)
|
|
|
|
; /* empty is ok */
|
|
|
|
else if (is_glob) {
|
2011-09-16 01:10:25 +04:00
|
|
|
if (check_refname_format(rs[i].src, flags))
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
goto invalid;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
; /* anything goes, for now */
|
|
|
|
/*
|
|
|
|
* RHS
|
|
|
|
* - missing is allowed, but LHS then must be a
|
|
|
|
* valid looking ref.
|
|
|
|
* - empty is not allowed.
|
|
|
|
* - otherwise it must be a valid looking ref.
|
|
|
|
*/
|
|
|
|
if (!rs[i].dst) {
|
2011-09-16 01:10:25 +04:00
|
|
|
if (check_refname_format(rs[i].src, flags))
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
goto invalid;
|
|
|
|
} else if (!*rs[i].dst) {
|
|
|
|
goto invalid;
|
|
|
|
} else {
|
2011-09-16 01:10:25 +04:00
|
|
|
if (check_refname_format(rs[i].dst, flags))
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
goto invalid;
|
|
|
|
}
|
2008-03-18 05:05:23 +03:00
|
|
|
}
|
2007-05-12 19:45:59 +04:00
|
|
|
}
|
|
|
|
return rs;
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
|
|
|
|
invalid:
|
2008-04-13 13:56:54 +04:00
|
|
|
if (verify) {
|
2008-08-22 04:16:30 +04:00
|
|
|
/*
|
|
|
|
* nr_refspec must be greater than zero and i must be valid
|
|
|
|
* since it is only possible to reach this point from within
|
|
|
|
* the for loop above.
|
|
|
|
*/
|
|
|
|
free_refspecs(rs, i+1);
|
2008-04-13 13:56:54 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
die("Invalid refspec '%s'", refspec[i]);
|
|
|
|
}
|
|
|
|
|
2008-04-13 13:56:54 +04:00
|
|
|
int valid_fetch_refspec(const char *fetch_refspec_str)
|
|
|
|
{
|
|
|
|
struct refspec *refspec;
|
|
|
|
|
2010-05-14 13:31:33 +04:00
|
|
|
refspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);
|
2008-08-22 04:16:30 +04:00
|
|
|
free_refspecs(refspec, 1);
|
2008-04-13 13:56:54 +04:00
|
|
|
return !!refspec;
|
|
|
|
}
|
|
|
|
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)
|
|
|
|
{
|
2008-04-13 13:56:54 +04:00
|
|
|
return parse_refspec_internal(nr_refspec, refspec, 1, 0);
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
}
|
|
|
|
|
2008-09-25 13:41:00 +04:00
|
|
|
static struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
{
|
2008-04-13 13:56:54 +04:00
|
|
|
return parse_refspec_internal(nr_refspec, refspec, 0, 0);
|
2007-05-12 19:45:59 +04:00
|
|
|
}
|
|
|
|
|
2009-11-18 04:42:28 +03:00
|
|
|
void free_refspec(int nr_refspec, struct refspec *refspec)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < nr_refspec; i++) {
|
|
|
|
free(refspec[i].src);
|
|
|
|
free(refspec[i].dst);
|
|
|
|
}
|
|
|
|
free(refspec);
|
|
|
|
}
|
|
|
|
|
2008-02-15 22:14:18 +03:00
|
|
|
static int valid_remote_nick(const char *name)
|
|
|
|
{
|
2009-01-10 15:07:50 +03:00
|
|
|
if (!name[0] || is_dot_or_dotdot(name))
|
2008-02-15 22:14:18 +03:00
|
|
|
return 0;
|
|
|
|
return !strchr(name, '/'); /* no slash */
|
|
|
|
}
|
|
|
|
|
remote.c: introduce a way to have different remotes for fetch/push
Currently, do_push() in push.c calls remote_get(), which gets the
configured remote for fetching and pushing. Replace this call with a
call to pushremote_get() instead, a new function that will return the
remote configured specifically for pushing. This function tries to
work with the string pushremote_name, before falling back to the
codepath of remote_get(). This patch has no visible impact, but
serves to enable future patches to introduce configuration variables
to set pushremote_name. For example, you can now do the following in
handle_config():
if (!strcmp(key, "remote.pushdefault"))
git_config_string(&pushremote_name, key, value);
Then, pushes will automatically go to the remote specified by
remote.pushdefault.
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
Reviewed-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-02 11:40:32 +04:00
|
|
|
static struct remote *remote_get_1(const char *name, const char *pushremote_name)
|
2007-05-12 19:45:53 +04:00
|
|
|
{
|
|
|
|
struct remote *ret;
|
Give error when no remote is configured
When there's no explicitly-named remote, we use the remote specified
for the current branch, which in turn defaults to "origin". But it
this case should require the remote to actually be configured, and not
fall back to the path "origin".
Possibly, the config file's "remote = something" should require the
something to be a configured remote instead of a bare repository URL,
but we actually test with a bare repository URL.
In fetch, we were giving the sensible error message when coming up
with a URL failed, but this wasn't actually reachable, so move that
error up and use it when appropriate.
In push, we need a new error message, because the old one (formerly
unreachable without a lot of help) used the repo name, which was NULL.
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-11 08:47:20 +03:00
|
|
|
int name_given = 0;
|
2007-05-12 19:45:53 +04:00
|
|
|
|
Give error when no remote is configured
When there's no explicitly-named remote, we use the remote specified
for the current branch, which in turn defaults to "origin". But it
this case should require the remote to actually be configured, and not
fall back to the path "origin".
Possibly, the config file's "remote = something" should require the
something to be a configured remote instead of a bare repository URL,
but we actually test with a bare repository URL.
In fetch, we were giving the sensible error message when coming up
with a URL failed, but this wasn't actually reachable, so move that
error up and use it when appropriate.
In push, we need a new error message, because the old one (formerly
unreachable without a lot of help) used the repo name, which was NULL.
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-11 08:47:20 +03:00
|
|
|
if (name)
|
|
|
|
name_given = 1;
|
|
|
|
else {
|
remote.c: introduce a way to have different remotes for fetch/push
Currently, do_push() in push.c calls remote_get(), which gets the
configured remote for fetching and pushing. Replace this call with a
call to pushremote_get() instead, a new function that will return the
remote configured specifically for pushing. This function tries to
work with the string pushremote_name, before falling back to the
codepath of remote_get(). This patch has no visible impact, but
serves to enable future patches to introduce configuration variables
to set pushremote_name. For example, you can now do the following in
handle_config():
if (!strcmp(key, "remote.pushdefault"))
git_config_string(&pushremote_name, key, value);
Then, pushes will automatically go to the remote specified by
remote.pushdefault.
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
Reviewed-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-02 11:40:32 +04:00
|
|
|
if (pushremote_name) {
|
|
|
|
name = pushremote_name;
|
|
|
|
name_given = 1;
|
|
|
|
} else {
|
|
|
|
name = default_remote_name;
|
|
|
|
name_given = explicit_default_remote_name;
|
|
|
|
}
|
Give error when no remote is configured
When there's no explicitly-named remote, we use the remote specified
for the current branch, which in turn defaults to "origin". But it
this case should require the remote to actually be configured, and not
fall back to the path "origin".
Possibly, the config file's "remote = something" should require the
something to be a configured remote instead of a bare repository URL,
but we actually test with a bare repository URL.
In fetch, we were giving the sensible error message when coming up
with a URL failed, but this wasn't actually reachable, so move that
error up and use it when appropriate.
In push, we need a new error message, because the old one (formerly
unreachable without a lot of help) used the repo name, which was NULL.
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-11 08:47:20 +03:00
|
|
|
}
|
2009-03-16 10:35:09 +03:00
|
|
|
|
2007-05-12 19:45:53 +04:00
|
|
|
ret = make_remote(name, 0);
|
2008-02-15 22:14:18 +03:00
|
|
|
if (valid_remote_nick(name)) {
|
2009-11-18 04:42:23 +03:00
|
|
|
if (!valid_remote(ret))
|
2007-05-12 19:45:53 +04:00
|
|
|
read_remotes_file(ret);
|
2009-11-18 04:42:23 +03:00
|
|
|
if (!valid_remote(ret))
|
2007-05-12 19:45:53 +04:00
|
|
|
read_branches_file(ret);
|
|
|
|
}
|
2009-11-18 04:42:23 +03:00
|
|
|
if (name_given && !valid_remote(ret))
|
2008-02-20 21:43:53 +03:00
|
|
|
add_url_alias(ret, name);
|
2009-11-18 04:42:23 +03:00
|
|
|
if (!valid_remote(ret))
|
2007-05-12 19:45:53 +04:00
|
|
|
return NULL;
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
ret->fetch = parse_fetch_refspec(ret->fetch_refspec_nr, ret->fetch_refspec);
|
|
|
|
ret->push = parse_push_refspec(ret->push_refspec_nr, ret->push_refspec);
|
2007-05-12 19:45:53 +04:00
|
|
|
return ret;
|
|
|
|
}
|
2007-05-12 19:45:59 +04:00
|
|
|
|
remote.c: introduce a way to have different remotes for fetch/push
Currently, do_push() in push.c calls remote_get(), which gets the
configured remote for fetching and pushing. Replace this call with a
call to pushremote_get() instead, a new function that will return the
remote configured specifically for pushing. This function tries to
work with the string pushremote_name, before falling back to the
codepath of remote_get(). This patch has no visible impact, but
serves to enable future patches to introduce configuration variables
to set pushremote_name. For example, you can now do the following in
handle_config():
if (!strcmp(key, "remote.pushdefault"))
git_config_string(&pushremote_name, key, value);
Then, pushes will automatically go to the remote specified by
remote.pushdefault.
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
Reviewed-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-04-02 11:40:32 +04:00
|
|
|
struct remote *remote_get(const char *name)
|
|
|
|
{
|
|
|
|
read_config();
|
|
|
|
return remote_get_1(name, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct remote *pushremote_get(const char *name)
|
|
|
|
{
|
|
|
|
read_config();
|
|
|
|
return remote_get_1(name, pushremote_name);
|
|
|
|
}
|
|
|
|
|
2009-04-06 17:41:01 +04:00
|
|
|
int remote_is_configured(const char *name)
|
|
|
|
{
|
2014-07-29 18:43:39 +04:00
|
|
|
struct remotes_hash_key lookup;
|
|
|
|
struct hashmap_entry lookup_entry;
|
2009-04-06 17:41:01 +04:00
|
|
|
read_config();
|
|
|
|
|
2014-07-29 18:43:39 +04:00
|
|
|
init_remotes_hash();
|
|
|
|
lookup.str = name;
|
|
|
|
lookup.len = strlen(name);
|
|
|
|
hashmap_entry_init(&lookup_entry, memhash(name, lookup.len));
|
|
|
|
|
|
|
|
return hashmap_get(&remotes_hash, &lookup_entry, &lookup) != NULL;
|
2009-04-06 17:41:01 +04:00
|
|
|
}
|
|
|
|
|
2007-07-10 21:48:40 +04:00
|
|
|
int for_each_remote(each_remote_fn fn, void *priv)
|
|
|
|
{
|
|
|
|
int i, result = 0;
|
|
|
|
read_config();
|
2008-02-19 07:41:41 +03:00
|
|
|
for (i = 0; i < remotes_nr && !result; i++) {
|
2007-07-10 21:48:40 +04:00
|
|
|
struct remote *r = remotes[i];
|
|
|
|
if (!r)
|
|
|
|
continue;
|
|
|
|
if (!r->fetch)
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
r->fetch = parse_fetch_refspec(r->fetch_refspec_nr,
|
|
|
|
r->fetch_refspec);
|
2007-07-10 21:48:40 +04:00
|
|
|
if (!r->push)
|
remote.c: Fix overtight refspec validation
We tightened the refspec validation code in an earlier commit ef00d15
(Tighten refspec processing, 2008-03-17) per my suggestion, but the
suggestion was misguided to begin with and it broke this usage:
$ git push origin HEAD~12:master
The syntax of push refspecs and fetch refspecs are similar in that they
are both colon separated LHS and RHS (possibly prefixed with a + to
force), but the similarity ends there. For example, LHS in a push refspec
can be anything that evaluates to a valid object name at runtime (except
when colon and RHS is missing, or it is a glob), while it must be a
valid-looking refname in a fetch refspec. To validate them correctly, the
caller needs to be able to say which kind of refspecs they are. It is
unreasonable to keep a single interface that cannot tell which kind it is
dealing with, and ask it to behave sensibly.
This commit separates the parsing of the two into different functions, and
clarifies the code to implement the parsing proper (i.e. splitting into
two parts, making sure both sides are wildcard or neither side is).
This happens to also allow pushing a commit named with the esoteric "look
for that string" syntax:
$ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-03-21 09:34:37 +03:00
|
|
|
r->push = parse_push_refspec(r->push_refspec_nr,
|
|
|
|
r->push_refspec);
|
2007-07-10 21:48:40 +04:00
|
|
|
result = fn(r, priv);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-10-30 09:33:10 +04:00
|
|
|
static void handle_duplicate(struct ref *ref1, struct ref *ref2)
|
|
|
|
{
|
2013-10-30 09:33:12 +04:00
|
|
|
if (strcmp(ref1->name, ref2->name)) {
|
|
|
|
if (ref1->fetch_head_status != FETCH_HEAD_IGNORE &&
|
|
|
|
ref2->fetch_head_status != FETCH_HEAD_IGNORE) {
|
|
|
|
die(_("Cannot fetch both %s and %s to %s"),
|
|
|
|
ref1->name, ref2->name, ref2->peer_ref->name);
|
|
|
|
} else if (ref1->fetch_head_status != FETCH_HEAD_IGNORE &&
|
|
|
|
ref2->fetch_head_status == FETCH_HEAD_IGNORE) {
|
|
|
|
warning(_("%s usually tracks %s, not %s"),
|
|
|
|
ref2->peer_ref->name, ref2->name, ref1->name);
|
|
|
|
} else if (ref1->fetch_head_status == FETCH_HEAD_IGNORE &&
|
|
|
|
ref2->fetch_head_status == FETCH_HEAD_IGNORE) {
|
|
|
|
die(_("%s tracks both %s and %s"),
|
|
|
|
ref2->peer_ref->name, ref1->name, ref2->name);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* This last possibility doesn't occur because
|
|
|
|
* FETCH_HEAD_IGNORE entries always appear at
|
|
|
|
* the end of the list.
|
|
|
|
*/
|
|
|
|
die(_("Internal error"));
|
|
|
|
}
|
|
|
|
}
|
2013-10-30 09:33:10 +04:00
|
|
|
free(ref2->peer_ref);
|
|
|
|
free(ref2);
|
|
|
|
}
|
|
|
|
|
2013-10-30 09:33:09 +04:00
|
|
|
struct ref *ref_remove_duplicates(struct ref *ref_map)
|
2007-10-08 08:25:07 +04:00
|
|
|
{
|
2010-07-04 23:46:19 +04:00
|
|
|
struct string_list refs = STRING_LIST_INIT_NODUP;
|
2013-10-30 09:33:09 +04:00
|
|
|
struct ref *retval = NULL;
|
|
|
|
struct ref **p = &retval;
|
2009-10-26 00:28:11 +03:00
|
|
|
|
2013-10-30 09:33:09 +04:00
|
|
|
while (ref_map) {
|
|
|
|
struct ref *ref = ref_map;
|
|
|
|
|
|
|
|
ref_map = ref_map->next;
|
|
|
|
ref->next = NULL;
|
2009-10-26 00:28:11 +03:00
|
|
|
|
2013-10-30 09:33:09 +04:00
|
|
|
if (!ref->peer_ref) {
|
|
|
|
*p = ref;
|
|
|
|
p = &ref->next;
|
2013-10-30 09:33:07 +04:00
|
|
|
} else {
|
2013-10-30 09:33:09 +04:00
|
|
|
struct string_list_item *item =
|
|
|
|
string_list_insert(&refs, ref->peer_ref->name);
|
|
|
|
|
|
|
|
if (item->util) {
|
|
|
|
/* Entry already existed */
|
2013-10-30 09:33:10 +04:00
|
|
|
handle_duplicate((struct ref *)item->util, ref);
|
2013-10-30 09:33:09 +04:00
|
|
|
} else {
|
|
|
|
*p = ref;
|
|
|
|
p = &ref->next;
|
|
|
|
item->util = ref;
|
|
|
|
}
|
2007-10-08 08:25:07 +04:00
|
|
|
}
|
|
|
|
}
|
2013-10-30 09:33:09 +04:00
|
|
|
|
2009-10-26 00:28:11 +03:00
|
|
|
string_list_clear(&refs, 0);
|
2013-10-30 09:33:09 +04:00
|
|
|
return retval;
|
2007-10-08 08:25:07 +04:00
|
|
|
}
|
|
|
|
|
2007-09-19 08:49:27 +04:00
|
|
|
int remote_has_url(struct remote *remote, const char *url)
|
2007-05-12 19:46:03 +04:00
|
|
|
{
|
|
|
|
int i;
|
2007-09-19 08:49:27 +04:00
|
|
|
for (i = 0; i < remote->url_nr; i++) {
|
|
|
|
if (!strcmp(remote->url[i], url))
|
2007-05-12 19:46:03 +04:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-07 09:11:34 +03:00
|
|
|
static int match_name_with_pattern(const char *key, const char *name,
|
|
|
|
const char *value, char **result)
|
2009-03-07 09:11:29 +03:00
|
|
|
{
|
2009-03-07 09:11:36 +03:00
|
|
|
const char *kstar = strchr(key, '*');
|
|
|
|
size_t klen;
|
2009-03-07 09:11:39 +03:00
|
|
|
size_t ksuffixlen;
|
|
|
|
size_t namelen;
|
2009-03-07 09:11:36 +03:00
|
|
|
int ret;
|
|
|
|
if (!kstar)
|
|
|
|
die("Key '%s' of pattern had no '*'", key);
|
|
|
|
klen = kstar - key;
|
2009-03-07 09:11:39 +03:00
|
|
|
ksuffixlen = strlen(kstar + 1);
|
|
|
|
namelen = strlen(name);
|
|
|
|
ret = !strncmp(name, key, klen) && namelen >= klen + ksuffixlen &&
|
|
|
|
!memcmp(name + namelen - ksuffixlen, kstar + 1, ksuffixlen);
|
2009-03-07 09:11:34 +03:00
|
|
|
if (ret && value) {
|
2009-03-07 09:11:36 +03:00
|
|
|
const char *vstar = strchr(value, '*');
|
|
|
|
size_t vlen;
|
2009-03-07 09:11:39 +03:00
|
|
|
size_t vsuffixlen;
|
2009-03-07 09:11:36 +03:00
|
|
|
if (!vstar)
|
|
|
|
die("Value '%s' of pattern has no '*'", value);
|
|
|
|
vlen = vstar - value;
|
2009-03-07 09:11:39 +03:00
|
|
|
vsuffixlen = strlen(vstar + 1);
|
|
|
|
*result = xmalloc(vlen + vsuffixlen +
|
2009-03-07 09:11:34 +03:00
|
|
|
strlen(name) -
|
2009-03-07 09:11:39 +03:00
|
|
|
klen - ksuffixlen + 1);
|
|
|
|
strncpy(*result, value, vlen);
|
|
|
|
strncpy(*result + vlen,
|
|
|
|
name + klen, namelen - klen - ksuffixlen);
|
|
|
|
strcpy(*result + vlen + namelen - klen - ksuffixlen,
|
|
|
|
vstar + 1);
|
2009-03-07 09:11:34 +03:00
|
|
|
}
|
2009-03-07 09:11:29 +03:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-02-27 13:00:10 +04:00
|
|
|
static void query_refspecs_multiple(struct refspec *refs, int ref_count, struct refspec *query, struct string_list *results)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int find_src = !query->src;
|
|
|
|
|
|
|
|
if (find_src && !query->dst)
|
|
|
|
error("query_refspecs_multiple: need either src or dst");
|
|
|
|
|
|
|
|
for (i = 0; i < ref_count; i++) {
|
|
|
|
struct refspec *refspec = &refs[i];
|
|
|
|
const char *key = find_src ? refspec->dst : refspec->src;
|
|
|
|
const char *value = find_src ? refspec->src : refspec->dst;
|
|
|
|
const char *needle = find_src ? query->dst : query->src;
|
|
|
|
char **result = find_src ? &query->src : &query->dst;
|
|
|
|
|
|
|
|
if (!refspec->dst)
|
|
|
|
continue;
|
|
|
|
if (refspec->pattern) {
|
|
|
|
if (match_name_with_pattern(key, needle, value, result))
|
|
|
|
string_list_append_nodup(results, *result);
|
|
|
|
} else if (!strcmp(needle, key)) {
|
|
|
|
string_list_append(results, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
push: use remote.$name.push as a refmap
Since f2690487 (fetch: opportunistically update tracking refs,
2013-05-11), we stopped taking a non-storing refspec given on the
command line of "git fetch" literally, and instead started mapping
it via remote.$name.fetch refspecs. This allows
$ git fetch origin master
from the 'origin' repository, which is configured with
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
to update refs/remotes/origin/master with the result, as if the
command line were
$ git fetch origin +master:refs/remotes/origin/master
to reduce surprises and improve usability. Before that change, a
refspec on the command line without a colon was only to fetch the
history and leave the result in FETCH_HEAD, without updating the
remote-tracking branches.
When you are simulating a fetch from you by your mothership with a
push by you into your mothership, instead of having:
[remote "satellite"]
fetch = +refs/heads/*:refs/remotes/satellite/*
on the mothership repository and running:
mothership$ git fetch satellite
you would have:
[remote "mothership"]
push = +refs/heads/*:refs/remotes/satellite/*
on your satellite machine, and run:
satellite$ git push mothership
Because we so far did not make the corresponding change to the push
side, this command:
satellite$ git push mothership master
does _not_ allow you on the satellite to only push 'master' out but
still to the usual destination (i.e. refs/remotes/satellite/master).
Implement the logic to map an unqualified refspec given on the
command line via the remote.$name.push refspec. This will bring a
bit more symmetry between "fetch" and "push".
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-12-04 03:41:15 +04:00
|
|
|
int query_refspecs(struct refspec *refs, int ref_count, struct refspec *query)
|
2009-11-18 04:42:28 +03:00
|
|
|
{
|
|
|
|
int i;
|
2011-10-15 09:04:24 +04:00
|
|
|
int find_src = !query->src;
|
2013-10-30 09:33:01 +04:00
|
|
|
const char *needle = find_src ? query->dst : query->src;
|
|
|
|
char **result = find_src ? &query->src : &query->dst;
|
2009-11-18 04:42:28 +03:00
|
|
|
|
2011-10-15 09:04:24 +04:00
|
|
|
if (find_src && !query->dst)
|
|
|
|
return error("query_refspecs: need either src or dst");
|
2007-07-10 21:48:40 +04:00
|
|
|
|
2011-10-15 09:04:24 +04:00
|
|
|
for (i = 0; i < ref_count; i++) {
|
|
|
|
struct refspec *refspec = &refs[i];
|
|
|
|
const char *key = find_src ? refspec->dst : refspec->src;
|
|
|
|
const char *value = find_src ? refspec->src : refspec->dst;
|
2007-07-10 21:48:40 +04:00
|
|
|
|
2011-10-15 09:04:24 +04:00
|
|
|
if (!refspec->dst)
|
2007-05-12 19:46:03 +04:00
|
|
|
continue;
|
2011-10-15 09:04:24 +04:00
|
|
|
if (refspec->pattern) {
|
2009-03-07 09:11:34 +03:00
|
|
|
if (match_name_with_pattern(key, needle, value, result)) {
|
2011-10-15 09:04:24 +04:00
|
|
|
query->force = refspec->force;
|
2007-05-12 19:46:03 +04:00
|
|
|
return 0;
|
|
|
|
}
|
2007-07-10 21:48:40 +04:00
|
|
|
} else if (!strcmp(needle, key)) {
|
|
|
|
*result = xstrdup(value);
|
2011-10-15 09:04:24 +04:00
|
|
|
query->force = refspec->force;
|
2007-07-10 21:48:40 +04:00
|
|
|
return 0;
|
2007-05-12 19:46:03 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-10-15 09:04:24 +04:00
|
|
|
char *apply_refspecs(struct refspec *refspecs, int nr_refspec,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
struct refspec query;
|
|
|
|
|
|
|
|
memset(&query, 0, sizeof(struct refspec));
|
|
|
|
query.src = (char *)name;
|
|
|
|
|
|
|
|
if (query_refspecs(refspecs, nr_refspec, &query))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return query.dst;
|
|
|
|
}
|
|
|
|
|
|
|
|
int remote_find_tracking(struct remote *remote, struct refspec *refspec)
|
|
|
|
{
|
|
|
|
return query_refspecs(remote->fetch, remote->fetch_refspec_nr, refspec);
|
|
|
|
}
|
|
|
|
|
2008-10-18 12:37:40 +04:00
|
|
|
static struct ref *alloc_ref_with_prefix(const char *prefix, size_t prefixlen,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
size_t len = strlen(name);
|
|
|
|
struct ref *ref = xcalloc(1, sizeof(struct ref) + prefixlen + len + 1);
|
|
|
|
memcpy(ref->name, prefix, prefixlen);
|
|
|
|
memcpy(ref->name + prefixlen, name, len);
|
|
|
|
return ref;
|
|
|
|
}
|
|
|
|
|
2008-10-18 12:44:18 +04:00
|
|
|
struct ref *alloc_ref(const char *name)
|
2007-07-10 08:47:23 +04:00
|
|
|
{
|
2008-10-18 12:44:18 +04:00
|
|
|
return alloc_ref_with_prefix("", 0, name);
|
2008-05-11 03:26:58 +04:00
|
|
|
}
|
|
|
|
|
2011-06-08 03:03:03 +04:00
|
|
|
struct ref *copy_ref(const struct ref *ref)
|
2007-09-11 07:03:08 +04:00
|
|
|
{
|
2009-02-27 22:10:04 +03:00
|
|
|
struct ref *cpy;
|
|
|
|
size_t len;
|
|
|
|
if (!ref)
|
|
|
|
return NULL;
|
|
|
|
len = strlen(ref->name);
|
|
|
|
cpy = xmalloc(sizeof(struct ref) + len + 1);
|
|
|
|
memcpy(cpy, ref, sizeof(struct ref) + len + 1);
|
|
|
|
cpy->next = NULL;
|
|
|
|
cpy->symref = ref->symref ? xstrdup(ref->symref) : NULL;
|
|
|
|
cpy->remote_status = ref->remote_status ? xstrdup(ref->remote_status) : NULL;
|
|
|
|
cpy->peer_ref = copy_ref(ref->peer_ref);
|
|
|
|
return cpy;
|
2007-09-11 07:03:08 +04:00
|
|
|
}
|
|
|
|
|
2007-10-30 04:05:40 +03:00
|
|
|
struct ref *copy_ref_list(const struct ref *ref)
|
|
|
|
{
|
|
|
|
struct ref *ret = NULL;
|
|
|
|
struct ref **tail = &ret;
|
|
|
|
while (ref) {
|
|
|
|
*tail = copy_ref(ref);
|
|
|
|
ref = ref->next;
|
|
|
|
tail = &((*tail)->next);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-09-25 13:41:00 +04:00
|
|
|
static void free_ref(struct ref *ref)
|
2008-04-26 23:53:12 +04:00
|
|
|
{
|
|
|
|
if (!ref)
|
|
|
|
return;
|
2009-02-27 22:10:04 +03:00
|
|
|
free_ref(ref->peer_ref);
|
2008-04-26 23:53:12 +04:00
|
|
|
free(ref->remote_status);
|
|
|
|
free(ref->symref);
|
|
|
|
free(ref);
|
|
|
|
}
|
|
|
|
|
2007-07-10 08:47:23 +04:00
|
|
|
void free_refs(struct ref *ref)
|
|
|
|
{
|
|
|
|
struct ref *next;
|
|
|
|
while (ref) {
|
|
|
|
next = ref->next;
|
2008-04-26 23:53:12 +04:00
|
|
|
free_ref(ref);
|
2007-07-10 08:47:23 +04:00
|
|
|
ref = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-22 02:19:28 +04:00
|
|
|
int ref_compare_name(const void *va, const void *vb)
|
|
|
|
{
|
|
|
|
const struct ref *a = va, *b = vb;
|
|
|
|
return strcmp(a->name, b->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *ref_list_get_next(const void *a)
|
|
|
|
{
|
|
|
|
return ((const struct ref *)a)->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ref_list_set_next(void *a, void *next)
|
|
|
|
{
|
|
|
|
((struct ref *)a)->next = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
void sort_ref_list(struct ref **l, int (*cmp)(const void *, const void *))
|
|
|
|
{
|
|
|
|
*l = llist_mergesort(*l, ref_list_get_next, ref_list_set_next, cmp);
|
|
|
|
}
|
|
|
|
|
push: use remote.$name.push as a refmap
Since f2690487 (fetch: opportunistically update tracking refs,
2013-05-11), we stopped taking a non-storing refspec given on the
command line of "git fetch" literally, and instead started mapping
it via remote.$name.fetch refspecs. This allows
$ git fetch origin master
from the 'origin' repository, which is configured with
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
to update refs/remotes/origin/master with the result, as if the
command line were
$ git fetch origin +master:refs/remotes/origin/master
to reduce surprises and improve usability. Before that change, a
refspec on the command line without a colon was only to fetch the
history and leave the result in FETCH_HEAD, without updating the
remote-tracking branches.
When you are simulating a fetch from you by your mothership with a
push by you into your mothership, instead of having:
[remote "satellite"]
fetch = +refs/heads/*:refs/remotes/satellite/*
on the mothership repository and running:
mothership$ git fetch satellite
you would have:
[remote "mothership"]
push = +refs/heads/*:refs/remotes/satellite/*
on your satellite machine, and run:
satellite$ git push mothership
Because we so far did not make the corresponding change to the push
side, this command:
satellite$ git push mothership master
does _not_ allow you on the satellite to only push 'master' out but
still to the usual destination (i.e. refs/remotes/satellite/master).
Implement the logic to map an unqualified refspec given on the
command line via the remote.$name.push refspec. This will bring a
bit more symmetry between "fetch" and "push".
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-12-04 03:41:15 +04:00
|
|
|
int count_refspec_match(const char *pattern,
|
|
|
|
struct ref *refs,
|
|
|
|
struct ref **matched_ref)
|
2007-05-12 19:45:59 +04:00
|
|
|
{
|
|
|
|
int patlen = strlen(pattern);
|
|
|
|
struct ref *matched_weak = NULL;
|
|
|
|
struct ref *matched = NULL;
|
|
|
|
int weak_match = 0;
|
|
|
|
int match = 0;
|
|
|
|
|
|
|
|
for (weak_match = match = 0; refs; refs = refs->next) {
|
|
|
|
char *name = refs->name;
|
|
|
|
int namelen = strlen(name);
|
|
|
|
|
2014-01-14 07:16:07 +04:00
|
|
|
if (!refname_match(pattern, name))
|
2007-05-12 19:45:59 +04:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* A match is "weak" if it is with refs outside
|
|
|
|
* heads or tags, and did not specify the pattern
|
|
|
|
* in full (e.g. "refs/remotes/origin/master") or at
|
|
|
|
* least from the toplevel (e.g. "remotes/origin/master");
|
|
|
|
* otherwise "git push $URL master" would result in
|
|
|
|
* ambiguity between remotes/origin/master and heads/master
|
|
|
|
* at the remote site.
|
|
|
|
*/
|
|
|
|
if (namelen != patlen &&
|
|
|
|
patlen != namelen - 5 &&
|
2013-12-01 00:55:40 +04:00
|
|
|
!starts_with(name, "refs/heads/") &&
|
|
|
|
!starts_with(name, "refs/tags/")) {
|
2007-05-12 19:45:59 +04:00
|
|
|
/* We want to catch the case where only weak
|
|
|
|
* matches are found and there are multiple
|
|
|
|
* matches, and where more than one strong
|
|
|
|
* matches are found, as ambiguous. One
|
|
|
|
* strong match with zero or more weak matches
|
|
|
|
* are acceptable as a unique match.
|
|
|
|
*/
|
|
|
|
matched_weak = refs;
|
|
|
|
weak_match++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
matched = refs;
|
|
|
|
match++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!matched) {
|
2014-03-05 23:03:43 +04:00
|
|
|
if (matched_ref)
|
|
|
|
*matched_ref = matched_weak;
|
2007-05-12 19:45:59 +04:00
|
|
|
return weak_match;
|
|
|
|
}
|
|
|
|
else {
|
2014-03-05 23:03:43 +04:00
|
|
|
if (matched_ref)
|
|
|
|
*matched_ref = matched;
|
2007-05-12 19:45:59 +04:00
|
|
|
return match;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-10 08:47:26 +04:00
|
|
|
static void tail_link_ref(struct ref *ref, struct ref ***tail)
|
2007-05-12 19:45:59 +04:00
|
|
|
{
|
|
|
|
**tail = ref;
|
2007-07-10 08:47:26 +04:00
|
|
|
while (ref->next)
|
|
|
|
ref = ref->next;
|
2007-05-12 19:45:59 +04:00
|
|
|
*tail = &ref->next;
|
|
|
|
}
|
|
|
|
|
2012-02-23 02:43:40 +04:00
|
|
|
static struct ref *alloc_delete_ref(void)
|
|
|
|
{
|
|
|
|
struct ref *ref = alloc_ref("(delete)");
|
|
|
|
hashclr(ref->new_sha1);
|
|
|
|
return ref;
|
|
|
|
}
|
|
|
|
|
2014-03-05 23:03:43 +04:00
|
|
|
static int try_explicit_object_name(const char *name,
|
|
|
|
struct ref **match)
|
2007-05-12 19:45:59 +04:00
|
|
|
{
|
|
|
|
unsigned char sha1[20];
|
|
|
|
|
2014-03-05 23:03:43 +04:00
|
|
|
if (!*name) {
|
|
|
|
if (match)
|
|
|
|
*match = alloc_delete_ref();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-05-12 19:45:59 +04:00
|
|
|
if (get_sha1(name, sha1))
|
2014-03-05 23:03:43 +04:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (match) {
|
|
|
|
*match = alloc_ref(name);
|
|
|
|
hashcpy((*match)->new_sha1, sha1);
|
|
|
|
}
|
|
|
|
return 0;
|
2007-05-12 19:45:59 +04:00
|
|
|
}
|
|
|
|
|
2007-07-10 08:47:26 +04:00
|
|
|
static struct ref *make_linked_ref(const char *name, struct ref ***tail)
|
2007-05-12 19:45:59 +04:00
|
|
|
{
|
2008-10-18 12:44:18 +04:00
|
|
|
struct ref *ret = alloc_ref(name);
|
2007-07-10 08:47:26 +04:00
|
|
|
tail_link_ref(ret, tail);
|
|
|
|
return ret;
|
2007-06-09 11:07:34 +04:00
|
|
|
}
|
2007-05-25 09:20:56 +04:00
|
|
|
|
push: allow unqualified dest refspecs to DWIM
Previously, a push like:
git push remote src:dst
would go through the following steps:
1. check for an unambiguous 'dst' on the remote; if it
exists, then push to that ref
2. otherwise, check if 'dst' begins with 'refs/'; if it
does, create a new ref
3. otherwise, complain because we don't know where in the
refs hierarchy to put 'dst'
However, in some cases, we can guess about the ref type of
'dst' based on the ref type of 'src'. Specifically, before
complaining we now check:
2.5. if 'src' resolves to a ref starting with refs/heads
or refs/tags, then prepend that to 'dst'
So now this creates a new branch on the remote, whereas it
previously failed with an error message:
git push master:newbranch
Note that, by design, we limit this DWIM behavior only to
source refs which resolve exactly (including symrefs which
resolve to existing refs). We still complain on a partial
destination refspec if the source is a raw sha1, or a ref
expression such as 'master~10'.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-04-23 13:16:06 +04:00
|
|
|
static char *guess_ref(const char *name, struct ref *peer)
|
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
unsigned char sha1[20];
|
|
|
|
|
2011-12-12 15:20:32 +04:00
|
|
|
const char *r = resolve_ref_unsafe(peer->name, sha1, 1, NULL);
|
push: allow unqualified dest refspecs to DWIM
Previously, a push like:
git push remote src:dst
would go through the following steps:
1. check for an unambiguous 'dst' on the remote; if it
exists, then push to that ref
2. otherwise, check if 'dst' begins with 'refs/'; if it
does, create a new ref
3. otherwise, complain because we don't know where in the
refs hierarchy to put 'dst'
However, in some cases, we can guess about the ref type of
'dst' based on the ref type of 'src'. Specifically, before
complaining we now check:
2.5. if 'src' resolves to a ref starting with refs/heads
or refs/tags, then prepend that to 'dst'
So now this creates a new branch on the remote, whereas it
previously failed with an error message:
git push master:newbranch
Note that, by design, we limit this DWIM behavior only to
source refs which resolve exactly (including symrefs which
resolve to existing refs). We still complain on a partial
destination refspec if the source is a raw sha1, or a ref
expression such as 'master~10'.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-04-23 13:16:06 +04:00
|
|
|
if (!r)
|
|
|
|
return NULL;
|
|
|
|
|
2013-12-01 00:55:40 +04:00
|
|
|
if (starts_with(r, "refs/heads/"))
|
push: allow unqualified dest refspecs to DWIM
Previously, a push like:
git push remote src:dst
would go through the following steps:
1. check for an unambiguous 'dst' on the remote; if it
exists, then push to that ref
2. otherwise, check if 'dst' begins with 'refs/'; if it
does, create a new ref
3. otherwise, complain because we don't know where in the
refs hierarchy to put 'dst'
However, in some cases, we can guess about the ref type of
'dst' based on the ref type of 'src'. Specifically, before
complaining we now check:
2.5. if 'src' resolves to a ref starting with refs/heads
or refs/tags, then prepend that to 'dst'
So now this creates a new branch on the remote, whereas it
previously failed with an error message:
git push master:newbranch
Note that, by design, we limit this DWIM behavior only to
source refs which resolve exactly (including symrefs which
resolve to existing refs). We still complain on a partial
destination refspec if the source is a raw sha1, or a ref
expression such as 'master~10'.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-04-23 13:16:06 +04:00
|
|
|
strbuf_addstr(&buf, "refs/heads/");
|
2013-12-01 00:55:40 +04:00
|
|
|
else if (starts_with(r, "refs/tags/"))
|
push: allow unqualified dest refspecs to DWIM
Previously, a push like:
git push remote src:dst
would go through the following steps:
1. check for an unambiguous 'dst' on the remote; if it
exists, then push to that ref
2. otherwise, check if 'dst' begins with 'refs/'; if it
does, create a new ref
3. otherwise, complain because we don't know where in the
refs hierarchy to put 'dst'
However, in some cases, we can guess about the ref type of
'dst' based on the ref type of 'src'. Specifically, before
complaining we now check:
2.5. if 'src' resolves to a ref starting with refs/heads
or refs/tags, then prepend that to 'dst'
So now this creates a new branch on the remote, whereas it
previously failed with an error message:
git push master:newbranch
Note that, by design, we limit this DWIM behavior only to
source refs which resolve exactly (including symrefs which
resolve to existing refs). We still complain on a partial
destination refspec if the source is a raw sha1, or a ref
expression such as 'master~10'.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-04-23 13:16:06 +04:00
|
|
|
strbuf_addstr(&buf, "refs/tags/");
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
strbuf_addstr(&buf, name);
|
|
|
|
return strbuf_detach(&buf, NULL);
|
|
|
|
}
|
|
|
|
|
2014-03-05 23:03:21 +04:00
|
|
|
static int match_explicit_lhs(struct ref *src,
|
|
|
|
struct refspec *rs,
|
|
|
|
struct ref **match,
|
|
|
|
int *allocated_match)
|
|
|
|
{
|
|
|
|
switch (count_refspec_match(rs->src, src, match)) {
|
|
|
|
case 1:
|
2014-03-05 23:03:43 +04:00
|
|
|
if (allocated_match)
|
|
|
|
*allocated_match = 0;
|
2014-03-05 23:03:21 +04:00
|
|
|
return 0;
|
|
|
|
case 0:
|
|
|
|
/* The source could be in the get_sha1() format
|
|
|
|
* not a reference name. :refs/other is a
|
|
|
|
* way to delete 'other' ref at the remote end.
|
|
|
|
*/
|
2014-03-05 23:03:43 +04:00
|
|
|
if (try_explicit_object_name(rs->src, match) < 0)
|
2014-03-05 23:03:21 +04:00
|
|
|
return error("src refspec %s does not match any.", rs->src);
|
2014-03-05 23:03:43 +04:00
|
|
|
if (allocated_match)
|
|
|
|
*allocated_match = 1;
|
2014-03-05 23:03:21 +04:00
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return error("src refspec %s matches more than one.", rs->src);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-09 10:22:58 +04:00
|
|
|
static int match_explicit(struct ref *src, struct ref *dst,
|
|
|
|
struct ref ***dst_tail,
|
clean up error conventions of remote.c:match_explicit
match_explicit is called for each push refspec to try to
fully resolve the source and destination sides of the
refspec. Currently, we look at each refspec and report
errors on both the source and the dest side before aborting.
It makes sense to report errors for each refspec, since an
error in one is independent of an error in the other.
However, reporting errors on the 'dst' side of a refspec if
there has been an error on the 'src' side does not
necessarily make sense, since the interpretation of the
'dst' side depends on the 'src' side (for example, when
creating a new unqualified remote ref, we use the same type
as the src ref).
This patch lets match_explicit return early when the src
side of the refspec is bogus. We still look at all of the
refspecs before aborting the push, though.
At the same time, we clean up the call signature, which
previously took an extra "errs" flag. This was pointless, as
we didn't act on that flag, but rather just passed it back
to the caller. Instead, we now use the more traditional
"return -1" to signal an error, and the caller aggregates
the error count.
This change fixes two bugs, as well:
- the early return avoids a segfault when passing a NULL
matched_src to guess_ref()
- the check for multiple sources pointing to a single dest
aborted if the "err" flag was set. Presumably the intent
was not to bother with the check if we had no
matched_src. However, since the err flag was passed in
from the caller, we might abort the check just because a
previous refspec had a problem, which doesn't make
sense.
In practice, this didn't matter, since due to the error
flag we end up aborting the push anyway.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-06-16 20:15:02 +04:00
|
|
|
struct refspec *rs)
|
2007-05-12 19:45:59 +04:00
|
|
|
{
|
2007-06-09 10:22:58 +04:00
|
|
|
struct ref *matched_src, *matched_dst;
|
2014-03-05 23:03:21 +04:00
|
|
|
int allocated_src;
|
2007-05-25 09:20:56 +04:00
|
|
|
|
2007-06-09 10:22:58 +04:00
|
|
|
const char *dst_value = rs->dst;
|
push: allow unqualified dest refspecs to DWIM
Previously, a push like:
git push remote src:dst
would go through the following steps:
1. check for an unambiguous 'dst' on the remote; if it
exists, then push to that ref
2. otherwise, check if 'dst' begins with 'refs/'; if it
does, create a new ref
3. otherwise, complain because we don't know where in the
refs hierarchy to put 'dst'
However, in some cases, we can guess about the ref type of
'dst' based on the ref type of 'src'. Specifically, before
complaining we now check:
2.5. if 'src' resolves to a ref starting with refs/heads
or refs/tags, then prepend that to 'dst'
So now this creates a new branch on the remote, whereas it
previously failed with an error message:
git push master:newbranch
Note that, by design, we limit this DWIM behavior only to
source refs which resolve exactly (including symrefs which
resolve to existing refs). We still complain on a partial
destination refspec if the source is a raw sha1, or a ref
expression such as 'master~10'.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-04-23 13:16:06 +04:00
|
|
|
char *dst_guess;
|
2007-05-12 19:45:59 +04:00
|
|
|
|
2008-04-28 19:32:12 +04:00
|
|
|
if (rs->pattern || rs->matching)
|
clean up error conventions of remote.c:match_explicit
match_explicit is called for each push refspec to try to
fully resolve the source and destination sides of the
refspec. Currently, we look at each refspec and report
errors on both the source and the dest side before aborting.
It makes sense to report errors for each refspec, since an
error in one is independent of an error in the other.
However, reporting errors on the 'dst' side of a refspec if
there has been an error on the 'src' side does not
necessarily make sense, since the interpretation of the
'dst' side depends on the 'src' side (for example, when
creating a new unqualified remote ref, we use the same type
as the src ref).
This patch lets match_explicit return early when the src
side of the refspec is bogus. We still look at all of the
refspecs before aborting the push, though.
At the same time, we clean up the call signature, which
previously took an extra "errs" flag. This was pointless, as
we didn't act on that flag, but rather just passed it back
to the caller. Instead, we now use the more traditional
"return -1" to signal an error, and the caller aggregates
the error count.
This change fixes two bugs, as well:
- the early return avoids a segfault when passing a NULL
matched_src to guess_ref()
- the check for multiple sources pointing to a single dest
aborted if the "err" flag was set. Presumably the intent
was not to bother with the check if we had no
matched_src. However, since the err flag was passed in
from the caller, we might abort the check just because a
previous refspec had a problem, which doesn't make
sense.
In practice, this didn't matter, since due to the error
flag we end up aborting the push anyway.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-06-16 20:15:02 +04:00
|
|
|
return 0;
|
2007-05-25 09:20:56 +04:00
|
|
|
|
2007-06-09 10:22:58 +04:00
|
|
|
matched_src = matched_dst = NULL;
|
2014-03-05 23:03:21 +04:00
|
|
|
if (match_explicit_lhs(src, rs, &matched_src, &allocated_src) < 0)
|
|
|
|
return -1;
|
2007-06-09 11:14:04 +04:00
|
|
|
|
2007-09-25 08:13:25 +04:00
|
|
|
if (!dst_value) {
|
2008-02-20 20:54:05 +03:00
|
|
|
unsigned char sha1[20];
|
|
|
|
int flag;
|
|
|
|
|
2011-12-12 15:20:32 +04:00
|
|
|
dst_value = resolve_ref_unsafe(matched_src->name, sha1, 1, &flag);
|
2008-02-20 20:54:05 +03:00
|
|
|
if (!dst_value ||
|
|
|
|
((flag & REF_ISSYMREF) &&
|
2013-12-01 00:55:40 +04:00
|
|
|
!starts_with(dst_value, "refs/heads/")))
|
2008-02-20 20:54:05 +03:00
|
|
|
die("%s cannot be resolved to branch.",
|
|
|
|
matched_src->name);
|
2007-09-25 08:13:25 +04:00
|
|
|
}
|
2007-06-09 11:14:04 +04:00
|
|
|
|
2007-06-09 10:22:58 +04:00
|
|
|
switch (count_refspec_match(dst_value, dst, &matched_dst)) {
|
|
|
|
case 1:
|
|
|
|
break;
|
|
|
|
case 0:
|
2014-06-06 21:24:48 +04:00
|
|
|
if (starts_with(dst_value, "refs/"))
|
2007-07-10 08:47:26 +04:00
|
|
|
matched_dst = make_linked_ref(dst_value, dst_tail);
|
push: don't guess at qualifying remote refs on deletion
When we try to push a ref and the right-hand side of the
refspec does not find a match, we try to create it. If it is
not fully qualified, we try to guess where it would go in
the refs hierarchy based on the left-hand source side. If
the source side is not a ref, then we give up and give a
long explanatory message.
For deletions, however, this doesn't make any sense. We
would never want to create on the remote side, and if an
unqualified ref can't be matched, it is simply an error. The
current code handles this already because the left-hand side
is empty, and therefore does not give us a hint as to where
the right-hand side should go, and we properly error out.
Unfortunately, the error message is the long "we tried to
qualify this, but the source side didn't let us guess"
message, which is quite confusing.
Instead, we can just be more succinct and say "we can't
delete this because we couldn't find it". So before:
$ git push origin :bogus
error: unable to push to unqualified destination: bogus
The destination refspec neither matches an existing ref on the remote nor
begins with refs/, and we are unable to guess a prefix based on the source ref.
error: failed to push some refs to '$URL'
and now:
$ git push origin :bogus
error: unable to delete 'bogus': remote ref does not exist
error: failed to push some refs to '$URL'
It is tempting to also catch a fully-qualified ref like
"refs/heads/bogus" and generate the same error message.
However, that currently does not error out at all, and
instead gets sent to the remote side, which typically
generates a warning:
$ git push origin:refs/heads/bogus
remote: warning: Deleting a non-existent ref.
To $URL
- [deleted] bogus
While it would be nice to catch this error early, a
client-side error would mean aborting the push entirely and
changing push's exit code. For example, right now you can
do:
$ git push origin refs/heads/foo refs/heads/bar
and end up in a state where "foo" and "bar" are deleted,
whether both of them currently exist or not (and see an
error only if we actually failed to contact the server).
Generating an error would cause a regression for this use
case.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-07-03 22:04:39 +04:00
|
|
|
else if (is_null_sha1(matched_src->new_sha1))
|
|
|
|
error("unable to delete '%s': remote ref does not exist",
|
|
|
|
dst_value);
|
2009-09-01 09:35:10 +04:00
|
|
|
else if ((dst_guess = guess_ref(dst_value, matched_src)))
|
push: allow unqualified dest refspecs to DWIM
Previously, a push like:
git push remote src:dst
would go through the following steps:
1. check for an unambiguous 'dst' on the remote; if it
exists, then push to that ref
2. otherwise, check if 'dst' begins with 'refs/'; if it
does, create a new ref
3. otherwise, complain because we don't know where in the
refs hierarchy to put 'dst'
However, in some cases, we can guess about the ref type of
'dst' based on the ref type of 'src'. Specifically, before
complaining we now check:
2.5. if 'src' resolves to a ref starting with refs/heads
or refs/tags, then prepend that to 'dst'
So now this creates a new branch on the remote, whereas it
previously failed with an error message:
git push master:newbranch
Note that, by design, we limit this DWIM behavior only to
source refs which resolve exactly (including symrefs which
resolve to existing refs). We still complain on a partial
destination refspec if the source is a raw sha1, or a ref
expression such as 'master~10'.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-04-23 13:16:06 +04:00
|
|
|
matched_dst = make_linked_ref(dst_guess, dst_tail);
|
2007-06-09 11:14:04 +04:00
|
|
|
else
|
push: allow unqualified dest refspecs to DWIM
Previously, a push like:
git push remote src:dst
would go through the following steps:
1. check for an unambiguous 'dst' on the remote; if it
exists, then push to that ref
2. otherwise, check if 'dst' begins with 'refs/'; if it
does, create a new ref
3. otherwise, complain because we don't know where in the
refs hierarchy to put 'dst'
However, in some cases, we can guess about the ref type of
'dst' based on the ref type of 'src'. Specifically, before
complaining we now check:
2.5. if 'src' resolves to a ref starting with refs/heads
or refs/tags, then prepend that to 'dst'
So now this creates a new branch on the remote, whereas it
previously failed with an error message:
git push master:newbranch
Note that, by design, we limit this DWIM behavior only to
source refs which resolve exactly (including symrefs which
resolve to existing refs). We still complain on a partial
destination refspec if the source is a raw sha1, or a ref
expression such as 'master~10'.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-04-23 13:16:06 +04:00
|
|
|
error("unable to push to unqualified destination: %s\n"
|
|
|
|
"The destination refspec neither matches an "
|
|
|
|
"existing ref on the remote nor\n"
|
|
|
|
"begins with refs/, and we are unable to "
|
|
|
|
"guess a prefix based on the source ref.",
|
|
|
|
dst_value);
|
2007-06-09 10:22:58 +04:00
|
|
|
break;
|
|
|
|
default:
|
2007-06-09 11:14:04 +04:00
|
|
|
matched_dst = NULL;
|
2007-06-09 10:22:58 +04:00
|
|
|
error("dst refspec %s matches more than one.",
|
|
|
|
dst_value);
|
|
|
|
break;
|
|
|
|
}
|
clean up error conventions of remote.c:match_explicit
match_explicit is called for each push refspec to try to
fully resolve the source and destination sides of the
refspec. Currently, we look at each refspec and report
errors on both the source and the dest side before aborting.
It makes sense to report errors for each refspec, since an
error in one is independent of an error in the other.
However, reporting errors on the 'dst' side of a refspec if
there has been an error on the 'src' side does not
necessarily make sense, since the interpretation of the
'dst' side depends on the 'src' side (for example, when
creating a new unqualified remote ref, we use the same type
as the src ref).
This patch lets match_explicit return early when the src
side of the refspec is bogus. We still look at all of the
refspecs before aborting the push, though.
At the same time, we clean up the call signature, which
previously took an extra "errs" flag. This was pointless, as
we didn't act on that flag, but rather just passed it back
to the caller. Instead, we now use the more traditional
"return -1" to signal an error, and the caller aggregates
the error count.
This change fixes two bugs, as well:
- the early return avoids a segfault when passing a NULL
matched_src to guess_ref()
- the check for multiple sources pointing to a single dest
aborted if the "err" flag was set. Presumably the intent
was not to bother with the check if we had no
matched_src. However, since the err flag was passed in
from the caller, we might abort the check just because a
previous refspec had a problem, which doesn't make
sense.
In practice, this didn't matter, since due to the error
flag we end up aborting the push anyway.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-06-16 20:15:02 +04:00
|
|
|
if (!matched_dst)
|
|
|
|
return -1;
|
|
|
|
if (matched_dst->peer_ref)
|
|
|
|
return error("dst ref %s receives from more than one src.",
|
2007-06-09 10:22:58 +04:00
|
|
|
matched_dst->name);
|
|
|
|
else {
|
2014-03-05 23:03:21 +04:00
|
|
|
matched_dst->peer_ref = allocated_src ?
|
|
|
|
matched_src :
|
|
|
|
copy_ref(matched_src);
|
2007-06-09 10:22:58 +04:00
|
|
|
matched_dst->force = rs->force;
|
2007-05-12 19:45:59 +04:00
|
|
|
}
|
clean up error conventions of remote.c:match_explicit
match_explicit is called for each push refspec to try to
fully resolve the source and destination sides of the
refspec. Currently, we look at each refspec and report
errors on both the source and the dest side before aborting.
It makes sense to report errors for each refspec, since an
error in one is independent of an error in the other.
However, reporting errors on the 'dst' side of a refspec if
there has been an error on the 'src' side does not
necessarily make sense, since the interpretation of the
'dst' side depends on the 'src' side (for example, when
creating a new unqualified remote ref, we use the same type
as the src ref).
This patch lets match_explicit return early when the src
side of the refspec is bogus. We still look at all of the
refspecs before aborting the push, though.
At the same time, we clean up the call signature, which
previously took an extra "errs" flag. This was pointless, as
we didn't act on that flag, but rather just passed it back
to the caller. Instead, we now use the more traditional
"return -1" to signal an error, and the caller aggregates
the error count.
This change fixes two bugs, as well:
- the early return avoids a segfault when passing a NULL
matched_src to guess_ref()
- the check for multiple sources pointing to a single dest
aborted if the "err" flag was set. Presumably the intent
was not to bother with the check if we had no
matched_src. However, since the err flag was passed in
from the caller, we might abort the check just because a
previous refspec had a problem, which doesn't make
sense.
In practice, this didn't matter, since due to the error
flag we end up aborting the push anyway.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-06-16 20:15:02 +04:00
|
|
|
return 0;
|
2007-06-09 10:22:58 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int match_explicit_refs(struct ref *src, struct ref *dst,
|
|
|
|
struct ref ***dst_tail, struct refspec *rs,
|
|
|
|
int rs_nr)
|
|
|
|
{
|
|
|
|
int i, errs;
|
|
|
|
for (i = errs = 0; i < rs_nr; i++)
|
clean up error conventions of remote.c:match_explicit
match_explicit is called for each push refspec to try to
fully resolve the source and destination sides of the
refspec. Currently, we look at each refspec and report
errors on both the source and the dest side before aborting.
It makes sense to report errors for each refspec, since an
error in one is independent of an error in the other.
However, reporting errors on the 'dst' side of a refspec if
there has been an error on the 'src' side does not
necessarily make sense, since the interpretation of the
'dst' side depends on the 'src' side (for example, when
creating a new unqualified remote ref, we use the same type
as the src ref).
This patch lets match_explicit return early when the src
side of the refspec is bogus. We still look at all of the
refspecs before aborting the push, though.
At the same time, we clean up the call signature, which
previously took an extra "errs" flag. This was pointless, as
we didn't act on that flag, but rather just passed it back
to the caller. Instead, we now use the more traditional
"return -1" to signal an error, and the caller aggregates
the error count.
This change fixes two bugs, as well:
- the early return avoids a segfault when passing a NULL
matched_src to guess_ref()
- the check for multiple sources pointing to a single dest
aborted if the "err" flag was set. Presumably the intent
was not to bother with the check if we had no
matched_src. However, since the err flag was passed in
from the caller, we might abort the check just because a
previous refspec had a problem, which doesn't make
sense.
In practice, this didn't matter, since due to the error
flag we end up aborting the push anyway.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-06-16 20:15:02 +04:00
|
|
|
errs += match_explicit(src, dst, dst_tail, &rs[i]);
|
|
|
|
return errs;
|
2007-05-12 19:45:59 +04:00
|
|
|
}
|
|
|
|
|
2012-02-23 02:43:39 +04:00
|
|
|
static char *get_ref_match(const struct refspec *rs, int rs_nr, const struct ref *ref,
|
2012-02-23 02:43:41 +04:00
|
|
|
int send_mirror, int direction, const struct refspec **ret_pat)
|
2007-05-25 09:20:56 +04:00
|
|
|
{
|
2012-02-23 02:43:39 +04:00
|
|
|
const struct refspec *pat;
|
|
|
|
char *name;
|
2007-05-25 09:20:56 +04:00
|
|
|
int i;
|
2008-04-28 19:32:12 +04:00
|
|
|
int matching_refs = -1;
|
2007-05-25 09:20:56 +04:00
|
|
|
for (i = 0; i < rs_nr; i++) {
|
2008-04-28 19:32:12 +04:00
|
|
|
if (rs[i].matching &&
|
|
|
|
(matching_refs == -1 || rs[i].force)) {
|
|
|
|
matching_refs = i;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-02-23 02:43:39 +04:00
|
|
|
if (rs[i].pattern) {
|
|
|
|
const char *dst_side = rs[i].dst ? rs[i].dst : rs[i].src;
|
2012-02-23 02:43:41 +04:00
|
|
|
int match;
|
|
|
|
if (direction == FROM_SRC)
|
|
|
|
match = match_name_with_pattern(rs[i].src, ref->name, dst_side, &name);
|
|
|
|
else
|
|
|
|
match = match_name_with_pattern(dst_side, ref->name, rs[i].src, &name);
|
|
|
|
if (match) {
|
2012-02-23 02:43:39 +04:00
|
|
|
matching_refs = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2007-05-25 09:20:56 +04:00
|
|
|
}
|
2012-02-23 02:43:39 +04:00
|
|
|
if (matching_refs == -1)
|
2008-04-28 19:32:12 +04:00
|
|
|
return NULL;
|
2012-02-23 02:43:39 +04:00
|
|
|
|
|
|
|
pat = rs + matching_refs;
|
|
|
|
if (pat->matching) {
|
|
|
|
/*
|
|
|
|
* "matching refs"; traditionally we pushed everything
|
|
|
|
* including refs outside refs/heads/ hierarchy, but
|
|
|
|
* that does not make much sense these days.
|
|
|
|
*/
|
2013-12-01 00:55:40 +04:00
|
|
|
if (!send_mirror && !starts_with(ref->name, "refs/heads/"))
|
2012-02-23 02:43:39 +04:00
|
|
|
return NULL;
|
|
|
|
name = xstrdup(ref->name);
|
|
|
|
}
|
|
|
|
if (ret_pat)
|
|
|
|
*ret_pat = pat;
|
|
|
|
return name;
|
2007-05-25 09:20:56 +04:00
|
|
|
}
|
|
|
|
|
2009-05-31 18:26:48 +04:00
|
|
|
static struct ref **tail_ref(struct ref **head)
|
|
|
|
{
|
|
|
|
struct ref **tail = head;
|
|
|
|
while (*tail)
|
|
|
|
tail = &((*tail)->next);
|
|
|
|
return tail;
|
|
|
|
}
|
|
|
|
|
2013-03-05 00:09:50 +04:00
|
|
|
struct tips {
|
|
|
|
struct commit **tip;
|
|
|
|
int nr, alloc;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void add_to_tips(struct tips *tips, const unsigned char *sha1)
|
|
|
|
{
|
|
|
|
struct commit *commit;
|
|
|
|
|
|
|
|
if (is_null_sha1(sha1))
|
|
|
|
return;
|
|
|
|
commit = lookup_commit_reference_gently(sha1, 1);
|
|
|
|
if (!commit || (commit->object.flags & TMP_MARK))
|
|
|
|
return;
|
|
|
|
commit->object.flags |= TMP_MARK;
|
|
|
|
ALLOC_GROW(tips->tip, tips->nr + 1, tips->alloc);
|
|
|
|
tips->tip[tips->nr++] = commit;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***dst_tail)
|
|
|
|
{
|
|
|
|
struct string_list dst_tag = STRING_LIST_INIT_NODUP;
|
|
|
|
struct string_list src_tag = STRING_LIST_INIT_NODUP;
|
|
|
|
struct string_list_item *item;
|
|
|
|
struct ref *ref;
|
|
|
|
struct tips sent_tips;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Collect everything we know they would have at the end of
|
|
|
|
* this push, and collect all tags they have.
|
|
|
|
*/
|
|
|
|
memset(&sent_tips, 0, sizeof(sent_tips));
|
|
|
|
for (ref = *dst; ref; ref = ref->next) {
|
|
|
|
if (ref->peer_ref &&
|
|
|
|
!is_null_sha1(ref->peer_ref->new_sha1))
|
|
|
|
add_to_tips(&sent_tips, ref->peer_ref->new_sha1);
|
|
|
|
else
|
|
|
|
add_to_tips(&sent_tips, ref->old_sha1);
|
2013-12-01 00:55:40 +04:00
|
|
|
if (starts_with(ref->name, "refs/tags/"))
|
2013-03-05 00:09:50 +04:00
|
|
|
string_list_append(&dst_tag, ref->name);
|
|
|
|
}
|
|
|
|
clear_commit_marks_many(sent_tips.nr, sent_tips.tip, TMP_MARK);
|
|
|
|
|
|
|
|
sort_string_list(&dst_tag);
|
|
|
|
|
|
|
|
/* Collect tags they do not have. */
|
|
|
|
for (ref = src; ref; ref = ref->next) {
|
2013-12-01 00:55:40 +04:00
|
|
|
if (!starts_with(ref->name, "refs/tags/"))
|
2013-03-05 00:09:50 +04:00
|
|
|
continue; /* not a tag */
|
|
|
|
if (string_list_has_string(&dst_tag, ref->name))
|
|
|
|
continue; /* they already have it */
|
|
|
|
if (sha1_object_info(ref->new_sha1, NULL) != OBJ_TAG)
|
|
|
|
continue; /* be conservative */
|
|
|
|
item = string_list_append(&src_tag, ref->name);
|
|
|
|
item->util = ref;
|
|
|
|
}
|
|
|
|
string_list_clear(&dst_tag, 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* At this point, src_tag lists tags that are missing from
|
|
|
|
* dst, and sent_tips lists the tips we are pushing or those
|
|
|
|
* that we know they already have. An element in the src_tag
|
|
|
|
* that is an ancestor of any of the sent_tips needs to be
|
|
|
|
* sent to the other side.
|
|
|
|
*/
|
|
|
|
if (sent_tips.nr) {
|
|
|
|
for_each_string_list_item(item, &src_tag) {
|
|
|
|
struct ref *ref = item->util;
|
|
|
|
struct ref *dst_ref;
|
|
|
|
struct commit *commit;
|
|
|
|
|
|
|
|
if (is_null_sha1(ref->new_sha1))
|
|
|
|
continue;
|
|
|
|
commit = lookup_commit_reference_gently(ref->new_sha1, 1);
|
|
|
|
if (!commit)
|
|
|
|
/* not pushing a commit, which is not an error */
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Is this tag, which they do not have, reachable from
|
|
|
|
* any of the commits we are sending?
|
|
|
|
*/
|
|
|
|
if (!in_merge_bases_many(commit, sent_tips.nr, sent_tips.tip))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Add it in */
|
|
|
|
dst_ref = make_linked_ref(ref->name, dst_tail);
|
|
|
|
hashcpy(dst_ref->new_sha1, ref->new_sha1);
|
|
|
|
dst_ref->peer_ref = copy_ref(ref);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
string_list_clear(&src_tag, 0);
|
|
|
|
free(sent_tips.tip);
|
|
|
|
}
|
|
|
|
|
2013-07-09 00:56:53 +04:00
|
|
|
struct ref *find_ref_by_name(const struct ref *list, const char *name)
|
|
|
|
{
|
|
|
|
for ( ; list; list = list->next)
|
|
|
|
if (!strcmp(list->name, name))
|
|
|
|
return (struct ref *)list;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
remote.c: avoid O(m*n) behavior in match_push_refs
When pushing using a matching refspec or a pattern refspec, each ref
in the local repository must be paired with a ref advertised by the
remote server. This is accomplished by using the refspec to transform
the name of the local ref into the name it should have in the remote
repository, and then performing a linear search through the list of
remote refs to see if the remote ref was advertised by the remote
system.
Each of these lookups has O(n) complexity and makes match_push_refs()
be an O(m*n) operation, where m is the number of local refs and n is
the number of remote refs. If there are many refs 100,000+, then this
ref matching can take a significant amount of time. Let's prepare an
index of the remote refs to allow searching in O(log n) time and
reduce the complexity of match_push_refs() to O(m log n).
We prepare the index lazily so that it is only created when necessary.
So, there should be no impact when _not_ using a matching or pattern
refspec, i.e. when pushing using only explicit refspecs.
Dry-run push of a repository with 121,913 local and remote refs:
before after
real 1m40.582s 0m0.804s
user 1m39.914s 0m0.515s
sys 0m0.125s 0m0.106s
The creation of the index has overhead. So, if there are very few
local refs, then it could take longer to create the index than it
would have taken to just perform n linear lookups into the remote
ref space. Using the index should provide some improvement when
the number of local refs is roughly greater than the log of the
number of remote refs (i.e. m >= log n). The pathological case is
when there is a single local ref and very many remote refs.
Dry-run push of a repository with 121,913 remote refs and a single
local ref:
before after
real 0m0.525s 0m0.566s
user 0m0.243s 0m0.279s
sys 0m0.075s 0m0.099s
Using an index takes 41 ms longer, or roughly 7.8% longer.
Jeff King measured a no-op push of a single ref into a remote repo
with 370,000 refs:
before after
real 0m1.087s 0m1.156s
user 0m1.344s 0m1.412s
sys 0m0.288s 0m0.284s
Using an index takes 69 ms longer, or roughly 6.3% longer.
None of the measurements above required transferring any objects to
the remote repository. If the push required transferring objects and
updating the refs in the remote repository, the impact of preparing
the search index would be even smaller.
A similar operation is performed in the reverse direction when pruning
using a matching or pattern refspec. Let's avoid O(m*n) behavior in
the same way by lazily preparing an index on the local refs.
Signed-off-by: Brandon Casey <drafnel@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-08 12:58:39 +04:00
|
|
|
static void prepare_ref_index(struct string_list *ref_index, struct ref *ref)
|
|
|
|
{
|
|
|
|
for ( ; ref; ref = ref->next)
|
|
|
|
string_list_append_nodup(ref_index, ref->name)->util = ref;
|
|
|
|
|
|
|
|
sort_string_list(ref_index);
|
|
|
|
}
|
|
|
|
|
2014-03-05 23:04:54 +04:00
|
|
|
/*
|
|
|
|
* Given only the set of local refs, sanity-check the set of push
|
|
|
|
* refspecs. We can't catch all errors that match_push_refs would,
|
|
|
|
* but we can catch some errors early before even talking to the
|
|
|
|
* remote side.
|
|
|
|
*/
|
|
|
|
int check_push_refs(struct ref *src, int nr_refspec, const char **refspec_names)
|
|
|
|
{
|
|
|
|
struct refspec *refspec = parse_push_refspec(nr_refspec, refspec_names);
|
|
|
|
int ret = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < nr_refspec; i++) {
|
|
|
|
struct refspec *rs = refspec + i;
|
|
|
|
|
|
|
|
if (rs->pattern || rs->matching)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ret |= match_explicit_lhs(src, rs, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
free_refspec(nr_refspec, refspec);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-06-09 10:22:58 +04:00
|
|
|
/*
|
2011-09-09 22:54:58 +04:00
|
|
|
* Given the set of refs the local repository has, the set of refs the
|
|
|
|
* remote repository has, and the refspec used for push, determine
|
|
|
|
* what remote refs we will update and with what value by setting
|
|
|
|
* peer_ref (which object is being pushed) and force (if the push is
|
|
|
|
* forced) in elements of "dst". The function may add new elements to
|
|
|
|
* dst (e.g. pushing to a new branch, done in match_explicit_refs).
|
2007-06-09 10:22:58 +04:00
|
|
|
*/
|
2011-09-09 22:54:58 +04:00
|
|
|
int match_push_refs(struct ref *src, struct ref **dst,
|
|
|
|
int nr_refspec, const char **refspec, int flags)
|
2007-05-12 19:45:59 +04:00
|
|
|
{
|
2008-04-28 19:32:12 +04:00
|
|
|
struct refspec *rs;
|
2007-11-10 02:32:10 +03:00
|
|
|
int send_all = flags & MATCH_REFS_ALL;
|
|
|
|
int send_mirror = flags & MATCH_REFS_MIRROR;
|
2012-02-23 02:43:41 +04:00
|
|
|
int send_prune = flags & MATCH_REFS_PRUNE;
|
2009-02-25 11:32:17 +03:00
|
|
|
int errs;
|
2009-06-18 21:28:43 +04:00
|
|
|
static const char *default_refspec[] = { ":", NULL };
|
2012-02-23 02:43:38 +04:00
|
|
|
struct ref *ref, **dst_tail = tail_ref(dst);
|
remote.c: avoid O(m*n) behavior in match_push_refs
When pushing using a matching refspec or a pattern refspec, each ref
in the local repository must be paired with a ref advertised by the
remote server. This is accomplished by using the refspec to transform
the name of the local ref into the name it should have in the remote
repository, and then performing a linear search through the list of
remote refs to see if the remote ref was advertised by the remote
system.
Each of these lookups has O(n) complexity and makes match_push_refs()
be an O(m*n) operation, where m is the number of local refs and n is
the number of remote refs. If there are many refs 100,000+, then this
ref matching can take a significant amount of time. Let's prepare an
index of the remote refs to allow searching in O(log n) time and
reduce the complexity of match_push_refs() to O(m log n).
We prepare the index lazily so that it is only created when necessary.
So, there should be no impact when _not_ using a matching or pattern
refspec, i.e. when pushing using only explicit refspecs.
Dry-run push of a repository with 121,913 local and remote refs:
before after
real 1m40.582s 0m0.804s
user 1m39.914s 0m0.515s
sys 0m0.125s 0m0.106s
The creation of the index has overhead. So, if there are very few
local refs, then it could take longer to create the index than it
would have taken to just perform n linear lookups into the remote
ref space. Using the index should provide some improvement when
the number of local refs is roughly greater than the log of the
number of remote refs (i.e. m >= log n). The pathological case is
when there is a single local ref and very many remote refs.
Dry-run push of a repository with 121,913 remote refs and a single
local ref:
before after
real 0m0.525s 0m0.566s
user 0m0.243s 0m0.279s
sys 0m0.075s 0m0.099s
Using an index takes 41 ms longer, or roughly 7.8% longer.
Jeff King measured a no-op push of a single ref into a remote repo
with 370,000 refs:
before after
real 0m1.087s 0m1.156s
user 0m1.344s 0m1.412s
sys 0m0.288s 0m0.284s
Using an index takes 69 ms longer, or roughly 6.3% longer.
None of the measurements above required transferring any objects to
the remote repository. If the push required transferring objects and
updating the refs in the remote repository, the impact of preparing
the search index would be even smaller.
A similar operation is performed in the reverse direction when pruning
using a matching or pattern refspec. Let's avoid O(m*n) behavior in
the same way by lazily preparing an index on the local refs.
Signed-off-by: Brandon Casey <drafnel@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-08 12:58:39 +04:00
|
|
|
struct string_list dst_ref_index = STRING_LIST_INIT_NODUP;
|
2007-05-12 19:45:59 +04:00
|
|
|
|
2008-04-28 19:32:12 +04:00
|
|
|
if (!nr_refspec) {
|
|
|
|
nr_refspec = 1;
|
|
|
|
refspec = default_refspec;
|
|
|
|
}
|
|
|
|
rs = parse_push_refspec(nr_refspec, (const char **) refspec);
|
2009-05-31 18:26:48 +04:00
|
|
|
errs = match_explicit_refs(src, *dst, &dst_tail, rs, nr_refspec);
|
2007-05-12 19:45:59 +04:00
|
|
|
|
|
|
|
/* pick the remainder */
|
2012-02-23 02:43:38 +04:00
|
|
|
for (ref = src; ref; ref = ref->next) {
|
remote.c: avoid O(m*n) behavior in match_push_refs
When pushing using a matching refspec or a pattern refspec, each ref
in the local repository must be paired with a ref advertised by the
remote server. This is accomplished by using the refspec to transform
the name of the local ref into the name it should have in the remote
repository, and then performing a linear search through the list of
remote refs to see if the remote ref was advertised by the remote
system.
Each of these lookups has O(n) complexity and makes match_push_refs()
be an O(m*n) operation, where m is the number of local refs and n is
the number of remote refs. If there are many refs 100,000+, then this
ref matching can take a significant amount of time. Let's prepare an
index of the remote refs to allow searching in O(log n) time and
reduce the complexity of match_push_refs() to O(m log n).
We prepare the index lazily so that it is only created when necessary.
So, there should be no impact when _not_ using a matching or pattern
refspec, i.e. when pushing using only explicit refspecs.
Dry-run push of a repository with 121,913 local and remote refs:
before after
real 1m40.582s 0m0.804s
user 1m39.914s 0m0.515s
sys 0m0.125s 0m0.106s
The creation of the index has overhead. So, if there are very few
local refs, then it could take longer to create the index than it
would have taken to just perform n linear lookups into the remote
ref space. Using the index should provide some improvement when
the number of local refs is roughly greater than the log of the
number of remote refs (i.e. m >= log n). The pathological case is
when there is a single local ref and very many remote refs.
Dry-run push of a repository with 121,913 remote refs and a single
local ref:
before after
real 0m0.525s 0m0.566s
user 0m0.243s 0m0.279s
sys 0m0.075s 0m0.099s
Using an index takes 41 ms longer, or roughly 7.8% longer.
Jeff King measured a no-op push of a single ref into a remote repo
with 370,000 refs:
before after
real 0m1.087s 0m1.156s
user 0m1.344s 0m1.412s
sys 0m0.288s 0m0.284s
Using an index takes 69 ms longer, or roughly 6.3% longer.
None of the measurements above required transferring any objects to
the remote repository. If the push required transferring objects and
updating the refs in the remote repository, the impact of preparing
the search index would be even smaller.
A similar operation is performed in the reverse direction when pruning
using a matching or pattern refspec. Let's avoid O(m*n) behavior in
the same way by lazily preparing an index on the local refs.
Signed-off-by: Brandon Casey <drafnel@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-08 12:58:39 +04:00
|
|
|
struct string_list_item *dst_item;
|
2007-05-12 19:45:59 +04:00
|
|
|
struct ref *dst_peer;
|
2007-06-08 03:43:05 +04:00
|
|
|
const struct refspec *pat = NULL;
|
|
|
|
char *dst_name;
|
2012-02-23 02:43:39 +04:00
|
|
|
|
2012-02-23 02:43:41 +04:00
|
|
|
dst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_SRC, &pat);
|
2012-02-23 02:43:39 +04:00
|
|
|
if (!dst_name)
|
2008-04-28 19:32:12 +04:00
|
|
|
continue;
|
|
|
|
|
remote.c: avoid O(m*n) behavior in match_push_refs
When pushing using a matching refspec or a pattern refspec, each ref
in the local repository must be paired with a ref advertised by the
remote server. This is accomplished by using the refspec to transform
the name of the local ref into the name it should have in the remote
repository, and then performing a linear search through the list of
remote refs to see if the remote ref was advertised by the remote
system.
Each of these lookups has O(n) complexity and makes match_push_refs()
be an O(m*n) operation, where m is the number of local refs and n is
the number of remote refs. If there are many refs 100,000+, then this
ref matching can take a significant amount of time. Let's prepare an
index of the remote refs to allow searching in O(log n) time and
reduce the complexity of match_push_refs() to O(m log n).
We prepare the index lazily so that it is only created when necessary.
So, there should be no impact when _not_ using a matching or pattern
refspec, i.e. when pushing using only explicit refspecs.
Dry-run push of a repository with 121,913 local and remote refs:
before after
real 1m40.582s 0m0.804s
user 1m39.914s 0m0.515s
sys 0m0.125s 0m0.106s
The creation of the index has overhead. So, if there are very few
local refs, then it could take longer to create the index than it
would have taken to just perform n linear lookups into the remote
ref space. Using the index should provide some improvement when
the number of local refs is roughly greater than the log of the
number of remote refs (i.e. m >= log n). The pathological case is
when there is a single local ref and very many remote refs.
Dry-run push of a repository with 121,913 remote refs and a single
local ref:
before after
real 0m0.525s 0m0.566s
user 0m0.243s 0m0.279s
sys 0m0.075s 0m0.099s
Using an index takes 41 ms longer, or roughly 7.8% longer.
Jeff King measured a no-op push of a single ref into a remote repo
with 370,000 refs:
before after
real 0m1.087s 0m1.156s
user 0m1.344s 0m1.412s
sys 0m0.288s 0m0.284s
Using an index takes 69 ms longer, or roughly 6.3% longer.
None of the measurements above required transferring any objects to
the remote repository. If the push required transferring objects and
updating the refs in the remote repository, the impact of preparing
the search index would be even smaller.
A similar operation is performed in the reverse direction when pruning
using a matching or pattern refspec. Let's avoid O(m*n) behavior in
the same way by lazily preparing an index on the local refs.
Signed-off-by: Brandon Casey <drafnel@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-08 12:58:39 +04:00
|
|
|
if (!dst_ref_index.nr)
|
|
|
|
prepare_ref_index(&dst_ref_index, *dst);
|
|
|
|
|
|
|
|
dst_item = string_list_lookup(&dst_ref_index, dst_name);
|
|
|
|
dst_peer = dst_item ? dst_item->util : NULL;
|
2008-04-28 19:32:12 +04:00
|
|
|
if (dst_peer) {
|
|
|
|
if (dst_peer->peer_ref)
|
|
|
|
/* We're already sending something to this ref. */
|
|
|
|
goto free_name;
|
|
|
|
} else {
|
|
|
|
if (pat->matching && !(send_all || send_mirror))
|
|
|
|
/*
|
|
|
|
* Remote doesn't have it, and we have no
|
|
|
|
* explicit pattern, and we don't have
|
2014-04-01 02:11:46 +04:00
|
|
|
* --all or --mirror.
|
2008-04-28 19:32:12 +04:00
|
|
|
*/
|
|
|
|
goto free_name;
|
2007-11-10 02:32:10 +03:00
|
|
|
|
2007-05-12 19:45:59 +04:00
|
|
|
/* Create a new one and link it */
|
2009-05-31 18:26:48 +04:00
|
|
|
dst_peer = make_linked_ref(dst_name, &dst_tail);
|
2012-02-23 02:43:38 +04:00
|
|
|
hashcpy(dst_peer->new_sha1, ref->new_sha1);
|
remote.c: avoid O(m*n) behavior in match_push_refs
When pushing using a matching refspec or a pattern refspec, each ref
in the local repository must be paired with a ref advertised by the
remote server. This is accomplished by using the refspec to transform
the name of the local ref into the name it should have in the remote
repository, and then performing a linear search through the list of
remote refs to see if the remote ref was advertised by the remote
system.
Each of these lookups has O(n) complexity and makes match_push_refs()
be an O(m*n) operation, where m is the number of local refs and n is
the number of remote refs. If there are many refs 100,000+, then this
ref matching can take a significant amount of time. Let's prepare an
index of the remote refs to allow searching in O(log n) time and
reduce the complexity of match_push_refs() to O(m log n).
We prepare the index lazily so that it is only created when necessary.
So, there should be no impact when _not_ using a matching or pattern
refspec, i.e. when pushing using only explicit refspecs.
Dry-run push of a repository with 121,913 local and remote refs:
before after
real 1m40.582s 0m0.804s
user 1m39.914s 0m0.515s
sys 0m0.125s 0m0.106s
The creation of the index has overhead. So, if there are very few
local refs, then it could take longer to create the index than it
would have taken to just perform n linear lookups into the remote
ref space. Using the index should provide some improvement when
the number of local refs is roughly greater than the log of the
number of remote refs (i.e. m >= log n). The pathological case is
when there is a single local ref and very many remote refs.
Dry-run push of a repository with 121,913 remote refs and a single
local ref:
before after
real 0m0.525s 0m0.566s
user 0m0.243s 0m0.279s
sys 0m0.075s 0m0.099s
Using an index takes 41 ms longer, or roughly 7.8% longer.
Jeff King measured a no-op push of a single ref into a remote repo
with 370,000 refs:
before after
real 0m1.087s 0m1.156s
user 0m1.344s 0m1.412s
sys 0m0.288s 0m0.284s
Using an index takes 69 ms longer, or roughly 6.3% longer.
None of the measurements above required transferring any objects to
the remote repository. If the push required transferring objects and
updating the refs in the remote repository, the impact of preparing
the search index would be even smaller.
A similar operation is performed in the reverse direction when pruning
using a matching or pattern refspec. Let's avoid O(m*n) behavior in
the same way by lazily preparing an index on the local refs.
Signed-off-by: Brandon Casey <drafnel@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-08 12:58:39 +04:00
|
|
|
string_list_insert(&dst_ref_index,
|
|
|
|
dst_peer->name)->util = dst_peer;
|
2007-05-12 19:45:59 +04:00
|
|
|
}
|
2012-02-23 02:43:38 +04:00
|
|
|
dst_peer->peer_ref = copy_ref(ref);
|
2008-04-28 19:32:12 +04:00
|
|
|
dst_peer->force = pat->force;
|
2007-06-08 03:43:05 +04:00
|
|
|
free_name:
|
|
|
|
free(dst_name);
|
2007-05-12 19:45:59 +04:00
|
|
|
}
|
2013-03-05 00:09:50 +04:00
|
|
|
|
remote.c: avoid O(m*n) behavior in match_push_refs
When pushing using a matching refspec or a pattern refspec, each ref
in the local repository must be paired with a ref advertised by the
remote server. This is accomplished by using the refspec to transform
the name of the local ref into the name it should have in the remote
repository, and then performing a linear search through the list of
remote refs to see if the remote ref was advertised by the remote
system.
Each of these lookups has O(n) complexity and makes match_push_refs()
be an O(m*n) operation, where m is the number of local refs and n is
the number of remote refs. If there are many refs 100,000+, then this
ref matching can take a significant amount of time. Let's prepare an
index of the remote refs to allow searching in O(log n) time and
reduce the complexity of match_push_refs() to O(m log n).
We prepare the index lazily so that it is only created when necessary.
So, there should be no impact when _not_ using a matching or pattern
refspec, i.e. when pushing using only explicit refspecs.
Dry-run push of a repository with 121,913 local and remote refs:
before after
real 1m40.582s 0m0.804s
user 1m39.914s 0m0.515s
sys 0m0.125s 0m0.106s
The creation of the index has overhead. So, if there are very few
local refs, then it could take longer to create the index than it
would have taken to just perform n linear lookups into the remote
ref space. Using the index should provide some improvement when
the number of local refs is roughly greater than the log of the
number of remote refs (i.e. m >= log n). The pathological case is
when there is a single local ref and very many remote refs.
Dry-run push of a repository with 121,913 remote refs and a single
local ref:
before after
real 0m0.525s 0m0.566s
user 0m0.243s 0m0.279s
sys 0m0.075s 0m0.099s
Using an index takes 41 ms longer, or roughly 7.8% longer.
Jeff King measured a no-op push of a single ref into a remote repo
with 370,000 refs:
before after
real 0m1.087s 0m1.156s
user 0m1.344s 0m1.412s
sys 0m0.288s 0m0.284s
Using an index takes 69 ms longer, or roughly 6.3% longer.
None of the measurements above required transferring any objects to
the remote repository. If the push required transferring objects and
updating the refs in the remote repository, the impact of preparing
the search index would be even smaller.
A similar operation is performed in the reverse direction when pruning
using a matching or pattern refspec. Let's avoid O(m*n) behavior in
the same way by lazily preparing an index on the local refs.
Signed-off-by: Brandon Casey <drafnel@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-08 12:58:39 +04:00
|
|
|
string_list_clear(&dst_ref_index, 0);
|
|
|
|
|
2013-03-05 00:09:50 +04:00
|
|
|
if (flags & MATCH_REFS_FOLLOW_TAGS)
|
|
|
|
add_missing_tags(src, dst, &dst_tail);
|
|
|
|
|
2012-02-23 02:43:41 +04:00
|
|
|
if (send_prune) {
|
remote.c: avoid O(m*n) behavior in match_push_refs
When pushing using a matching refspec or a pattern refspec, each ref
in the local repository must be paired with a ref advertised by the
remote server. This is accomplished by using the refspec to transform
the name of the local ref into the name it should have in the remote
repository, and then performing a linear search through the list of
remote refs to see if the remote ref was advertised by the remote
system.
Each of these lookups has O(n) complexity and makes match_push_refs()
be an O(m*n) operation, where m is the number of local refs and n is
the number of remote refs. If there are many refs 100,000+, then this
ref matching can take a significant amount of time. Let's prepare an
index of the remote refs to allow searching in O(log n) time and
reduce the complexity of match_push_refs() to O(m log n).
We prepare the index lazily so that it is only created when necessary.
So, there should be no impact when _not_ using a matching or pattern
refspec, i.e. when pushing using only explicit refspecs.
Dry-run push of a repository with 121,913 local and remote refs:
before after
real 1m40.582s 0m0.804s
user 1m39.914s 0m0.515s
sys 0m0.125s 0m0.106s
The creation of the index has overhead. So, if there are very few
local refs, then it could take longer to create the index than it
would have taken to just perform n linear lookups into the remote
ref space. Using the index should provide some improvement when
the number of local refs is roughly greater than the log of the
number of remote refs (i.e. m >= log n). The pathological case is
when there is a single local ref and very many remote refs.
Dry-run push of a repository with 121,913 remote refs and a single
local ref:
before after
real 0m0.525s 0m0.566s
user 0m0.243s 0m0.279s
sys 0m0.075s 0m0.099s
Using an index takes 41 ms longer, or roughly 7.8% longer.
Jeff King measured a no-op push of a single ref into a remote repo
with 370,000 refs:
before after
real 0m1.087s 0m1.156s
user 0m1.344s 0m1.412s
sys 0m0.288s 0m0.284s
Using an index takes 69 ms longer, or roughly 6.3% longer.
None of the measurements above required transferring any objects to
the remote repository. If the push required transferring objects and
updating the refs in the remote repository, the impact of preparing
the search index would be even smaller.
A similar operation is performed in the reverse direction when pruning
using a matching or pattern refspec. Let's avoid O(m*n) behavior in
the same way by lazily preparing an index on the local refs.
Signed-off-by: Brandon Casey <drafnel@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-08 12:58:39 +04:00
|
|
|
struct string_list src_ref_index = STRING_LIST_INIT_NODUP;
|
2012-02-23 02:43:41 +04:00
|
|
|
/* check for missing refs on the remote */
|
|
|
|
for (ref = *dst; ref; ref = ref->next) {
|
|
|
|
char *src_name;
|
|
|
|
|
|
|
|
if (ref->peer_ref)
|
|
|
|
/* We're already sending something to this ref. */
|
|
|
|
continue;
|
|
|
|
|
|
|
|
src_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_DST, NULL);
|
|
|
|
if (src_name) {
|
remote.c: avoid O(m*n) behavior in match_push_refs
When pushing using a matching refspec or a pattern refspec, each ref
in the local repository must be paired with a ref advertised by the
remote server. This is accomplished by using the refspec to transform
the name of the local ref into the name it should have in the remote
repository, and then performing a linear search through the list of
remote refs to see if the remote ref was advertised by the remote
system.
Each of these lookups has O(n) complexity and makes match_push_refs()
be an O(m*n) operation, where m is the number of local refs and n is
the number of remote refs. If there are many refs 100,000+, then this
ref matching can take a significant amount of time. Let's prepare an
index of the remote refs to allow searching in O(log n) time and
reduce the complexity of match_push_refs() to O(m log n).
We prepare the index lazily so that it is only created when necessary.
So, there should be no impact when _not_ using a matching or pattern
refspec, i.e. when pushing using only explicit refspecs.
Dry-run push of a repository with 121,913 local and remote refs:
before after
real 1m40.582s 0m0.804s
user 1m39.914s 0m0.515s
sys 0m0.125s 0m0.106s
The creation of the index has overhead. So, if there are very few
local refs, then it could take longer to create the index than it
would have taken to just perform n linear lookups into the remote
ref space. Using the index should provide some improvement when
the number of local refs is roughly greater than the log of the
number of remote refs (i.e. m >= log n). The pathological case is
when there is a single local ref and very many remote refs.
Dry-run push of a repository with 121,913 remote refs and a single
local ref:
before after
real 0m0.525s 0m0.566s
user 0m0.243s 0m0.279s
sys 0m0.075s 0m0.099s
Using an index takes 41 ms longer, or roughly 7.8% longer.
Jeff King measured a no-op push of a single ref into a remote repo
with 370,000 refs:
before after
real 0m1.087s 0m1.156s
user 0m1.344s 0m1.412s
sys 0m0.288s 0m0.284s
Using an index takes 69 ms longer, or roughly 6.3% longer.
None of the measurements above required transferring any objects to
the remote repository. If the push required transferring objects and
updating the refs in the remote repository, the impact of preparing
the search index would be even smaller.
A similar operation is performed in the reverse direction when pruning
using a matching or pattern refspec. Let's avoid O(m*n) behavior in
the same way by lazily preparing an index on the local refs.
Signed-off-by: Brandon Casey <drafnel@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-08 12:58:39 +04:00
|
|
|
if (!src_ref_index.nr)
|
|
|
|
prepare_ref_index(&src_ref_index, src);
|
|
|
|
if (!string_list_has_string(&src_ref_index,
|
|
|
|
src_name))
|
2012-02-23 02:43:41 +04:00
|
|
|
ref->peer_ref = alloc_delete_ref();
|
|
|
|
free(src_name);
|
|
|
|
}
|
|
|
|
}
|
remote.c: avoid O(m*n) behavior in match_push_refs
When pushing using a matching refspec or a pattern refspec, each ref
in the local repository must be paired with a ref advertised by the
remote server. This is accomplished by using the refspec to transform
the name of the local ref into the name it should have in the remote
repository, and then performing a linear search through the list of
remote refs to see if the remote ref was advertised by the remote
system.
Each of these lookups has O(n) complexity and makes match_push_refs()
be an O(m*n) operation, where m is the number of local refs and n is
the number of remote refs. If there are many refs 100,000+, then this
ref matching can take a significant amount of time. Let's prepare an
index of the remote refs to allow searching in O(log n) time and
reduce the complexity of match_push_refs() to O(m log n).
We prepare the index lazily so that it is only created when necessary.
So, there should be no impact when _not_ using a matching or pattern
refspec, i.e. when pushing using only explicit refspecs.
Dry-run push of a repository with 121,913 local and remote refs:
before after
real 1m40.582s 0m0.804s
user 1m39.914s 0m0.515s
sys 0m0.125s 0m0.106s
The creation of the index has overhead. So, if there are very few
local refs, then it could take longer to create the index than it
would have taken to just perform n linear lookups into the remote
ref space. Using the index should provide some improvement when
the number of local refs is roughly greater than the log of the
number of remote refs (i.e. m >= log n). The pathological case is
when there is a single local ref and very many remote refs.
Dry-run push of a repository with 121,913 remote refs and a single
local ref:
before after
real 0m0.525s 0m0.566s
user 0m0.243s 0m0.279s
sys 0m0.075s 0m0.099s
Using an index takes 41 ms longer, or roughly 7.8% longer.
Jeff King measured a no-op push of a single ref into a remote repo
with 370,000 refs:
before after
real 0m1.087s 0m1.156s
user 0m1.344s 0m1.412s
sys 0m0.288s 0m0.284s
Using an index takes 69 ms longer, or roughly 6.3% longer.
None of the measurements above required transferring any objects to
the remote repository. If the push required transferring objects and
updating the refs in the remote repository, the impact of preparing
the search index would be even smaller.
A similar operation is performed in the reverse direction when pruning
using a matching or pattern refspec. Let's avoid O(m*n) behavior in
the same way by lazily preparing an index on the local refs.
Signed-off-by: Brandon Casey <drafnel@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-08 12:58:39 +04:00
|
|
|
string_list_clear(&src_ref_index, 0);
|
2012-02-23 02:43:41 +04:00
|
|
|
}
|
2009-02-25 11:32:17 +03:00
|
|
|
if (errs)
|
|
|
|
return -1;
|
2007-05-12 19:45:59 +04:00
|
|
|
return 0;
|
|
|
|
}
|
2007-09-11 07:02:56 +04:00
|
|
|
|
2010-01-08 05:12:42 +03:00
|
|
|
void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
|
2013-07-09 01:42:40 +04:00
|
|
|
int force_update)
|
2010-01-08 05:12:42 +03:00
|
|
|
{
|
|
|
|
struct ref *ref;
|
|
|
|
|
|
|
|
for (ref = remote_refs; ref; ref = ref->next) {
|
2012-11-30 05:41:36 +04:00
|
|
|
int force_ref_update = ref->force || force_update;
|
2013-07-09 01:42:40 +04:00
|
|
|
int reject_reason = 0;
|
2012-11-30 05:41:36 +04:00
|
|
|
|
2010-01-08 05:12:42 +03:00
|
|
|
if (ref->peer_ref)
|
|
|
|
hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
|
|
|
|
else if (!send_mirror)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ref->deletion = is_null_sha1(ref->new_sha1);
|
|
|
|
if (!ref->deletion &&
|
|
|
|
!hashcmp(ref->old_sha1, ref->new_sha1)) {
|
|
|
|
ref->status = REF_STATUS_UPTODATE;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-11-30 05:41:40 +04:00
|
|
|
/*
|
2013-07-09 01:42:40 +04:00
|
|
|
* Bypass the usual "must fast-forward" check but
|
|
|
|
* replace it with a weaker "the old value must be
|
|
|
|
* this value we observed". If the remote ref has
|
|
|
|
* moved and is now different from what we expect,
|
|
|
|
* reject any push.
|
|
|
|
*
|
|
|
|
* It also is an error if the user told us to check
|
|
|
|
* with the remote-tracking branch to find the value
|
|
|
|
* to expect, but we did not have such a tracking
|
|
|
|
* branch.
|
|
|
|
*/
|
|
|
|
if (ref->expect_old_sha1) {
|
|
|
|
if (ref->expect_old_no_trackback ||
|
|
|
|
hashcmp(ref->old_sha1, ref->old_sha1_expect))
|
|
|
|
reject_reason = REF_STATUS_REJECT_STALE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The usual "must fast-forward" rules.
|
|
|
|
*
|
push: fix "refs/tags/ hierarchy cannot be updated without --force"
When pushing to update a branch with a commit that is not a
descendant of the commit at the tip, a wrong message "already
exists" was given, instead of the correct "non-fast-forward", if we
do not have the object sitting in the destination repository at the
tip of the ref we are updating.
The primary cause of the bug is that the check in a new helper
function is_forwardable() assumed both old and new objects are
available and can be checked, which is not always the case.
The way the caller uses the result of this function is also wrong.
If the helper says "we do not want to let this push go through", the
caller unconditionally translates it into "we blocked it because the
destination already exists", which is not true at all in this case.
Fix this by doing these three things:
* Remove unnecessary not_forwardable from "struct ref"; it is only
used inside set_ref_status_for_push();
* Make "refs/tags/" the only hierarchy that cannot be replaced
without --force;
* Remove the misguided attempt to force that everything that
updates an existing ref has to be a commit outside "refs/tags/"
hierarchy.
The policy last one tried to implement may later be resurrected and
extended to ensure fast-forwardness (defined as "not losing
objects", extending from the traditional "not losing commits from
the resulting history") when objects that are not commit are
involved (e.g. an annotated tag in hierarchies outside refs/tags),
but such a logic belongs to "is this a fast-forward?" check that is
done by ref_newer(); is_forwardable(), which is now removed, was not
the right place to do so.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-17 01:02:27 +04:00
|
|
|
* Decide whether an individual refspec A:B can be
|
|
|
|
* pushed. The push will succeed if any of the
|
|
|
|
* following are true:
|
2010-01-08 05:12:42 +03:00
|
|
|
*
|
2012-11-30 05:41:40 +04:00
|
|
|
* (1) the remote reference B does not exist
|
2010-01-08 05:12:42 +03:00
|
|
|
*
|
2012-11-30 05:41:40 +04:00
|
|
|
* (2) the remote reference B is being removed (i.e.,
|
|
|
|
* pushing :B where no source is specified)
|
2010-01-08 05:12:42 +03:00
|
|
|
*
|
push: fix "refs/tags/ hierarchy cannot be updated without --force"
When pushing to update a branch with a commit that is not a
descendant of the commit at the tip, a wrong message "already
exists" was given, instead of the correct "non-fast-forward", if we
do not have the object sitting in the destination repository at the
tip of the ref we are updating.
The primary cause of the bug is that the check in a new helper
function is_forwardable() assumed both old and new objects are
available and can be checked, which is not always the case.
The way the caller uses the result of this function is also wrong.
If the helper says "we do not want to let this push go through", the
caller unconditionally translates it into "we blocked it because the
destination already exists", which is not true at all in this case.
Fix this by doing these three things:
* Remove unnecessary not_forwardable from "struct ref"; it is only
used inside set_ref_status_for_push();
* Make "refs/tags/" the only hierarchy that cannot be replaced
without --force;
* Remove the misguided attempt to force that everything that
updates an existing ref has to be a commit outside "refs/tags/"
hierarchy.
The policy last one tried to implement may later be resurrected and
extended to ensure fast-forwardness (defined as "not losing
objects", extending from the traditional "not losing commits from
the resulting history") when objects that are not commit are
involved (e.g. an annotated tag in hierarchies outside refs/tags),
but such a logic belongs to "is this a fast-forward?" check that is
done by ref_newer(); is_forwardable(), which is now removed, was not
the right place to do so.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-17 01:02:27 +04:00
|
|
|
* (3) the destination is not under refs/tags/, and
|
|
|
|
* if the old and new value is a commit, the new
|
|
|
|
* is a descendant of the old.
|
2010-01-08 05:12:42 +03:00
|
|
|
*
|
2012-11-30 05:41:40 +04:00
|
|
|
* (4) it is forced using the +A:B notation, or by
|
|
|
|
* passing the --force argument
|
2010-01-08 05:12:42 +03:00
|
|
|
*/
|
|
|
|
|
2013-07-09 01:42:40 +04:00
|
|
|
else if (!ref->deletion && !is_null_sha1(ref->old_sha1)) {
|
2013-12-01 00:55:40 +04:00
|
|
|
if (starts_with(ref->name, "refs/tags/"))
|
2013-07-09 01:42:40 +04:00
|
|
|
reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
|
push: introduce REJECT_FETCH_FIRST and REJECT_NEEDS_FORCE
When we push to update an existing ref, if:
* the object at the tip of the remote is not a commit; or
* the object we are pushing is not a commit,
it won't be correct to suggest to fetch, integrate and push again,
as the old and new objects will not "merge". We should explain that
the push must be forced when there is a non-committish object is
involved in such a case.
If we do not have the current object at the tip of the remote, we do
not even know that object, when fetched, is something that can be
merged. In such a case, suggesting to pull first just like
non-fast-forward case may not be technically correct, but in
practice, most such failures are seen when you try to push your work
to a branch without knowing that somebody else already pushed to
update the same branch since you forked, so "pull first" would work
as a suggestion most of the time. And if the object at the tip is
not a commit, "pull first" will fail, without making any permanent
damage. As a side effect, it also makes the error message the user
will get during the next "push" attempt easier to understand, now
the user is aware that a non-commit object is involved.
In these cases, the current code already rejects such a push on the
client end, but we used the same error and advice messages as the
ones used when rejecting a non-fast-forward push, i.e. pull from
there and integrate before pushing again.
Introduce new rejection reasons and reword the messages
appropriately.
[jc: with help by Peff on message details]
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-24 01:55:30 +04:00
|
|
|
else if (!has_sha1_file(ref->old_sha1))
|
2013-07-09 01:42:40 +04:00
|
|
|
reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
|
push: introduce REJECT_FETCH_FIRST and REJECT_NEEDS_FORCE
When we push to update an existing ref, if:
* the object at the tip of the remote is not a commit; or
* the object we are pushing is not a commit,
it won't be correct to suggest to fetch, integrate and push again,
as the old and new objects will not "merge". We should explain that
the push must be forced when there is a non-committish object is
involved in such a case.
If we do not have the current object at the tip of the remote, we do
not even know that object, when fetched, is something that can be
merged. In such a case, suggesting to pull first just like
non-fast-forward case may not be technically correct, but in
practice, most such failures are seen when you try to push your work
to a branch without knowing that somebody else already pushed to
update the same branch since you forked, so "pull first" would work
as a suggestion most of the time. And if the object at the tip is
not a commit, "pull first" will fail, without making any permanent
damage. As a side effect, it also makes the error message the user
will get during the next "push" attempt easier to understand, now
the user is aware that a non-commit object is involved.
In these cases, the current code already rejects such a push on the
client end, but we used the same error and advice messages as the
ones used when rejecting a non-fast-forward push, i.e. pull from
there and integrate before pushing again.
Introduce new rejection reasons and reword the messages
appropriately.
[jc: with help by Peff on message details]
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-24 01:55:30 +04:00
|
|
|
else if (!lookup_commit_reference_gently(ref->old_sha1, 1) ||
|
|
|
|
!lookup_commit_reference_gently(ref->new_sha1, 1))
|
2013-07-09 01:42:40 +04:00
|
|
|
reject_reason = REF_STATUS_REJECT_NEEDS_FORCE;
|
push: introduce REJECT_FETCH_FIRST and REJECT_NEEDS_FORCE
When we push to update an existing ref, if:
* the object at the tip of the remote is not a commit; or
* the object we are pushing is not a commit,
it won't be correct to suggest to fetch, integrate and push again,
as the old and new objects will not "merge". We should explain that
the push must be forced when there is a non-committish object is
involved in such a case.
If we do not have the current object at the tip of the remote, we do
not even know that object, when fetched, is something that can be
merged. In such a case, suggesting to pull first just like
non-fast-forward case may not be technically correct, but in
practice, most such failures are seen when you try to push your work
to a branch without knowing that somebody else already pushed to
update the same branch since you forked, so "pull first" would work
as a suggestion most of the time. And if the object at the tip is
not a commit, "pull first" will fail, without making any permanent
damage. As a side effect, it also makes the error message the user
will get during the next "push" attempt easier to understand, now
the user is aware that a non-commit object is involved.
In these cases, the current code already rejects such a push on the
client end, but we used the same error and advice messages as the
ones used when rejecting a non-fast-forward push, i.e. pull from
there and integrate before pushing again.
Introduce new rejection reasons and reword the messages
appropriately.
[jc: with help by Peff on message details]
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-24 01:55:30 +04:00
|
|
|
else if (!ref_newer(ref->new_sha1, ref->old_sha1))
|
2013-07-09 01:42:40 +04:00
|
|
|
reject_reason = REF_STATUS_REJECT_NONFASTFORWARD;
|
2010-01-08 05:12:42 +03:00
|
|
|
}
|
2013-07-09 01:42:40 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* "--force" will defeat any rejection implemented
|
|
|
|
* by the rules above.
|
|
|
|
*/
|
|
|
|
if (!force_ref_update)
|
|
|
|
ref->status = reject_reason;
|
|
|
|
else if (reject_reason)
|
|
|
|
ref->forced_update = 1;
|
2010-01-08 05:12:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-11 07:02:56 +04:00
|
|
|
struct branch *branch_get(const char *name)
|
|
|
|
{
|
|
|
|
struct branch *ret;
|
|
|
|
|
|
|
|
read_config();
|
|
|
|
if (!name || !*name || !strcmp(name, "HEAD"))
|
|
|
|
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) {
|
|
|
|
int i;
|
2013-05-28 16:54:27 +04:00
|
|
|
ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge));
|
2007-09-11 07:02:56 +04:00
|
|
|
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]);
|
2009-04-02 01:42:49 +04:00
|
|
|
if (remote_find_tracking(ret->remote, ret->merge[i])
|
|
|
|
&& !strcmp(ret->remote_name, "."))
|
|
|
|
ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
|
2007-09-11 07:02:56 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int branch_has_merge_config(struct branch *branch)
|
|
|
|
{
|
|
|
|
return branch && !!branch->merge;
|
|
|
|
}
|
|
|
|
|
2007-09-18 12:54:53 +04:00
|
|
|
int branch_merge_matches(struct branch *branch,
|
|
|
|
int i,
|
|
|
|
const char *refname)
|
2007-09-11 07:02:56 +04:00
|
|
|
{
|
2007-09-18 12:54:53 +04:00
|
|
|
if (!branch || i < 0 || i >= branch->merge_nr)
|
2007-09-11 07:02:56 +04:00
|
|
|
return 0;
|
2014-01-14 07:16:07 +04:00
|
|
|
return refname_match(branch->merge[i]->src, refname);
|
2007-09-11 07:02:56 +04:00
|
|
|
}
|
2007-09-11 07:03:08 +04:00
|
|
|
|
fetch: ignore wildcarded refspecs that update local symbolic refs
In a repository cloned from somewhere else, you typically have a
symbolic ref refs/remotes/origin/HEAD pointing at the 'master'
remote-tracking ref that is next to it. When fetching into such a
repository with "git fetch --mirror" from another repository that
was similarly cloned, the implied wildcard refspec refs/*:refs/*
will end up asking to update refs/remotes/origin/HEAD with the
object at refs/remotes/origin/HEAD at the remote side, while asking
to update refs/remotes/origin/master the same way. Depending on the
order the two updates happen, the latter one would find that the
value of the ref before it is updated has changed from what the code
expects.
When the user asks to update the underlying ref via the symbolic ref
explicitly without using a wildcard refspec, e.g. "git fetch $there
refs/heads/master:refs/remotes/origin/HEAD", we should still let him
do so, but when expanding wildcard refs, it will result in a more
intuitive outcome if we simply ignore local symbolic refs.
As the purpose of the symbolic ref refs/remotes/origin/HEAD is to
follow the ref it points at (e.g. refs/remotes/origin/master), its
value would change when the underlying ref is updated.
Earlier commit da3efdb (receive-pack: detect aliased updates which
can occur with symrefs, 2010-04-19) fixed a similar issue for "git
push".
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-12-12 01:00:52 +04:00
|
|
|
static int ignore_symref_update(const char *refname)
|
|
|
|
{
|
|
|
|
unsigned char sha1[20];
|
|
|
|
int flag;
|
|
|
|
|
|
|
|
if (!resolve_ref_unsafe(refname, sha1, 0, &flag))
|
|
|
|
return 0; /* non-existing refs are OK */
|
|
|
|
return (flag & REF_ISSYMREF);
|
|
|
|
}
|
|
|
|
|
2013-10-30 09:32:56 +04:00
|
|
|
/*
|
|
|
|
* Create and return a list of (struct ref) consisting of copies of
|
|
|
|
* each remote_ref that matches refspec. refspec must be a pattern.
|
|
|
|
* Fill in the copies' peer_ref to describe the local tracking refs to
|
|
|
|
* which they map. Omit any references that would map to an existing
|
|
|
|
* local symbolic ref.
|
|
|
|
*/
|
2007-10-30 04:05:40 +03:00
|
|
|
static struct ref *get_expanded_map(const struct ref *remote_refs,
|
2007-09-11 07:03:08 +04:00
|
|
|
const struct refspec *refspec)
|
|
|
|
{
|
2007-10-30 04:05:40 +03:00
|
|
|
const struct ref *ref;
|
2007-09-11 07:03:08 +04:00
|
|
|
struct ref *ret = NULL;
|
|
|
|
struct ref **tail = &ret;
|
|
|
|
|
|
|
|
for (ref = remote_refs; ref; ref = ref->next) {
|
2013-10-30 09:32:57 +04:00
|
|
|
char *expn_name = NULL;
|
|
|
|
|
2007-09-11 07:03:08 +04:00
|
|
|
if (strchr(ref->name, '^'))
|
|
|
|
continue; /* a dereference item */
|
2009-03-07 09:11:34 +03:00
|
|
|
if (match_name_with_pattern(refspec->src, ref->name,
|
fetch: ignore wildcarded refspecs that update local symbolic refs
In a repository cloned from somewhere else, you typically have a
symbolic ref refs/remotes/origin/HEAD pointing at the 'master'
remote-tracking ref that is next to it. When fetching into such a
repository with "git fetch --mirror" from another repository that
was similarly cloned, the implied wildcard refspec refs/*:refs/*
will end up asking to update refs/remotes/origin/HEAD with the
object at refs/remotes/origin/HEAD at the remote side, while asking
to update refs/remotes/origin/master the same way. Depending on the
order the two updates happen, the latter one would find that the
value of the ref before it is updated has changed from what the code
expects.
When the user asks to update the underlying ref via the symbolic ref
explicitly without using a wildcard refspec, e.g. "git fetch $there
refs/heads/master:refs/remotes/origin/HEAD", we should still let him
do so, but when expanding wildcard refs, it will result in a more
intuitive outcome if we simply ignore local symbolic refs.
As the purpose of the symbolic ref refs/remotes/origin/HEAD is to
follow the ref it points at (e.g. refs/remotes/origin/master), its
value would change when the underlying ref is updated.
Earlier commit da3efdb (receive-pack: detect aliased updates which
can occur with symrefs, 2010-04-19) fixed a similar issue for "git
push".
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-12-12 01:00:52 +04:00
|
|
|
refspec->dst, &expn_name) &&
|
|
|
|
!ignore_symref_update(expn_name)) {
|
2007-09-11 07:03:08 +04:00
|
|
|
struct ref *cpy = copy_ref(ref);
|
|
|
|
|
2009-03-07 09:11:34 +03:00
|
|
|
cpy->peer_ref = alloc_ref(expn_name);
|
2007-09-11 07:03:08 +04:00
|
|
|
if (refspec->force)
|
|
|
|
cpy->peer_ref->force = 1;
|
|
|
|
*tail = cpy;
|
|
|
|
tail = &cpy->next;
|
|
|
|
}
|
2013-10-30 09:32:57 +04:00
|
|
|
free(expn_name);
|
2007-09-11 07:03:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-10-30 04:05:40 +03:00
|
|
|
static const struct ref *find_ref_by_name_abbrev(const struct ref *refs, const char *name)
|
2007-09-11 07:03:08 +04:00
|
|
|
{
|
2007-10-30 04:05:40 +03:00
|
|
|
const struct ref *ref;
|
2007-09-11 07:03:08 +04:00
|
|
|
for (ref = refs; ref; ref = ref->next) {
|
2014-01-14 07:16:07 +04:00
|
|
|
if (refname_match(name, ref->name))
|
2007-09-11 07:03:08 +04:00
|
|
|
return ref;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2007-10-30 04:05:40 +03:00
|
|
|
struct ref *get_remote_ref(const struct ref *remote_refs, const char *name)
|
2007-09-11 07:03:08 +04:00
|
|
|
{
|
2007-10-30 04:05:40 +03:00
|
|
|
const struct ref *ref = find_ref_by_name_abbrev(remote_refs, name);
|
2007-09-11 07:03:08 +04:00
|
|
|
|
|
|
|
if (!ref)
|
2007-10-27 10:09:48 +04:00
|
|
|
return NULL;
|
2007-09-11 07:03:08 +04:00
|
|
|
|
|
|
|
return copy_ref(ref);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ref *get_local_ref(const char *name)
|
|
|
|
{
|
2009-06-17 17:38:36 +04:00
|
|
|
if (!name || name[0] == '\0')
|
2007-09-11 07:03:08 +04:00
|
|
|
return NULL;
|
|
|
|
|
2013-12-01 00:55:40 +04:00
|
|
|
if (starts_with(name, "refs/"))
|
2008-10-18 12:44:18 +04:00
|
|
|
return alloc_ref(name);
|
2007-09-11 07:03:08 +04:00
|
|
|
|
2013-12-01 00:55:40 +04:00
|
|
|
if (starts_with(name, "heads/") ||
|
|
|
|
starts_with(name, "tags/") ||
|
|
|
|
starts_with(name, "remotes/"))
|
2008-10-18 12:37:40 +04:00
|
|
|
return alloc_ref_with_prefix("refs/", 5, name);
|
2007-09-11 07:03:08 +04:00
|
|
|
|
2008-10-18 12:37:40 +04:00
|
|
|
return alloc_ref_with_prefix("refs/heads/", 11, name);
|
2007-09-11 07:03:08 +04:00
|
|
|
}
|
|
|
|
|
2007-10-30 04:05:40 +03:00
|
|
|
int get_fetch_map(const struct ref *remote_refs,
|
2007-09-11 07:03:08 +04:00
|
|
|
const struct refspec *refspec,
|
2007-10-27 10:09:48 +04:00
|
|
|
struct ref ***tail,
|
|
|
|
int missing_ok)
|
2007-09-11 07:03:08 +04:00
|
|
|
{
|
2008-03-18 05:05:23 +03:00
|
|
|
struct ref *ref_map, **rmp;
|
2007-09-11 07:03:08 +04:00
|
|
|
|
|
|
|
if (refspec->pattern) {
|
|
|
|
ref_map = get_expanded_map(remote_refs, refspec);
|
|
|
|
} else {
|
2007-10-27 10:09:48 +04:00
|
|
|
const char *name = refspec->src[0] ? refspec->src : "HEAD";
|
|
|
|
|
2013-01-30 02:02:15 +04:00
|
|
|
if (refspec->exact_sha1) {
|
|
|
|
ref_map = alloc_ref(name);
|
|
|
|
get_sha1_hex(name, ref_map->old_sha1);
|
|
|
|
} else {
|
|
|
|
ref_map = get_remote_ref(remote_refs, name);
|
|
|
|
}
|
2007-10-27 10:09:48 +04:00
|
|
|
if (!missing_ok && !ref_map)
|
|
|
|
die("Couldn't find remote ref %s", name);
|
|
|
|
if (ref_map) {
|
|
|
|
ref_map->peer_ref = get_local_ref(refspec->dst);
|
|
|
|
if (ref_map->peer_ref && refspec->force)
|
|
|
|
ref_map->peer_ref->force = 1;
|
|
|
|
}
|
2007-09-11 07:03:08 +04:00
|
|
|
}
|
|
|
|
|
2008-03-18 05:05:23 +03:00
|
|
|
for (rmp = &ref_map; *rmp; ) {
|
|
|
|
if ((*rmp)->peer_ref) {
|
2013-12-01 00:55:40 +04:00
|
|
|
if (!starts_with((*rmp)->peer_ref->name, "refs/") ||
|
2012-05-05 02:35:18 +04:00
|
|
|
check_refname_format((*rmp)->peer_ref->name, 0)) {
|
2008-03-18 05:05:23 +03:00
|
|
|
struct ref *ignore = *rmp;
|
|
|
|
error("* Ignoring funny ref '%s' locally",
|
|
|
|
(*rmp)->peer_ref->name);
|
|
|
|
*rmp = (*rmp)->next;
|
|
|
|
free(ignore->peer_ref);
|
|
|
|
free(ignore);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rmp = &((*rmp)->next);
|
2007-09-11 07:03:08 +04:00
|
|
|
}
|
|
|
|
|
2007-10-13 00:40:04 +04:00
|
|
|
if (ref_map)
|
|
|
|
tail_link_ref(ref_map, tail);
|
2007-09-11 07:03:08 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2008-04-26 23:53:12 +04:00
|
|
|
|
|
|
|
int resolve_remote_symref(struct ref *ref, struct ref *list)
|
|
|
|
{
|
|
|
|
if (!ref->symref)
|
|
|
|
return 0;
|
|
|
|
for (; list; list = list->next)
|
|
|
|
if (!strcmp(ref->symref, list->name)) {
|
|
|
|
hashcpy(ref->old_sha1, list->old_sha1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
2008-07-02 11:51:18 +04:00
|
|
|
|
2009-02-25 11:32:12 +03:00
|
|
|
static void unmark_and_free(struct commit_list *list, unsigned int mark)
|
|
|
|
{
|
|
|
|
while (list) {
|
|
|
|
struct commit_list *temp = list;
|
|
|
|
temp->item->object.flags &= ~mark;
|
|
|
|
list = temp->next;
|
|
|
|
free(temp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
|
|
|
|
{
|
|
|
|
struct object *o;
|
|
|
|
struct commit *old, *new;
|
|
|
|
struct commit_list *list, *used;
|
|
|
|
int found = 0;
|
|
|
|
|
push: introduce REJECT_FETCH_FIRST and REJECT_NEEDS_FORCE
When we push to update an existing ref, if:
* the object at the tip of the remote is not a commit; or
* the object we are pushing is not a commit,
it won't be correct to suggest to fetch, integrate and push again,
as the old and new objects will not "merge". We should explain that
the push must be forced when there is a non-committish object is
involved in such a case.
If we do not have the current object at the tip of the remote, we do
not even know that object, when fetched, is something that can be
merged. In such a case, suggesting to pull first just like
non-fast-forward case may not be technically correct, but in
practice, most such failures are seen when you try to push your work
to a branch without knowing that somebody else already pushed to
update the same branch since you forked, so "pull first" would work
as a suggestion most of the time. And if the object at the tip is
not a commit, "pull first" will fail, without making any permanent
damage. As a side effect, it also makes the error message the user
will get during the next "push" attempt easier to understand, now
the user is aware that a non-commit object is involved.
In these cases, the current code already rejects such a push on the
client end, but we used the same error and advice messages as the
ones used when rejecting a non-fast-forward push, i.e. pull from
there and integrate before pushing again.
Introduce new rejection reasons and reword the messages
appropriately.
[jc: with help by Peff on message details]
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-01-24 01:55:30 +04:00
|
|
|
/*
|
|
|
|
* Both new and old must be commit-ish and new is descendant of
|
2009-02-25 11:32:12 +03:00
|
|
|
* old. Otherwise we require --force.
|
|
|
|
*/
|
|
|
|
o = deref_tag(parse_object(old_sha1), NULL, 0);
|
|
|
|
if (!o || o->type != OBJ_COMMIT)
|
|
|
|
return 0;
|
|
|
|
old = (struct commit *) o;
|
|
|
|
|
|
|
|
o = deref_tag(parse_object(new_sha1), NULL, 0);
|
|
|
|
if (!o || o->type != OBJ_COMMIT)
|
|
|
|
return 0;
|
|
|
|
new = (struct commit *) o;
|
|
|
|
|
|
|
|
if (parse_commit(new) < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
used = list = NULL;
|
|
|
|
commit_list_insert(new, &list);
|
|
|
|
while (list) {
|
|
|
|
new = pop_most_recent_commit(&list, TMP_MARK);
|
|
|
|
commit_list_insert(new, &used);
|
|
|
|
if (new == old) {
|
|
|
|
found = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
unmark_and_free(list, TMP_MARK);
|
|
|
|
unmark_and_free(used, TMP_MARK);
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
2008-07-02 11:51:18 +04:00
|
|
|
/*
|
branch: report invalid tracking branch as gone
Command "git branch -vv" will report tracking branches, but invalid
tracking branches are also reported. This is because the function
stat_tracking_info() can not distinguish invalid tracking branch
from other cases which it would not like to report, such as
there is no upstream settings at all, or nothing is changed between
one branch and its upstream.
Junio suggested missing upstream should be reported [1] like:
$ git branch -v -v
master e67ac84 initial
* topic 3fc0f2a [topicbase: gone] topic
$ git status
# On branch topic
# Your branch is based on 'topicbase', but the upstream is gone.
# (use "git branch --unset-upstream" to fixup)
...
$ git status -b -s
## topic...topicbase [gone]
...
In order to do like that, we need to distinguish these three cases
(i.e. no tracking, with configured but no longer valid tracking, and
with tracking) in function stat_tracking_info(). So the refactored
function stat_tracking_info() has three return values: -1 (with "gone"
base), 0 (no base), and 1 (with base).
If the caller does not like to report tracking info when nothing
changed between the branch and its upstream, simply checks if
num_theirs and num_ours are both 0.
[1]: http://thread.gmane.org/gmane.comp.version-control.git/231830/focus=232288
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-26 11:02:48 +04:00
|
|
|
* Compare a branch with its upstream, and save their differences (number
|
|
|
|
* of commits) in *num_ours and *num_theirs.
|
|
|
|
*
|
|
|
|
* Return 0 if branch has no upstream (no base), -1 if upstream is missing
|
|
|
|
* (with "gone" base), otherwise 1 (with base).
|
2008-07-02 11:51:18 +04:00
|
|
|
*/
|
|
|
|
int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)
|
|
|
|
{
|
|
|
|
unsigned char sha1[20];
|
|
|
|
struct commit *ours, *theirs;
|
|
|
|
char symmetric[84];
|
|
|
|
struct rev_info revs;
|
|
|
|
const char *rev_argv[10], *base;
|
|
|
|
int rev_argc;
|
|
|
|
|
branch: report invalid tracking branch as gone
Command "git branch -vv" will report tracking branches, but invalid
tracking branches are also reported. This is because the function
stat_tracking_info() can not distinguish invalid tracking branch
from other cases which it would not like to report, such as
there is no upstream settings at all, or nothing is changed between
one branch and its upstream.
Junio suggested missing upstream should be reported [1] like:
$ git branch -v -v
master e67ac84 initial
* topic 3fc0f2a [topicbase: gone] topic
$ git status
# On branch topic
# Your branch is based on 'topicbase', but the upstream is gone.
# (use "git branch --unset-upstream" to fixup)
...
$ git status -b -s
## topic...topicbase [gone]
...
In order to do like that, we need to distinguish these three cases
(i.e. no tracking, with configured but no longer valid tracking, and
with tracking) in function stat_tracking_info(). So the refactored
function stat_tracking_info() has three return values: -1 (with "gone"
base), 0 (no base), and 1 (with base).
If the caller does not like to report tracking info when nothing
changed between the branch and its upstream, simply checks if
num_theirs and num_ours are both 0.
[1]: http://thread.gmane.org/gmane.comp.version-control.git/231830/focus=232288
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-26 11:02:48 +04:00
|
|
|
/* Cannot stat unless we are marked to build on top of somebody else. */
|
2008-07-02 11:51:18 +04:00
|
|
|
if (!branch ||
|
|
|
|
!branch->merge || !branch->merge[0] || !branch->merge[0]->dst)
|
|
|
|
return 0;
|
|
|
|
|
branch: report invalid tracking branch as gone
Command "git branch -vv" will report tracking branches, but invalid
tracking branches are also reported. This is because the function
stat_tracking_info() can not distinguish invalid tracking branch
from other cases which it would not like to report, such as
there is no upstream settings at all, or nothing is changed between
one branch and its upstream.
Junio suggested missing upstream should be reported [1] like:
$ git branch -v -v
master e67ac84 initial
* topic 3fc0f2a [topicbase: gone] topic
$ git status
# On branch topic
# Your branch is based on 'topicbase', but the upstream is gone.
# (use "git branch --unset-upstream" to fixup)
...
$ git status -b -s
## topic...topicbase [gone]
...
In order to do like that, we need to distinguish these three cases
(i.e. no tracking, with configured but no longer valid tracking, and
with tracking) in function stat_tracking_info(). So the refactored
function stat_tracking_info() has three return values: -1 (with "gone"
base), 0 (no base), and 1 (with base).
If the caller does not like to report tracking info when nothing
changed between the branch and its upstream, simply checks if
num_theirs and num_ours are both 0.
[1]: http://thread.gmane.org/gmane.comp.version-control.git/231830/focus=232288
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-26 11:02:48 +04:00
|
|
|
/* Cannot stat if what we used to build on no longer exists */
|
2008-07-02 11:51:18 +04:00
|
|
|
base = branch->merge[0]->dst;
|
2011-11-13 14:22:14 +04:00
|
|
|
if (read_ref(base, sha1))
|
branch: report invalid tracking branch as gone
Command "git branch -vv" will report tracking branches, but invalid
tracking branches are also reported. This is because the function
stat_tracking_info() can not distinguish invalid tracking branch
from other cases which it would not like to report, such as
there is no upstream settings at all, or nothing is changed between
one branch and its upstream.
Junio suggested missing upstream should be reported [1] like:
$ git branch -v -v
master e67ac84 initial
* topic 3fc0f2a [topicbase: gone] topic
$ git status
# On branch topic
# Your branch is based on 'topicbase', but the upstream is gone.
# (use "git branch --unset-upstream" to fixup)
...
$ git status -b -s
## topic...topicbase [gone]
...
In order to do like that, we need to distinguish these three cases
(i.e. no tracking, with configured but no longer valid tracking, and
with tracking) in function stat_tracking_info(). So the refactored
function stat_tracking_info() has three return values: -1 (with "gone"
base), 0 (no base), and 1 (with base).
If the caller does not like to report tracking info when nothing
changed between the branch and its upstream, simply checks if
num_theirs and num_ours are both 0.
[1]: http://thread.gmane.org/gmane.comp.version-control.git/231830/focus=232288
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-26 11:02:48 +04:00
|
|
|
return -1;
|
2009-05-11 18:42:54 +04:00
|
|
|
theirs = lookup_commit_reference(sha1);
|
2008-07-02 11:51:18 +04:00
|
|
|
if (!theirs)
|
branch: report invalid tracking branch as gone
Command "git branch -vv" will report tracking branches, but invalid
tracking branches are also reported. This is because the function
stat_tracking_info() can not distinguish invalid tracking branch
from other cases which it would not like to report, such as
there is no upstream settings at all, or nothing is changed between
one branch and its upstream.
Junio suggested missing upstream should be reported [1] like:
$ git branch -v -v
master e67ac84 initial
* topic 3fc0f2a [topicbase: gone] topic
$ git status
# On branch topic
# Your branch is based on 'topicbase', but the upstream is gone.
# (use "git branch --unset-upstream" to fixup)
...
$ git status -b -s
## topic...topicbase [gone]
...
In order to do like that, we need to distinguish these three cases
(i.e. no tracking, with configured but no longer valid tracking, and
with tracking) in function stat_tracking_info(). So the refactored
function stat_tracking_info() has three return values: -1 (with "gone"
base), 0 (no base), and 1 (with base).
If the caller does not like to report tracking info when nothing
changed between the branch and its upstream, simply checks if
num_theirs and num_ours are both 0.
[1]: http://thread.gmane.org/gmane.comp.version-control.git/231830/focus=232288
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-26 11:02:48 +04:00
|
|
|
return -1;
|
2008-07-02 11:51:18 +04:00
|
|
|
|
2011-11-13 14:22:14 +04:00
|
|
|
if (read_ref(branch->refname, sha1))
|
branch: report invalid tracking branch as gone
Command "git branch -vv" will report tracking branches, but invalid
tracking branches are also reported. This is because the function
stat_tracking_info() can not distinguish invalid tracking branch
from other cases which it would not like to report, such as
there is no upstream settings at all, or nothing is changed between
one branch and its upstream.
Junio suggested missing upstream should be reported [1] like:
$ git branch -v -v
master e67ac84 initial
* topic 3fc0f2a [topicbase: gone] topic
$ git status
# On branch topic
# Your branch is based on 'topicbase', but the upstream is gone.
# (use "git branch --unset-upstream" to fixup)
...
$ git status -b -s
## topic...topicbase [gone]
...
In order to do like that, we need to distinguish these three cases
(i.e. no tracking, with configured but no longer valid tracking, and
with tracking) in function stat_tracking_info(). So the refactored
function stat_tracking_info() has three return values: -1 (with "gone"
base), 0 (no base), and 1 (with base).
If the caller does not like to report tracking info when nothing
changed between the branch and its upstream, simply checks if
num_theirs and num_ours are both 0.
[1]: http://thread.gmane.org/gmane.comp.version-control.git/231830/focus=232288
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-26 11:02:48 +04:00
|
|
|
return -1;
|
2009-05-11 18:42:54 +04:00
|
|
|
ours = lookup_commit_reference(sha1);
|
2008-07-02 11:51:18 +04:00
|
|
|
if (!ours)
|
branch: report invalid tracking branch as gone
Command "git branch -vv" will report tracking branches, but invalid
tracking branches are also reported. This is because the function
stat_tracking_info() can not distinguish invalid tracking branch
from other cases which it would not like to report, such as
there is no upstream settings at all, or nothing is changed between
one branch and its upstream.
Junio suggested missing upstream should be reported [1] like:
$ git branch -v -v
master e67ac84 initial
* topic 3fc0f2a [topicbase: gone] topic
$ git status
# On branch topic
# Your branch is based on 'topicbase', but the upstream is gone.
# (use "git branch --unset-upstream" to fixup)
...
$ git status -b -s
## topic...topicbase [gone]
...
In order to do like that, we need to distinguish these three cases
(i.e. no tracking, with configured but no longer valid tracking, and
with tracking) in function stat_tracking_info(). So the refactored
function stat_tracking_info() has three return values: -1 (with "gone"
base), 0 (no base), and 1 (with base).
If the caller does not like to report tracking info when nothing
changed between the branch and its upstream, simply checks if
num_theirs and num_ours are both 0.
[1]: http://thread.gmane.org/gmane.comp.version-control.git/231830/focus=232288
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-26 11:02:48 +04:00
|
|
|
return -1;
|
2008-07-02 11:51:18 +04:00
|
|
|
|
|
|
|
/* are we the same? */
|
branch: report invalid tracking branch as gone
Command "git branch -vv" will report tracking branches, but invalid
tracking branches are also reported. This is because the function
stat_tracking_info() can not distinguish invalid tracking branch
from other cases which it would not like to report, such as
there is no upstream settings at all, or nothing is changed between
one branch and its upstream.
Junio suggested missing upstream should be reported [1] like:
$ git branch -v -v
master e67ac84 initial
* topic 3fc0f2a [topicbase: gone] topic
$ git status
# On branch topic
# Your branch is based on 'topicbase', but the upstream is gone.
# (use "git branch --unset-upstream" to fixup)
...
$ git status -b -s
## topic...topicbase [gone]
...
In order to do like that, we need to distinguish these three cases
(i.e. no tracking, with configured but no longer valid tracking, and
with tracking) in function stat_tracking_info(). So the refactored
function stat_tracking_info() has three return values: -1 (with "gone"
base), 0 (no base), and 1 (with base).
If the caller does not like to report tracking info when nothing
changed between the branch and its upstream, simply checks if
num_theirs and num_ours are both 0.
[1]: http://thread.gmane.org/gmane.comp.version-control.git/231830/focus=232288
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-26 11:02:48 +04:00
|
|
|
if (theirs == ours) {
|
|
|
|
*num_theirs = *num_ours = 0;
|
|
|
|
return 1;
|
|
|
|
}
|
2008-07-02 11:51:18 +04:00
|
|
|
|
2009-04-22 03:32:18 +04:00
|
|
|
/* Run "rev-list --left-right ours...theirs" internally... */
|
2008-07-02 11:51:18 +04:00
|
|
|
rev_argc = 0;
|
|
|
|
rev_argv[rev_argc++] = NULL;
|
|
|
|
rev_argv[rev_argc++] = "--left-right";
|
|
|
|
rev_argv[rev_argc++] = symmetric;
|
|
|
|
rev_argv[rev_argc++] = "--";
|
|
|
|
rev_argv[rev_argc] = NULL;
|
|
|
|
|
|
|
|
strcpy(symmetric, sha1_to_hex(ours->object.sha1));
|
|
|
|
strcpy(symmetric + 40, "...");
|
|
|
|
strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1));
|
|
|
|
|
|
|
|
init_revisions(&revs, NULL);
|
|
|
|
setup_revisions(rev_argc, rev_argv, &revs, NULL);
|
2014-08-11 01:33:26 +04:00
|
|
|
if (prepare_revision_walk(&revs))
|
|
|
|
die("revision walk setup failed");
|
2008-07-02 11:51:18 +04:00
|
|
|
|
|
|
|
/* ... and count the commits on each side. */
|
|
|
|
*num_ours = 0;
|
|
|
|
*num_theirs = 0;
|
|
|
|
while (1) {
|
|
|
|
struct commit *c = get_revision(&revs);
|
|
|
|
if (!c)
|
|
|
|
break;
|
|
|
|
if (c->object.flags & SYMMETRIC_LEFT)
|
|
|
|
(*num_ours)++;
|
|
|
|
else
|
|
|
|
(*num_theirs)++;
|
|
|
|
}
|
2008-07-03 23:09:48 +04:00
|
|
|
|
|
|
|
/* clear object flags smudged by the above traversal */
|
|
|
|
clear_commit_marks(ours, ALL_REV_FLAGS);
|
|
|
|
clear_commit_marks(theirs, ALL_REV_FLAGS);
|
2008-07-02 11:51:18 +04:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return true when there is anything to report, otherwise false.
|
|
|
|
*/
|
|
|
|
int format_tracking_info(struct branch *branch, struct strbuf *sb)
|
|
|
|
{
|
branch: report invalid tracking branch as gone
Command "git branch -vv" will report tracking branches, but invalid
tracking branches are also reported. This is because the function
stat_tracking_info() can not distinguish invalid tracking branch
from other cases which it would not like to report, such as
there is no upstream settings at all, or nothing is changed between
one branch and its upstream.
Junio suggested missing upstream should be reported [1] like:
$ git branch -v -v
master e67ac84 initial
* topic 3fc0f2a [topicbase: gone] topic
$ git status
# On branch topic
# Your branch is based on 'topicbase', but the upstream is gone.
# (use "git branch --unset-upstream" to fixup)
...
$ git status -b -s
## topic...topicbase [gone]
...
In order to do like that, we need to distinguish these three cases
(i.e. no tracking, with configured but no longer valid tracking, and
with tracking) in function stat_tracking_info(). So the refactored
function stat_tracking_info() has three return values: -1 (with "gone"
base), 0 (no base), and 1 (with base).
If the caller does not like to report tracking info when nothing
changed between the branch and its upstream, simply checks if
num_theirs and num_ours are both 0.
[1]: http://thread.gmane.org/gmane.comp.version-control.git/231830/focus=232288
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-26 11:02:48 +04:00
|
|
|
int ours, theirs;
|
2014-08-10 23:43:33 +04:00
|
|
|
char *base;
|
branch: report invalid tracking branch as gone
Command "git branch -vv" will report tracking branches, but invalid
tracking branches are also reported. This is because the function
stat_tracking_info() can not distinguish invalid tracking branch
from other cases which it would not like to report, such as
there is no upstream settings at all, or nothing is changed between
one branch and its upstream.
Junio suggested missing upstream should be reported [1] like:
$ git branch -v -v
master e67ac84 initial
* topic 3fc0f2a [topicbase: gone] topic
$ git status
# On branch topic
# Your branch is based on 'topicbase', but the upstream is gone.
# (use "git branch --unset-upstream" to fixup)
...
$ git status -b -s
## topic...topicbase [gone]
...
In order to do like that, we need to distinguish these three cases
(i.e. no tracking, with configured but no longer valid tracking, and
with tracking) in function stat_tracking_info(). So the refactored
function stat_tracking_info() has three return values: -1 (with "gone"
base), 0 (no base), and 1 (with base).
If the caller does not like to report tracking info when nothing
changed between the branch and its upstream, simply checks if
num_theirs and num_ours are both 0.
[1]: http://thread.gmane.org/gmane.comp.version-control.git/231830/focus=232288
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-26 11:02:48 +04:00
|
|
|
int upstream_is_gone = 0;
|
2008-07-02 11:51:18 +04:00
|
|
|
|
branch: report invalid tracking branch as gone
Command "git branch -vv" will report tracking branches, but invalid
tracking branches are also reported. This is because the function
stat_tracking_info() can not distinguish invalid tracking branch
from other cases which it would not like to report, such as
there is no upstream settings at all, or nothing is changed between
one branch and its upstream.
Junio suggested missing upstream should be reported [1] like:
$ git branch -v -v
master e67ac84 initial
* topic 3fc0f2a [topicbase: gone] topic
$ git status
# On branch topic
# Your branch is based on 'topicbase', but the upstream is gone.
# (use "git branch --unset-upstream" to fixup)
...
$ git status -b -s
## topic...topicbase [gone]
...
In order to do like that, we need to distinguish these three cases
(i.e. no tracking, with configured but no longer valid tracking, and
with tracking) in function stat_tracking_info(). So the refactored
function stat_tracking_info() has three return values: -1 (with "gone"
base), 0 (no base), and 1 (with base).
If the caller does not like to report tracking info when nothing
changed between the branch and its upstream, simply checks if
num_theirs and num_ours are both 0.
[1]: http://thread.gmane.org/gmane.comp.version-control.git/231830/focus=232288
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-26 11:02:48 +04:00
|
|
|
switch (stat_tracking_info(branch, &ours, &theirs)) {
|
|
|
|
case 0:
|
|
|
|
/* no base */
|
2008-07-02 11:51:18 +04:00
|
|
|
return 0;
|
branch: report invalid tracking branch as gone
Command "git branch -vv" will report tracking branches, but invalid
tracking branches are also reported. This is because the function
stat_tracking_info() can not distinguish invalid tracking branch
from other cases which it would not like to report, such as
there is no upstream settings at all, or nothing is changed between
one branch and its upstream.
Junio suggested missing upstream should be reported [1] like:
$ git branch -v -v
master e67ac84 initial
* topic 3fc0f2a [topicbase: gone] topic
$ git status
# On branch topic
# Your branch is based on 'topicbase', but the upstream is gone.
# (use "git branch --unset-upstream" to fixup)
...
$ git status -b -s
## topic...topicbase [gone]
...
In order to do like that, we need to distinguish these three cases
(i.e. no tracking, with configured but no longer valid tracking, and
with tracking) in function stat_tracking_info(). So the refactored
function stat_tracking_info() has three return values: -1 (with "gone"
base), 0 (no base), and 1 (with base).
If the caller does not like to report tracking info when nothing
changed between the branch and its upstream, simply checks if
num_theirs and num_ours are both 0.
[1]: http://thread.gmane.org/gmane.comp.version-control.git/231830/focus=232288
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-26 11:02:48 +04:00
|
|
|
case -1:
|
|
|
|
/* with "gone" base */
|
|
|
|
upstream_is_gone = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* with base */
|
|
|
|
break;
|
|
|
|
}
|
2008-07-02 11:51:18 +04:00
|
|
|
|
2014-08-10 23:43:33 +04:00
|
|
|
base = shorten_unambiguous_ref(branch->merge[0]->dst, 0);
|
branch: report invalid tracking branch as gone
Command "git branch -vv" will report tracking branches, but invalid
tracking branches are also reported. This is because the function
stat_tracking_info() can not distinguish invalid tracking branch
from other cases which it would not like to report, such as
there is no upstream settings at all, or nothing is changed between
one branch and its upstream.
Junio suggested missing upstream should be reported [1] like:
$ git branch -v -v
master e67ac84 initial
* topic 3fc0f2a [topicbase: gone] topic
$ git status
# On branch topic
# Your branch is based on 'topicbase', but the upstream is gone.
# (use "git branch --unset-upstream" to fixup)
...
$ git status -b -s
## topic...topicbase [gone]
...
In order to do like that, we need to distinguish these three cases
(i.e. no tracking, with configured but no longer valid tracking, and
with tracking) in function stat_tracking_info(). So the refactored
function stat_tracking_info() has three return values: -1 (with "gone"
base), 0 (no base), and 1 (with base).
If the caller does not like to report tracking info when nothing
changed between the branch and its upstream, simply checks if
num_theirs and num_ours are both 0.
[1]: http://thread.gmane.org/gmane.comp.version-control.git/231830/focus=232288
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-26 11:02:48 +04:00
|
|
|
if (upstream_is_gone) {
|
|
|
|
strbuf_addf(sb,
|
|
|
|
_("Your branch is based on '%s', but the upstream is gone.\n"),
|
|
|
|
base);
|
|
|
|
if (advice_status_hints)
|
|
|
|
strbuf_addf(sb,
|
|
|
|
_(" (use \"git branch --unset-upstream\" to fixup)\n"));
|
2013-08-26 11:02:49 +04:00
|
|
|
} else if (!ours && !theirs) {
|
|
|
|
strbuf_addf(sb,
|
|
|
|
_("Your branch is up-to-date with '%s'.\n"),
|
|
|
|
base);
|
branch: report invalid tracking branch as gone
Command "git branch -vv" will report tracking branches, but invalid
tracking branches are also reported. This is because the function
stat_tracking_info() can not distinguish invalid tracking branch
from other cases which it would not like to report, such as
there is no upstream settings at all, or nothing is changed between
one branch and its upstream.
Junio suggested missing upstream should be reported [1] like:
$ git branch -v -v
master e67ac84 initial
* topic 3fc0f2a [topicbase: gone] topic
$ git status
# On branch topic
# Your branch is based on 'topicbase', but the upstream is gone.
# (use "git branch --unset-upstream" to fixup)
...
$ git status -b -s
## topic...topicbase [gone]
...
In order to do like that, we need to distinguish these three cases
(i.e. no tracking, with configured but no longer valid tracking, and
with tracking) in function stat_tracking_info(). So the refactored
function stat_tracking_info() has three return values: -1 (with "gone"
base), 0 (no base), and 1 (with base).
If the caller does not like to report tracking info when nothing
changed between the branch and its upstream, simply checks if
num_theirs and num_ours are both 0.
[1]: http://thread.gmane.org/gmane.comp.version-control.git/231830/focus=232288
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-26 11:02:48 +04:00
|
|
|
} else if (!theirs) {
|
2012-02-02 06:02:23 +04:00
|
|
|
strbuf_addf(sb,
|
|
|
|
Q_("Your branch is ahead of '%s' by %d commit.\n",
|
|
|
|
"Your branch is ahead of '%s' by %d commits.\n",
|
branch: report invalid tracking branch as gone
Command "git branch -vv" will report tracking branches, but invalid
tracking branches are also reported. This is because the function
stat_tracking_info() can not distinguish invalid tracking branch
from other cases which it would not like to report, such as
there is no upstream settings at all, or nothing is changed between
one branch and its upstream.
Junio suggested missing upstream should be reported [1] like:
$ git branch -v -v
master e67ac84 initial
* topic 3fc0f2a [topicbase: gone] topic
$ git status
# On branch topic
# Your branch is based on 'topicbase', but the upstream is gone.
# (use "git branch --unset-upstream" to fixup)
...
$ git status -b -s
## topic...topicbase [gone]
...
In order to do like that, we need to distinguish these three cases
(i.e. no tracking, with configured but no longer valid tracking, and
with tracking) in function stat_tracking_info(). So the refactored
function stat_tracking_info() has three return values: -1 (with "gone"
base), 0 (no base), and 1 (with base).
If the caller does not like to report tracking info when nothing
changed between the branch and its upstream, simply checks if
num_theirs and num_ours are both 0.
[1]: http://thread.gmane.org/gmane.comp.version-control.git/231830/focus=232288
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-26 11:02:48 +04:00
|
|
|
ours),
|
|
|
|
base, ours);
|
2012-12-03 10:16:57 +04:00
|
|
|
if (advice_status_hints)
|
|
|
|
strbuf_addf(sb,
|
|
|
|
_(" (use \"git push\" to publish your local commits)\n"));
|
branch: report invalid tracking branch as gone
Command "git branch -vv" will report tracking branches, but invalid
tracking branches are also reported. This is because the function
stat_tracking_info() can not distinguish invalid tracking branch
from other cases which it would not like to report, such as
there is no upstream settings at all, or nothing is changed between
one branch and its upstream.
Junio suggested missing upstream should be reported [1] like:
$ git branch -v -v
master e67ac84 initial
* topic 3fc0f2a [topicbase: gone] topic
$ git status
# On branch topic
# Your branch is based on 'topicbase', but the upstream is gone.
# (use "git branch --unset-upstream" to fixup)
...
$ git status -b -s
## topic...topicbase [gone]
...
In order to do like that, we need to distinguish these three cases
(i.e. no tracking, with configured but no longer valid tracking, and
with tracking) in function stat_tracking_info(). So the refactored
function stat_tracking_info() has three return values: -1 (with "gone"
base), 0 (no base), and 1 (with base).
If the caller does not like to report tracking info when nothing
changed between the branch and its upstream, simply checks if
num_theirs and num_ours are both 0.
[1]: http://thread.gmane.org/gmane.comp.version-control.git/231830/focus=232288
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-26 11:02:48 +04:00
|
|
|
} else if (!ours) {
|
2012-02-02 06:02:23 +04:00
|
|
|
strbuf_addf(sb,
|
|
|
|
Q_("Your branch is behind '%s' by %d commit, "
|
|
|
|
"and can be fast-forwarded.\n",
|
|
|
|
"Your branch is behind '%s' by %d commits, "
|
|
|
|
"and can be fast-forwarded.\n",
|
branch: report invalid tracking branch as gone
Command "git branch -vv" will report tracking branches, but invalid
tracking branches are also reported. This is because the function
stat_tracking_info() can not distinguish invalid tracking branch
from other cases which it would not like to report, such as
there is no upstream settings at all, or nothing is changed between
one branch and its upstream.
Junio suggested missing upstream should be reported [1] like:
$ git branch -v -v
master e67ac84 initial
* topic 3fc0f2a [topicbase: gone] topic
$ git status
# On branch topic
# Your branch is based on 'topicbase', but the upstream is gone.
# (use "git branch --unset-upstream" to fixup)
...
$ git status -b -s
## topic...topicbase [gone]
...
In order to do like that, we need to distinguish these three cases
(i.e. no tracking, with configured but no longer valid tracking, and
with tracking) in function stat_tracking_info(). So the refactored
function stat_tracking_info() has three return values: -1 (with "gone"
base), 0 (no base), and 1 (with base).
If the caller does not like to report tracking info when nothing
changed between the branch and its upstream, simply checks if
num_theirs and num_ours are both 0.
[1]: http://thread.gmane.org/gmane.comp.version-control.git/231830/focus=232288
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-26 11:02:48 +04:00
|
|
|
theirs),
|
|
|
|
base, theirs);
|
2012-12-03 10:16:57 +04:00
|
|
|
if (advice_status_hints)
|
|
|
|
strbuf_addf(sb,
|
|
|
|
_(" (use \"git pull\" to update your local branch)\n"));
|
2012-11-15 14:45:00 +04:00
|
|
|
} else {
|
2012-02-02 06:02:23 +04:00
|
|
|
strbuf_addf(sb,
|
|
|
|
Q_("Your branch and '%s' have diverged,\n"
|
|
|
|
"and have %d and %d different commit each, "
|
|
|
|
"respectively.\n",
|
|
|
|
"Your branch and '%s' have diverged,\n"
|
|
|
|
"and have %d and %d different commits each, "
|
|
|
|
"respectively.\n",
|
branch: report invalid tracking branch as gone
Command "git branch -vv" will report tracking branches, but invalid
tracking branches are also reported. This is because the function
stat_tracking_info() can not distinguish invalid tracking branch
from other cases which it would not like to report, such as
there is no upstream settings at all, or nothing is changed between
one branch and its upstream.
Junio suggested missing upstream should be reported [1] like:
$ git branch -v -v
master e67ac84 initial
* topic 3fc0f2a [topicbase: gone] topic
$ git status
# On branch topic
# Your branch is based on 'topicbase', but the upstream is gone.
# (use "git branch --unset-upstream" to fixup)
...
$ git status -b -s
## topic...topicbase [gone]
...
In order to do like that, we need to distinguish these three cases
(i.e. no tracking, with configured but no longer valid tracking, and
with tracking) in function stat_tracking_info(). So the refactored
function stat_tracking_info() has three return values: -1 (with "gone"
base), 0 (no base), and 1 (with base).
If the caller does not like to report tracking info when nothing
changed between the branch and its upstream, simply checks if
num_theirs and num_ours are both 0.
[1]: http://thread.gmane.org/gmane.comp.version-control.git/231830/focus=232288
Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiang Xin <worldhello.net@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-08-26 11:02:48 +04:00
|
|
|
theirs),
|
|
|
|
base, ours, theirs);
|
2012-12-03 10:16:57 +04:00
|
|
|
if (advice_status_hints)
|
|
|
|
strbuf_addf(sb,
|
|
|
|
_(" (use \"git pull\" to merge the remote branch into yours)\n"));
|
2012-11-15 14:45:00 +04:00
|
|
|
}
|
2014-08-10 23:43:33 +04:00
|
|
|
free(base);
|
2008-07-02 11:51:18 +04:00
|
|
|
return 1;
|
|
|
|
}
|
2009-02-25 11:32:11 +03:00
|
|
|
|
|
|
|
static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
|
|
|
|
{
|
|
|
|
struct ref ***local_tail = cb_data;
|
|
|
|
struct ref *ref;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
/* we already know it starts with refs/ to get here */
|
2011-09-16 01:10:25 +04:00
|
|
|
if (check_refname_format(refname + 5, 0))
|
2009-02-25 11:32:11 +03:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
len = strlen(refname) + 1;
|
|
|
|
ref = xcalloc(1, sizeof(*ref) + len);
|
|
|
|
hashcpy(ref->new_sha1, sha1);
|
|
|
|
memcpy(ref->name, refname, len);
|
|
|
|
**local_tail = ref;
|
|
|
|
*local_tail = &ref->next;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ref *get_local_heads(void)
|
|
|
|
{
|
2009-04-17 02:16:23 +04:00
|
|
|
struct ref *local_refs = NULL, **local_tail = &local_refs;
|
2009-02-25 11:32:11 +03:00
|
|
|
for_each_ref(one_local_ref, &local_tail);
|
|
|
|
return local_refs;
|
|
|
|
}
|
2009-02-25 11:32:13 +03:00
|
|
|
|
2009-02-27 22:10:05 +03:00
|
|
|
struct ref *guess_remote_head(const struct ref *head,
|
|
|
|
const struct ref *refs,
|
|
|
|
int all)
|
2009-02-25 11:32:13 +03:00
|
|
|
{
|
|
|
|
const struct ref *r;
|
2009-02-27 22:10:05 +03:00
|
|
|
struct ref *list = NULL;
|
|
|
|
struct ref **tail = &list;
|
2009-02-25 11:32:13 +03:00
|
|
|
|
2009-02-25 11:32:14 +03:00
|
|
|
if (!head)
|
2009-02-25 11:32:13 +03:00
|
|
|
return NULL;
|
|
|
|
|
2009-02-27 22:10:06 +03:00
|
|
|
/*
|
|
|
|
* Some transports support directly peeking at
|
|
|
|
* where HEAD points; if that is the case, then
|
|
|
|
* we don't have to guess.
|
|
|
|
*/
|
|
|
|
if (head->symref)
|
|
|
|
return copy_ref(find_ref_by_name(refs, head->symref));
|
|
|
|
|
2009-02-25 11:32:13 +03:00
|
|
|
/* If refs/heads/master could be right, it is. */
|
2009-02-27 22:10:05 +03:00
|
|
|
if (!all) {
|
|
|
|
r = find_ref_by_name(refs, "refs/heads/master");
|
|
|
|
if (r && !hashcmp(r->old_sha1, head->old_sha1))
|
|
|
|
return copy_ref(r);
|
|
|
|
}
|
2009-02-25 11:32:13 +03:00
|
|
|
|
|
|
|
/* Look for another ref that points there */
|
2009-02-27 22:10:05 +03:00
|
|
|
for (r = refs; r; r = r->next) {
|
2011-06-03 09:11:13 +04:00
|
|
|
if (r != head &&
|
2013-12-01 00:55:40 +04:00
|
|
|
starts_with(r->name, "refs/heads/") &&
|
2011-06-03 09:11:13 +04:00
|
|
|
!hashcmp(r->old_sha1, head->old_sha1)) {
|
2009-02-27 22:10:05 +03:00
|
|
|
*tail = copy_ref(r);
|
|
|
|
tail = &((*tail)->next);
|
|
|
|
if (!all)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-02-25 11:32:13 +03:00
|
|
|
|
2009-02-27 22:10:05 +03:00
|
|
|
return list;
|
2009-02-25 11:32:13 +03:00
|
|
|
}
|
2009-11-10 08:03:31 +03:00
|
|
|
|
|
|
|
struct stale_heads_info {
|
|
|
|
struct string_list *ref_names;
|
|
|
|
struct ref **stale_refs_tail;
|
2011-10-15 09:04:25 +04:00
|
|
|
struct refspec *refs;
|
|
|
|
int ref_count;
|
2009-11-10 08:03:31 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static int get_stale_heads_cb(const char *refname,
|
|
|
|
const unsigned char *sha1, int flags, void *cb_data)
|
|
|
|
{
|
|
|
|
struct stale_heads_info *info = cb_data;
|
2014-02-27 13:00:10 +04:00
|
|
|
struct string_list matches = STRING_LIST_INIT_DUP;
|
2011-10-15 09:04:25 +04:00
|
|
|
struct refspec query;
|
2014-02-27 13:00:10 +04:00
|
|
|
int i, stale = 1;
|
2011-10-15 09:04:25 +04:00
|
|
|
memset(&query, 0, sizeof(struct refspec));
|
|
|
|
query.dst = (char *)refname;
|
|
|
|
|
2014-02-27 13:00:10 +04:00
|
|
|
query_refspecs_multiple(info->refs, info->ref_count, &query, &matches);
|
|
|
|
if (matches.nr == 0)
|
|
|
|
goto clean_exit; /* No matches */
|
2011-10-15 09:04:25 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we did find a suitable refspec and it's not a symref and
|
|
|
|
* it's not in the list of refs that currently exist in that
|
2014-02-27 13:00:10 +04:00
|
|
|
* remote, we consider it to be stale. In order to deal with
|
|
|
|
* overlapping refspecs, we need to go over all of the
|
|
|
|
* matching refs.
|
2011-10-15 09:04:25 +04:00
|
|
|
*/
|
2014-02-27 13:00:10 +04:00
|
|
|
if (flags & REF_ISSYMREF)
|
|
|
|
goto clean_exit;
|
|
|
|
|
|
|
|
for (i = 0; stale && i < matches.nr; i++)
|
|
|
|
if (string_list_has_string(info->ref_names, matches.items[i].string))
|
|
|
|
stale = 0;
|
|
|
|
|
|
|
|
if (stale) {
|
2011-10-15 09:04:25 +04:00
|
|
|
struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
|
|
|
|
hashcpy(ref->new_sha1, sha1);
|
2009-11-10 08:03:31 +03:00
|
|
|
}
|
2011-10-15 09:04:25 +04:00
|
|
|
|
2014-02-27 13:00:10 +04:00
|
|
|
clean_exit:
|
|
|
|
string_list_clear(&matches, 0);
|
2009-11-10 08:03:31 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-10-15 09:04:25 +04:00
|
|
|
struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map)
|
2009-11-10 08:03:31 +03:00
|
|
|
{
|
|
|
|
struct ref *ref, *stale_refs = NULL;
|
2010-07-04 23:46:19 +04:00
|
|
|
struct string_list ref_names = STRING_LIST_INIT_NODUP;
|
2009-11-10 08:03:31 +03:00
|
|
|
struct stale_heads_info info;
|
|
|
|
info.ref_names = &ref_names;
|
|
|
|
info.stale_refs_tail = &stale_refs;
|
2011-10-15 09:04:25 +04:00
|
|
|
info.refs = refs;
|
|
|
|
info.ref_count = ref_count;
|
2009-11-10 08:03:31 +03:00
|
|
|
for (ref = fetch_map; ref; ref = ref->next)
|
2010-06-26 03:41:38 +04:00
|
|
|
string_list_append(&ref_names, ref->name);
|
2009-11-10 08:03:31 +03:00
|
|
|
sort_string_list(&ref_names);
|
|
|
|
for_each_ref(get_stale_heads_cb, &info);
|
|
|
|
string_list_clear(&ref_names, 0);
|
|
|
|
return stale_refs;
|
|
|
|
}
|
remote.c: add command line option parser for "--force-with-lease"
Update "git push" and "git send-pack" to parse this commnd line
option.
The intended sematics is:
* "--force-with-lease" alone, without specifying the details, will
protect _all_ remote refs that are going to be updated by
requiring their current value to be the same as some reasonable
default, unless otherwise specified;
* "--force-with-lease=refname", without specifying the expected
value, will protect that refname, if it is going to be updated,
by requiring its current value to be the same as some reasonable
default.
* "--force-with-lease=refname:value" will protect that refname, if
it is going to be updated, by requiring its current value to be
the same as the specified value; and
* "--no-force-with-lease" will cancel all the previous --force-with-lease on the
command line.
For now, "some reasonable default" is tentatively defined as "the
value of the remote-tracking branch we have for the ref of the
remote being updated", and it is an error if we do not have such a
remote-tracking branch. But this is known to be fragile, its use is
not yet recommended, and hopefully we will find more reasonable
default as we gain experience with this feature. The manual marks
the feature as experimental unless the expected value is specified
explicitly for this reason.
Because the command line options are parsed _before_ we know which
remote we are pushing to, there needs further processing to the
parsed data after we instantiate the transport object to:
* expand "refname" given by the user to a full refname to be
matched with the list of "struct ref" used in match_push_refs()
and set_ref_status_for_push(); and
* learning the actual local ref that is the remote-tracking branch
for the specified remote ref.
Further, some processing need to be deferred until we find the set
of remote refs and match_push_refs() returns in order to find the
ones that need to be checked after explicit ones have been processed
for "--force-with-lease" (no specific details).
These post-processing will be the topic of the next patch.
This option was originally called "cas" (for "compare and swap"),
the name which nobody liked because it was too technical. The
second attempt called it "lockref" (because it is conceptually like
pushing after taking a lock) but the word "lock" was hated because
it implied that it may reject push by others, which is not the way
this option works. This round calls it "force-with-lease". You
assume you took the lease on the ref when you fetched to decide what
the rebased history should be, and you can push back only if the
lease has not been broken.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-09 02:34:36 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Compare-and-swap
|
|
|
|
*/
|
|
|
|
void clear_cas_option(struct push_cas_option *cas)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < cas->nr; i++)
|
|
|
|
free(cas->entry[i].refname);
|
|
|
|
free(cas->entry);
|
|
|
|
memset(cas, 0, sizeof(*cas));
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct push_cas *add_cas_entry(struct push_cas_option *cas,
|
|
|
|
const char *refname,
|
|
|
|
size_t refnamelen)
|
|
|
|
{
|
|
|
|
struct push_cas *entry;
|
|
|
|
ALLOC_GROW(cas->entry, cas->nr + 1, cas->alloc);
|
|
|
|
entry = &cas->entry[cas->nr++];
|
|
|
|
memset(entry, 0, sizeof(*entry));
|
|
|
|
entry->refname = xmemdupz(refname, refnamelen);
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
int parse_push_cas_option(struct push_cas_option *cas, const char *arg, int unset)
|
|
|
|
{
|
|
|
|
const char *colon;
|
|
|
|
struct push_cas *entry;
|
|
|
|
|
|
|
|
if (unset) {
|
|
|
|
/* "--no-<option>" */
|
|
|
|
clear_cas_option(cas);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!arg) {
|
|
|
|
/* just "--<option>" */
|
|
|
|
cas->use_tracking_for_rest = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* "--<option>=refname" or "--<option>=refname:value" */
|
|
|
|
colon = strchrnul(arg, ':');
|
|
|
|
entry = add_cas_entry(cas, arg, colon - arg);
|
|
|
|
if (!*colon)
|
|
|
|
entry->use_tracking = 1;
|
|
|
|
else if (get_sha1(colon + 1, entry->expect))
|
|
|
|
return error("cannot parse expected object name '%s'", colon + 1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int parseopt_push_cas_option(const struct option *opt, const char *arg, int unset)
|
|
|
|
{
|
|
|
|
return parse_push_cas_option(opt->value, arg, unset);
|
|
|
|
}
|
2013-07-09 22:01:06 +04:00
|
|
|
|
|
|
|
int is_empty_cas(const struct push_cas_option *cas)
|
|
|
|
{
|
|
|
|
return !cas->use_tracking_for_rest && !cas->nr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Look at remote.fetch refspec and see if we have a remote
|
|
|
|
* tracking branch for the refname there. Fill its current
|
|
|
|
* value in sha1[].
|
|
|
|
* If we cannot do so, return negative to signal an error.
|
|
|
|
*/
|
|
|
|
static int remote_tracking(struct remote *remote, const char *refname,
|
|
|
|
unsigned char sha1[20])
|
|
|
|
{
|
|
|
|
char *dst;
|
|
|
|
|
|
|
|
dst = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);
|
|
|
|
if (!dst)
|
|
|
|
return -1; /* no tracking ref for refname at remote */
|
|
|
|
if (read_ref(dst, sha1))
|
|
|
|
return -1; /* we know what the tracking ref is but we cannot read it */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void apply_cas(struct push_cas_option *cas,
|
|
|
|
struct remote *remote,
|
|
|
|
struct ref *ref)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Find an explicit --<option>=<name>[:<value>] entry */
|
|
|
|
for (i = 0; i < cas->nr; i++) {
|
|
|
|
struct push_cas *entry = &cas->entry[i];
|
2014-01-14 07:16:07 +04:00
|
|
|
if (!refname_match(entry->refname, ref->name))
|
2013-07-09 22:01:06 +04:00
|
|
|
continue;
|
|
|
|
ref->expect_old_sha1 = 1;
|
|
|
|
if (!entry->use_tracking)
|
|
|
|
hashcpy(ref->old_sha1_expect, cas->entry[i].expect);
|
|
|
|
else if (remote_tracking(remote, ref->name, ref->old_sha1_expect))
|
|
|
|
ref->expect_old_no_trackback = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Are we using "--<option>" to cover all? */
|
|
|
|
if (!cas->use_tracking_for_rest)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ref->expect_old_sha1 = 1;
|
|
|
|
if (remote_tracking(remote, ref->name, ref->old_sha1_expect))
|
|
|
|
ref->expect_old_no_trackback = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void apply_push_cas(struct push_cas_option *cas,
|
|
|
|
struct remote *remote,
|
|
|
|
struct ref *remote_refs)
|
|
|
|
{
|
|
|
|
struct ref *ref;
|
|
|
|
for (ref = remote_refs; ref; ref = ref->next)
|
|
|
|
apply_cas(cas, remote, ref);
|
|
|
|
}
|