2007-05-12 17:45:53 +02:00
|
|
|
#include "cache.h"
|
2017-06-14 20:07:36 +02:00
|
|
|
#include "config.h"
|
2007-05-12 17:45:53 +02:00
|
|
|
#include "remote.h"
|
|
|
|
#include "refs.h"
|
2018-05-17 00:57:48 +02:00
|
|
|
#include "refspec.h"
|
2018-05-16 01:42:15 +02:00
|
|
|
#include "object-store.h"
|
2008-07-02 09:51:18 +02:00
|
|
|
#include "commit.h"
|
|
|
|
#include "diff.h"
|
|
|
|
#include "revision.h"
|
2009-01-10 13:07:50 +01:00
|
|
|
#include "dir.h"
|
2009-02-25 09:32:12 +01:00
|
|
|
#include "tag.h"
|
2009-10-25 22:28:11 +01:00
|
|
|
#include "string-list.h"
|
2012-05-22 00:19:28 +02:00
|
|
|
#include "mergesort.h"
|
2020-07-28 22:23:39 +02:00
|
|
|
#include "strvec.h"
|
2018-07-20 18:33:04 +02:00
|
|
|
#include "commit-reach.h"
|
push: add an advice on unqualified <dst> push
Add an advice to the recently improved error message added in
f8aae12034 ("push: allow unqualified dest refspecs to DWIM",
2008-04-23).
Now with advice.pushUnqualifiedRefName=true (on by default) we show a
hint about how to proceed:
$ ./git-push avar v2.19.0^{commit}:newbranch -n
error: The destination you provided is not a full refname (i.e.,
starting with "refs/"). We tried to guess what you meant by:
- Looking for a ref that matches 'newbranch' on the remote side.
- Checking if the <src> being pushed ('v2.19.0^{commit}')
is a ref in "refs/{heads,tags}/". If so we add a corresponding
refs/{heads,tags}/ prefix on the remote side.
Neither worked, so we gave up. You must fully qualify the ref.
hint: The <src> part of the refspec is a commit object.
hint: Did you mean to create a new branch by pushing to
hint: 'v2.19.0^{commit}:refs/heads/newbranch'?
error: failed to push some refs to 'git@github.com:avar/git.git'
When trying to push a tag, tree or a blob we suggest that perhaps the
user meant to push them to refs/tags/ instead.
The if/else duplication for all of OBJ_{COMMIT,TAG,TREE,BLOB} is
unfortunate, but is required to correctly mark the messages for
translation. See the discussion in
<87r2gxebsi.fsf@evledraar.gmail.com> about that.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-11-13 20:52:43 +01:00
|
|
|
#include "advice.h"
|
2007-05-12 17:45:53 +02:00
|
|
|
|
2012-02-22 23:43:41 +01:00
|
|
|
enum map_direction { FROM_SRC, FROM_DST };
|
|
|
|
|
2008-02-25 07:25:04 +01:00
|
|
|
struct counted_string {
|
|
|
|
size_t len;
|
|
|
|
const char *s;
|
|
|
|
};
|
2008-02-20 19:43:53 +01:00
|
|
|
struct rewrite {
|
|
|
|
const char *base;
|
2008-02-25 07:25:04 +01:00
|
|
|
size_t baselen;
|
|
|
|
struct counted_string *instead_of;
|
2008-02-20 19:43:53 +01:00
|
|
|
int instead_of_nr;
|
|
|
|
int instead_of_alloc;
|
|
|
|
};
|
2009-09-07 10:56:00 +02:00
|
|
|
struct rewrites {
|
|
|
|
struct rewrite **rewrite;
|
|
|
|
int rewrite_alloc;
|
|
|
|
int rewrite_nr;
|
|
|
|
};
|
2008-02-20 19:43:53 +01:00
|
|
|
|
2007-05-12 17:45:53 +02:00
|
|
|
static struct remote **remotes;
|
2008-02-19 05:41:41 +01:00
|
|
|
static int remotes_alloc;
|
|
|
|
static int remotes_nr;
|
2014-07-29 16:43:39 +02:00
|
|
|
static struct hashmap remotes_hash;
|
2007-05-12 17:45:53 +02:00
|
|
|
|
2007-09-11 05:02:56 +02:00
|
|
|
static struct branch **branches;
|
2008-02-19 05:41:41 +01:00
|
|
|
static int branches_alloc;
|
|
|
|
static int branches_nr;
|
2007-09-11 05:02:56 +02:00
|
|
|
|
|
|
|
static struct branch *current_branch;
|
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 09:40:32 +02:00
|
|
|
static const char *pushremote_name;
|
2007-09-11 05:02:56 +02:00
|
|
|
|
2009-09-07 10:56:00 +02:00
|
|
|
static struct rewrites rewrites;
|
2009-09-07 10:56:33 +02:00
|
|
|
static struct rewrites rewrites_push;
|
2008-02-20 19:43:53 +01:00
|
|
|
|
2009-11-18 02:42:23 +01:00
|
|
|
static int valid_remote(const struct remote *remote)
|
|
|
|
{
|
2009-11-18 02:42:25 +01:00
|
|
|
return (!!remote->url) || (!!remote->foreign_vcs);
|
2009-11-18 02:42:23 +01:00
|
|
|
}
|
|
|
|
|
2009-09-07 10:56:00 +02:00
|
|
|
static const char *alias_url(const char *url, struct rewrites *r)
|
2008-02-20 19:43:53 +01:00
|
|
|
{
|
|
|
|
int i, j;
|
2008-02-25 07:25:04 +01:00
|
|
|
struct counted_string *longest;
|
|
|
|
int longest_i;
|
|
|
|
|
|
|
|
longest = NULL;
|
|
|
|
longest_i = -1;
|
2009-09-07 10:56:00 +02:00
|
|
|
for (i = 0; i < r->rewrite_nr; i++) {
|
|
|
|
if (!r->rewrite[i])
|
2008-02-20 19:43:53 +01:00
|
|
|
continue;
|
2009-09-07 10:56:00 +02:00
|
|
|
for (j = 0; j < r->rewrite[i]->instead_of_nr; j++) {
|
2013-11-30 21:55:40 +01:00
|
|
|
if (starts_with(url, r->rewrite[i]->instead_of[j].s) &&
|
2008-02-25 07:25:04 +01:00
|
|
|
(!longest ||
|
2009-09-07 10:56:00 +02:00
|
|
|
longest->len < r->rewrite[i]->instead_of[j].len)) {
|
|
|
|
longest = &(r->rewrite[i]->instead_of[j]);
|
2008-02-25 07:25:04 +01:00
|
|
|
longest_i = i;
|
2008-02-20 19:43:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-02-25 07:25:04 +01:00
|
|
|
if (!longest)
|
|
|
|
return url;
|
|
|
|
|
2015-09-24 23:07:03 +02:00
|
|
|
return xstrfmt("%s%s", r->rewrite[longest_i]->base, url + longest->len);
|
2008-02-20 19:43:53 +01:00
|
|
|
}
|
|
|
|
|
2007-09-19 06:49:27 +02:00
|
|
|
static void add_url(struct remote *remote, const char *url)
|
2007-05-12 17:45:53 +02:00
|
|
|
{
|
2008-02-19 05:41:41 +01:00
|
|
|
ALLOC_GROW(remote->url, remote->url_nr + 1, remote->url_alloc);
|
|
|
|
remote->url[remote->url_nr++] = url;
|
2007-05-12 17:45:53 +02:00
|
|
|
}
|
|
|
|
|
2009-06-09 18:01:34 +02: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 10:56:33 +02: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 16:43:39 +02:00
|
|
|
struct remotes_hash_key {
|
|
|
|
const char *str;
|
|
|
|
int len;
|
|
|
|
};
|
|
|
|
|
2017-06-30 21:14:05 +02:00
|
|
|
static int remotes_hash_cmp(const void *unused_cmp_data,
|
2019-10-07 01:30:37 +02:00
|
|
|
const struct hashmap_entry *eptr,
|
|
|
|
const struct hashmap_entry *entry_or_key,
|
2017-07-01 02:28:35 +02:00
|
|
|
const void *keydata)
|
2014-07-29 16:43:39 +02:00
|
|
|
{
|
2019-10-07 01:30:37 +02:00
|
|
|
const struct remote *a, *b;
|
2017-07-01 02:28:35 +02:00
|
|
|
const struct remotes_hash_key *key = keydata;
|
|
|
|
|
2019-10-07 01:30:37 +02:00
|
|
|
a = container_of(eptr, const struct remote, ent);
|
|
|
|
b = container_of(entry_or_key, const struct remote, ent);
|
|
|
|
|
2014-07-29 16:43:39 +02:00
|
|
|
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)
|
2017-07-01 02:28:35 +02:00
|
|
|
hashmap_init(&remotes_hash, remotes_hash_cmp, NULL, 0);
|
2014-07-29 16:43:39 +02:00
|
|
|
}
|
|
|
|
|
2007-05-12 17:45:53 +02:00
|
|
|
static struct remote *make_remote(const char *name, int len)
|
|
|
|
{
|
2014-07-29 16:43:39 +02:00
|
|
|
struct remote *ret, *replaced;
|
|
|
|
struct remotes_hash_key lookup;
|
2019-10-07 01:30:36 +02:00
|
|
|
struct hashmap_entry lookup_entry, *e;
|
2007-05-12 17:45:53 +02:00
|
|
|
|
2014-07-29 16:43:39 +02:00
|
|
|
if (!len)
|
|
|
|
len = strlen(name);
|
|
|
|
|
|
|
|
init_remotes_hash();
|
|
|
|
lookup.str = name;
|
|
|
|
lookup.len = len;
|
|
|
|
hashmap_entry_init(&lookup_entry, memhash(name, len));
|
|
|
|
|
2019-10-07 01:30:36 +02:00
|
|
|
e = hashmap_get(&remotes_hash, &lookup_entry, &lookup);
|
|
|
|
if (e)
|
|
|
|
return container_of(e, struct remote, ent);
|
2007-05-12 17:45:53 +02:00
|
|
|
|
2008-02-19 05:41:41 +01:00
|
|
|
ret = xcalloc(1, sizeof(struct remote));
|
2013-07-13 11:36:24 +02:00
|
|
|
ret->prune = -1; /* unspecified */
|
fetch: add a --prune-tags option and fetch.pruneTags config
Add a --prune-tags option to git-fetch, along with fetch.pruneTags
config option and a -P shorthand (-p is --prune). This allows for
doing any of:
git fetch -p -P
git fetch --prune --prune-tags
git fetch -p -P origin
git fetch --prune --prune-tags origin
Or simply:
git config fetch.prune true &&
git config fetch.pruneTags true &&
git fetch
Instead of the much more verbose:
git fetch --prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'
Before this feature it was painful to support the use-case of pulling
from a repo which is having both its branches *and* tags deleted
regularly, and have our local references to reflect upstream.
At work we create deployment tags in the repo for each rollout, and
there's *lots* of those, so they're archived within weeks for
performance reasons.
Without this change it's hard to centrally configure such repos in
/etc/gitconfig (on servers that are only used for working with
them). You need to set fetch.prune=true globally, and then for each
repo:
git -C {} config --replace-all remote.origin.fetch "refs/tags/*:refs/tags/*" "^\+*refs/tags/\*:refs/tags/\*$"
Now I can simply set fetch.pruneTags=true in /etc/gitconfig as well,
and users running "git pull" will automatically get the pruning
semantics I want.
Even though "git remote" has corresponding "prune" and "update
--prune" subcommands I'm intentionally not adding a corresponding
prune-tags or "update --prune --prune-tags" mode to that command.
It's advertised (as noted in my recent "git remote doc: correct
dangerous lies about what prune does") as only modifying remote
tracking references, whereas any --prune-tags option is always going
to modify what from the user's perspective is a local copy of the tag,
since there's no such thing as a remote tracking tag.
Ideally add_prune_tags_to_fetch_refspec() would be something that
would use ALLOC_GROW() to grow the 'fetch` member of the 'remote'
struct. Instead I'm realloc-ing remote->fetch and adding the
tag_refspec to the end.
The reason is that parse_{fetch,push}_refspec which allocate the
refspec (ultimately remote->fetch) struct are called many places that
don't have access to a 'remote' struct. It would be hard to change all
their callsites to be amenable to carry around the bookkeeping
variables required for dynamic allocation.
All the other callers of the API first incrementally construct the
string version of the refspec in remote->fetch_refspec via
add_fetch_refspec(), before finally calling parse_fetch_refspec() via
some variation of remote_get().
It's less of a pain to deal with the one special case that needs to
modify already constructed refspecs than to chase down and change all
the other callsites. The API I'm adding is intentionally not
generalized because if we add more of these we'd probably want to
re-visit how this is done.
See my "Re: [BUG] git remote prune removes local tags, depending on
fetch config" (87po6ahx87.fsf@evledraar.gmail.com;
https://public-inbox.org/git/87po6ahx87.fsf@evledraar.gmail.com/) for
more background info.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-02-09 21:32:15 +01:00
|
|
|
ret->prune_tags = -1; /* unspecified */
|
2018-05-17 00:58:00 +02:00
|
|
|
ret->name = xstrndup(name, len);
|
|
|
|
refspec_init(&ret->push, REFSPEC_PUSH);
|
2018-05-17 00:58:01 +02:00
|
|
|
refspec_init(&ret->fetch, REFSPEC_FETCH);
|
2018-05-17 00:58:00 +02:00
|
|
|
|
2008-02-19 05:41:41 +01:00
|
|
|
ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc);
|
|
|
|
remotes[remotes_nr++] = ret;
|
2014-07-29 16:43:39 +02:00
|
|
|
|
2019-10-07 01:30:27 +02:00
|
|
|
hashmap_entry_init(&ret->ent, lookup_entry.hash);
|
2019-10-07 01:30:42 +02:00
|
|
|
replaced = hashmap_put_entry(&remotes_hash, ret, ent);
|
2014-07-29 16:43:39 +02:00
|
|
|
assert(replaced == NULL); /* no previous entry overwritten */
|
2008-02-19 05:41:41 +01:00
|
|
|
return ret;
|
2007-05-12 17:45:53 +02:00
|
|
|
}
|
|
|
|
|
2007-09-11 05:02:56 +02:00
|
|
|
static void add_merge(struct branch *branch, const char *name)
|
|
|
|
{
|
2008-02-19 05:41:41 +01:00
|
|
|
ALLOC_GROW(branch->merge_name, branch->merge_nr + 1,
|
|
|
|
branch->merge_alloc);
|
|
|
|
branch->merge_name[branch->merge_nr++] = name;
|
2007-09-11 05:02:56 +02:00
|
|
|
}
|
|
|
|
|
2020-04-10 21:43:41 +02:00
|
|
|
static struct branch *make_branch(const char *name, size_t len)
|
2007-09-11 05:02:56 +02:00
|
|
|
{
|
2008-02-19 05:41:41 +01:00
|
|
|
struct branch *ret;
|
|
|
|
int i;
|
2007-09-11 05:02:56 +02:00
|
|
|
|
2008-02-19 05:41:41 +01:00
|
|
|
for (i = 0; i < branches_nr; i++) {
|
2020-04-10 21:43:41 +02:00
|
|
|
if (!strncmp(name, branches[i]->name, len) &&
|
|
|
|
!branches[i]->name[len])
|
2008-02-19 05:41:41 +01:00
|
|
|
return branches[i];
|
2007-09-11 05:02:56 +02:00
|
|
|
}
|
|
|
|
|
2008-02-19 05:41:41 +01:00
|
|
|
ALLOC_GROW(branches, branches_nr + 1, branches_alloc);
|
|
|
|
ret = xcalloc(1, sizeof(struct branch));
|
|
|
|
branches[branches_nr++] = ret;
|
2020-04-10 21:43:41 +02:00
|
|
|
ret->name = xstrndup(name, len);
|
2014-06-18 22:02:13 +02:00
|
|
|
ret->refname = xstrfmt("refs/heads/%s", ret->name);
|
2007-09-11 05:02:56 +02:00
|
|
|
|
2008-02-19 05:41:41 +01:00
|
|
|
return ret;
|
2007-09-11 05:02:56 +02:00
|
|
|
}
|
|
|
|
|
2020-04-10 21:43:41 +02:00
|
|
|
static struct rewrite *make_rewrite(struct rewrites *r,
|
|
|
|
const char *base, size_t len)
|
2008-02-20 19:43:53 +01:00
|
|
|
{
|
|
|
|
struct rewrite *ret;
|
|
|
|
int i;
|
|
|
|
|
2009-09-07 10:56:00 +02:00
|
|
|
for (i = 0; i < r->rewrite_nr; i++) {
|
2020-04-10 21:43:41 +02:00
|
|
|
if (len == r->rewrite[i]->baselen &&
|
|
|
|
!strncmp(base, r->rewrite[i]->base, len))
|
2009-09-07 10:56:00 +02:00
|
|
|
return r->rewrite[i];
|
2008-02-20 19:43:53 +01:00
|
|
|
}
|
|
|
|
|
2009-09-07 10:56:00 +02:00
|
|
|
ALLOC_GROW(r->rewrite, r->rewrite_nr + 1, r->rewrite_alloc);
|
2008-02-20 19:43:53 +01:00
|
|
|
ret = xcalloc(1, sizeof(struct rewrite));
|
2009-09-07 10:56:00 +02:00
|
|
|
r->rewrite[r->rewrite_nr++] = ret;
|
2020-04-10 21:43:41 +02:00
|
|
|
ret->base = xstrndup(base, len);
|
|
|
|
ret->baselen = len;
|
2008-02-20 19:43:53 +01: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 07:25:04 +01: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 19:43:53 +01:00
|
|
|
}
|
|
|
|
|
2015-09-24 23:07:20 +02:00
|
|
|
static const char *skip_spaces(const char *s)
|
|
|
|
{
|
|
|
|
while (isspace(*s))
|
|
|
|
s++;
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2007-05-12 17:45:53 +02:00
|
|
|
static void read_remotes_file(struct remote *remote)
|
|
|
|
{
|
2015-09-24 23:07:20 +02:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
2017-05-03 12:16:50 +02:00
|
|
|
FILE *f = fopen_or_warn(git_path("remotes/%s", remote->name), "r");
|
2007-05-12 17:45:53 +02:00
|
|
|
|
|
|
|
if (!f)
|
|
|
|
return;
|
2017-01-19 22:20:02 +01:00
|
|
|
remote->configured_in_repo = 1;
|
2008-11-10 21:43:00 +01:00
|
|
|
remote->origin = REMOTE_REMOTES;
|
2015-10-28 21:27:33 +01:00
|
|
|
while (strbuf_getline(&buf, f) != EOF) {
|
2015-09-24 23:07:20 +02:00
|
|
|
const char *v;
|
2007-05-12 17:45:53 +02:00
|
|
|
|
2015-09-24 23:07:20 +02:00
|
|
|
strbuf_rtrim(&buf);
|
2007-05-12 17:45:53 +02:00
|
|
|
|
2015-09-24 23:07:20 +02:00
|
|
|
if (skip_prefix(buf.buf, "URL:", &v))
|
|
|
|
add_url_alias(remote, xstrdup(skip_spaces(v)));
|
|
|
|
else if (skip_prefix(buf.buf, "Push:", &v))
|
2018-05-17 00:58:00 +02:00
|
|
|
refspec_append(&remote->push, skip_spaces(v));
|
2015-09-24 23:07:20 +02:00
|
|
|
else if (skip_prefix(buf.buf, "Pull:", &v))
|
2018-05-17 00:58:01 +02:00
|
|
|
refspec_append(&remote->fetch, skip_spaces(v));
|
2007-05-12 17:45:53 +02:00
|
|
|
}
|
2015-09-24 23:07:20 +02:00
|
|
|
strbuf_release(&buf);
|
2007-05-12 17:45:53 +02:00
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void read_branches_file(struct remote *remote)
|
|
|
|
{
|
2007-09-11 05:02:56 +02:00
|
|
|
char *frag;
|
2015-09-24 23:07:18 +02:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
2017-05-03 12:16:50 +02:00
|
|
|
FILE *f = fopen_or_warn(git_path("branches/%s", remote->name), "r");
|
2007-05-12 17:45:53 +02:00
|
|
|
|
|
|
|
if (!f)
|
|
|
|
return;
|
2015-09-24 23:07:18 +02:00
|
|
|
|
2016-01-14 00:31:17 +01:00
|
|
|
strbuf_getline_lf(&buf, f);
|
2015-10-23 08:02:51 +02:00
|
|
|
fclose(f);
|
2015-09-24 23:07:18 +02:00
|
|
|
strbuf_trim(&buf);
|
|
|
|
if (!buf.len) {
|
|
|
|
strbuf_release(&buf);
|
2007-05-12 17:45:53 +02:00
|
|
|
return;
|
2015-09-24 23:07:18 +02:00
|
|
|
}
|
|
|
|
|
2017-01-19 22:20:02 +01:00
|
|
|
remote->configured_in_repo = 1;
|
2008-11-10 21:43:00 +01:00
|
|
|
remote->origin = REMOTE_BRANCHES;
|
2008-03-26 00:35:28 +01:00
|
|
|
|
|
|
|
/*
|
2013-06-22 09:58:12 +02:00
|
|
|
* The branches file would have URL and optionally
|
2020-06-24 16:46:35 +02:00
|
|
|
* #branch specified. The default (or specified) branch is
|
2015-09-24 23:07:18 +02:00
|
|
|
* fetched and stored in the local branch matching the
|
|
|
|
* remote name.
|
2008-03-26 00:35:28 +01:00
|
|
|
*/
|
2015-09-24 23:07:18 +02:00
|
|
|
frag = strchr(buf.buf, '#');
|
|
|
|
if (frag)
|
2007-09-11 05:02:56 +02:00
|
|
|
*(frag++) = '\0';
|
2015-09-24 23:07:18 +02:00
|
|
|
else
|
2020-06-24 16:46:35 +02:00
|
|
|
frag = (char *)git_default_branch_name();
|
2015-09-24 23:07:18 +02:00
|
|
|
|
|
|
|
add_url_alias(remote, strbuf_detach(&buf, NULL));
|
2018-05-17 00:58:01 +02:00
|
|
|
strbuf_addf(&buf, "refs/heads/%s:refs/heads/%s",
|
|
|
|
frag, remote->name);
|
|
|
|
refspec_append(&remote->fetch, buf.buf);
|
2013-06-22 09:58:12 +02:00
|
|
|
|
2008-11-10 22:47:11 +01:00
|
|
|
/*
|
|
|
|
* Cogito compatible push: push current HEAD to remote #branch
|
|
|
|
* (master if missing)
|
|
|
|
*/
|
2018-05-17 00:58:01 +02:00
|
|
|
strbuf_reset(&buf);
|
2018-05-17 00:58:00 +02:00
|
|
|
strbuf_addf(&buf, "HEAD:refs/heads/%s", frag);
|
|
|
|
refspec_append(&remote->push, buf.buf);
|
2007-09-11 05:03:08 +02:00
|
|
|
remote->fetch_tags = 1; /* always auto-follow */
|
2018-05-17 00:58:00 +02:00
|
|
|
strbuf_release(&buf);
|
2007-05-12 17:45:53 +02:00
|
|
|
}
|
|
|
|
|
2008-05-14 19:46:53 +02:00
|
|
|
static int handle_config(const char *key, const char *value, void *cb)
|
2007-05-12 17:45:53 +02:00
|
|
|
{
|
|
|
|
const char *name;
|
2020-04-10 21:44:28 +02:00
|
|
|
size_t namelen;
|
2007-05-12 17:45:53 +02:00
|
|
|
const char *subkey;
|
|
|
|
struct remote *remote;
|
2007-09-11 05:02:56 +02:00
|
|
|
struct branch *branch;
|
2016-02-16 10:47:49 +01:00
|
|
|
if (parse_config_key(key, "branch", &name, &namelen, &subkey) >= 0) {
|
|
|
|
if (!name)
|
2007-09-11 05:02:56 +02:00
|
|
|
return 0;
|
2016-02-16 10:47:49 +01:00
|
|
|
branch = make_branch(name, namelen);
|
|
|
|
if (!strcmp(subkey, "remote")) {
|
2015-05-02 00:44:41 +02:00
|
|
|
return git_config_string(&branch->remote_name, key, value);
|
2016-02-16 10:47:49 +01:00
|
|
|
} else if (!strcmp(subkey, "pushremote")) {
|
2015-05-21 06:45:20 +02:00
|
|
|
return git_config_string(&branch->pushremote_name, key, value);
|
2016-02-16 10:47:49 +01:00
|
|
|
} else if (!strcmp(subkey, "merge")) {
|
2008-02-11 20:00:10 +01:00
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(key);
|
2007-09-11 05:02:56 +02:00
|
|
|
add_merge(branch, xstrdup(value));
|
2008-02-11 20:00:10 +01:00
|
|
|
}
|
2007-09-11 05:02:56 +02:00
|
|
|
return 0;
|
2007-05-12 17:45:53 +02:00
|
|
|
}
|
2016-02-16 10:47:49 +01:00
|
|
|
if (parse_config_key(key, "url", &name, &namelen, &subkey) >= 0) {
|
2008-02-20 19:43:53 +01:00
|
|
|
struct rewrite *rewrite;
|
2016-02-16 10:47:49 +01:00
|
|
|
if (!name)
|
2008-02-20 19:43:53 +01:00
|
|
|
return 0;
|
2016-02-16 10:47:49 +01:00
|
|
|
if (!strcmp(subkey, "insteadof")) {
|
2009-09-07 10:56:33 +02:00
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(key);
|
2018-11-22 18:31:09 +01:00
|
|
|
rewrite = make_rewrite(&rewrites, name, namelen);
|
2009-09-07 10:56:33 +02:00
|
|
|
add_instead_of(rewrite, xstrdup(value));
|
2016-02-16 10:47:49 +01:00
|
|
|
} else if (!strcmp(subkey, "pushinsteadof")) {
|
2008-02-20 19:43:53 +01:00
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(key);
|
2018-11-22 18:31:09 +01:00
|
|
|
rewrite = make_rewrite(&rewrites_push, name, namelen);
|
2008-02-20 19:43:53 +01:00
|
|
|
add_instead_of(rewrite, xstrdup(value));
|
|
|
|
}
|
|
|
|
}
|
2013-04-02 09:40:33 +02:00
|
|
|
|
2016-02-16 10:47:49 +01:00
|
|
|
if (parse_config_key(key, "remote", &name, &namelen, &subkey) < 0)
|
2007-05-12 17:45:53 +02:00
|
|
|
return 0;
|
2013-04-02 09:40:33 +02:00
|
|
|
|
|
|
|
/* Handle remote.* variables */
|
2016-02-16 10:47:49 +01:00
|
|
|
if (!name && !strcmp(subkey, "pushdefault"))
|
2013-04-02 09:40:33 +02:00
|
|
|
return git_config_string(&pushremote_name, key, value);
|
|
|
|
|
2016-02-16 10:47:49 +01:00
|
|
|
if (!name)
|
|
|
|
return 0;
|
2013-04-02 09:40:33 +02:00
|
|
|
/* Handle remote.<name>.* variables */
|
2008-10-14 22:30:21 +02:00
|
|
|
if (*name == '/') {
|
2018-11-10 06:16:09 +01:00
|
|
|
warning(_("config remote shorthand cannot begin with '/': %s"),
|
2008-10-14 22:30:21 +02:00
|
|
|
name);
|
|
|
|
return 0;
|
|
|
|
}
|
2016-02-16 10:47:49 +01:00
|
|
|
remote = make_remote(name, namelen);
|
2008-11-10 21:43:00 +01:00
|
|
|
remote->origin = REMOTE_CONFIG;
|
2020-02-10 01:30:54 +01:00
|
|
|
if (current_config_scope() == CONFIG_SCOPE_LOCAL ||
|
|
|
|
current_config_scope() == CONFIG_SCOPE_WORKTREE)
|
2017-01-19 22:20:02 +01:00
|
|
|
remote->configured_in_repo = 1;
|
2016-02-16 10:47:49 +01:00
|
|
|
if (!strcmp(subkey, "mirror"))
|
2008-04-17 13:17:20 +02:00
|
|
|
remote->mirror = git_config_bool(key, value);
|
2016-02-16 10:47:49 +01:00
|
|
|
else if (!strcmp(subkey, "skipdefaultupdate"))
|
2008-04-17 13:17:20 +02:00
|
|
|
remote->skip_default_update = git_config_bool(key, value);
|
2016-02-16 10:47:49 +01:00
|
|
|
else if (!strcmp(subkey, "skipfetchall"))
|
2009-11-09 21:11:06 +01:00
|
|
|
remote->skip_default_update = git_config_bool(key, value);
|
2016-02-16 10:47:49 +01:00
|
|
|
else if (!strcmp(subkey, "prune"))
|
2013-07-13 11:36:24 +02:00
|
|
|
remote->prune = git_config_bool(key, value);
|
fetch: add a --prune-tags option and fetch.pruneTags config
Add a --prune-tags option to git-fetch, along with fetch.pruneTags
config option and a -P shorthand (-p is --prune). This allows for
doing any of:
git fetch -p -P
git fetch --prune --prune-tags
git fetch -p -P origin
git fetch --prune --prune-tags origin
Or simply:
git config fetch.prune true &&
git config fetch.pruneTags true &&
git fetch
Instead of the much more verbose:
git fetch --prune origin 'refs/tags/*:refs/tags/*' '+refs/heads/*:refs/remotes/origin/*'
Before this feature it was painful to support the use-case of pulling
from a repo which is having both its branches *and* tags deleted
regularly, and have our local references to reflect upstream.
At work we create deployment tags in the repo for each rollout, and
there's *lots* of those, so they're archived within weeks for
performance reasons.
Without this change it's hard to centrally configure such repos in
/etc/gitconfig (on servers that are only used for working with
them). You need to set fetch.prune=true globally, and then for each
repo:
git -C {} config --replace-all remote.origin.fetch "refs/tags/*:refs/tags/*" "^\+*refs/tags/\*:refs/tags/\*$"
Now I can simply set fetch.pruneTags=true in /etc/gitconfig as well,
and users running "git pull" will automatically get the pruning
semantics I want.
Even though "git remote" has corresponding "prune" and "update
--prune" subcommands I'm intentionally not adding a corresponding
prune-tags or "update --prune --prune-tags" mode to that command.
It's advertised (as noted in my recent "git remote doc: correct
dangerous lies about what prune does") as only modifying remote
tracking references, whereas any --prune-tags option is always going
to modify what from the user's perspective is a local copy of the tag,
since there's no such thing as a remote tracking tag.
Ideally add_prune_tags_to_fetch_refspec() would be something that
would use ALLOC_GROW() to grow the 'fetch` member of the 'remote'
struct. Instead I'm realloc-ing remote->fetch and adding the
tag_refspec to the end.
The reason is that parse_{fetch,push}_refspec which allocate the
refspec (ultimately remote->fetch) struct are called many places that
don't have access to a 'remote' struct. It would be hard to change all
their callsites to be amenable to carry around the bookkeeping
variables required for dynamic allocation.
All the other callers of the API first incrementally construct the
string version of the refspec in remote->fetch_refspec via
add_fetch_refspec(), before finally calling parse_fetch_refspec() via
some variation of remote_get().
It's less of a pain to deal with the one special case that needs to
modify already constructed refspecs than to chase down and change all
the other callsites. The API I'm adding is intentionally not
generalized because if we add more of these we'd probably want to
re-visit how this is done.
See my "Re: [BUG] git remote prune removes local tags, depending on
fetch config" (87po6ahx87.fsf@evledraar.gmail.com;
https://public-inbox.org/git/87po6ahx87.fsf@evledraar.gmail.com/) for
more background info.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-02-09 21:32:15 +01:00
|
|
|
else if (!strcmp(subkey, "prunetags"))
|
|
|
|
remote->prune_tags = git_config_bool(key, value);
|
2016-02-16 10:47:49 +01:00
|
|
|
else if (!strcmp(subkey, "url")) {
|
2008-04-17 13:17:20 +02:00
|
|
|
const char *v;
|
|
|
|
if (git_config_string(&v, key, value))
|
|
|
|
return -1;
|
|
|
|
add_url(remote, v);
|
2016-02-16 10:47:49 +01:00
|
|
|
} else if (!strcmp(subkey, "pushurl")) {
|
2009-06-09 18:01:34 +02:00
|
|
|
const char *v;
|
|
|
|
if (git_config_string(&v, key, value))
|
|
|
|
return -1;
|
|
|
|
add_pushurl(remote, v);
|
2016-02-16 10:47:49 +01:00
|
|
|
} else if (!strcmp(subkey, "push")) {
|
2008-04-17 13:17:20 +02:00
|
|
|
const char *v;
|
|
|
|
if (git_config_string(&v, key, value))
|
|
|
|
return -1;
|
2018-05-17 00:58:00 +02:00
|
|
|
refspec_append(&remote->push, v);
|
|
|
|
free((char *)v);
|
2016-02-16 10:47:49 +01:00
|
|
|
} else if (!strcmp(subkey, "fetch")) {
|
2008-04-17 13:17:20 +02:00
|
|
|
const char *v;
|
|
|
|
if (git_config_string(&v, key, value))
|
|
|
|
return -1;
|
2018-05-17 00:58:01 +02:00
|
|
|
refspec_append(&remote->fetch, v);
|
|
|
|
free((char *)v);
|
2016-02-16 10:47:49 +01:00
|
|
|
} else if (!strcmp(subkey, "receivepack")) {
|
2008-04-17 13:17:20 +02:00
|
|
|
const char *v;
|
|
|
|
if (git_config_string(&v, key, value))
|
|
|
|
return -1;
|
2007-05-12 17:45:53 +02:00
|
|
|
if (!remote->receivepack)
|
2008-04-17 13:17:20 +02:00
|
|
|
remote->receivepack = v;
|
2007-05-12 17:45:53 +02:00
|
|
|
else
|
2018-11-10 06:16:09 +01:00
|
|
|
error(_("more than one receivepack given, using the first"));
|
2016-02-16 10:47:49 +01:00
|
|
|
} else if (!strcmp(subkey, "uploadpack")) {
|
2008-04-17 13:17:20 +02:00
|
|
|
const char *v;
|
|
|
|
if (git_config_string(&v, key, value))
|
|
|
|
return -1;
|
2007-09-11 05:02:51 +02:00
|
|
|
if (!remote->uploadpack)
|
2008-04-17 13:17:20 +02:00
|
|
|
remote->uploadpack = v;
|
2007-09-11 05:02:51 +02:00
|
|
|
else
|
2018-11-10 06:16:09 +01:00
|
|
|
error(_("more than one uploadpack given, using the first"));
|
2016-02-16 10:47:49 +01:00
|
|
|
} else if (!strcmp(subkey, "tagopt")) {
|
2007-09-11 05:03:08 +02:00
|
|
|
if (!strcmp(value, "--no-tags"))
|
|
|
|
remote->fetch_tags = -1;
|
2010-04-20 01:31:25 +02:00
|
|
|
else if (!strcmp(value, "--tags"))
|
|
|
|
remote->fetch_tags = 2;
|
2016-02-16 10:47:49 +01:00
|
|
|
} else if (!strcmp(subkey, "proxy")) {
|
2008-04-17 13:17:20 +02:00
|
|
|
return git_config_string((const char **)&remote->http_proxy,
|
|
|
|
key, value);
|
2016-02-16 10:47:49 +01:00
|
|
|
} else if (!strcmp(subkey, "proxyauthmethod")) {
|
2016-01-26 14:02:47 +01:00
|
|
|
return git_config_string((const char **)&remote->http_proxy_authmethod,
|
|
|
|
key, value);
|
2016-02-16 10:47:49 +01:00
|
|
|
} else if (!strcmp(subkey, "vcs")) {
|
2009-11-18 02:42:25 +01:00
|
|
|
return git_config_string(&remote->foreign_vcs, key, value);
|
2008-04-17 13:17:20 +02:00
|
|
|
}
|
2007-05-12 17:45:53 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-02-20 19:43:53 +01:00
|
|
|
static void alias_all_urls(void)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
for (i = 0; i < remotes_nr; i++) {
|
2009-09-07 10:56:33 +02:00
|
|
|
int add_pushurl_aliases;
|
2008-02-20 19:43:53 +01:00
|
|
|
if (!remotes[i])
|
|
|
|
continue;
|
2009-06-09 18:01:34 +02:00
|
|
|
for (j = 0; j < remotes[i]->pushurl_nr; j++) {
|
2009-09-07 10:56:00 +02:00
|
|
|
remotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[j], &rewrites);
|
2009-06-09 18:01:34 +02:00
|
|
|
|