From: Junio C Hamano Date: Mon, 9 Sep 2013 21:27:11 +0000 (-0700) Subject: Merge branch 'ms/fetch-prune-configuration' X-Git-Tag: v1.8.5-rc0~174 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/a5e10f8bc1db418fc806abb7574bb1e468948d19?ds=inline;hp=-c Merge branch 'ms/fetch-prune-configuration' Allow fetch.prune and remote.*.prune configuration variables to be set, and "git fetch" to behave as if "--prune" is given. "git fetch" that honors remote.*.prune is fine, but I wonder if we should somehow make "git push" aware of it as well. Perhaps remote.*.prune should not be just a boolean, but a 4-way "none", "push", "fetch", "both"? * ms/fetch-prune-configuration: fetch: make --prune configurable --- a5e10f8bc1db418fc806abb7574bb1e468948d19 diff --combined Documentation/config.txt index ec57a15ac5,e4ce7c46f9..4a51e6a68d --- a/Documentation/config.txt +++ b/Documentation/config.txt @@@ -199,9 -199,6 +199,9 @@@ advice.*: amWorkDir:: Advice that shows the location of the patch file when linkgit:git-am[1] fails to apply it. + rmHints:: + In case of failure in the output of linkgit:git-rm[1], + show directions on how to proceed from the current state. -- core.fileMode:: @@@ -213,6 -210,17 +213,6 @@@ The default is true, except linkgit:git will probe and set core.fileMode false if appropriate when the repository is created. -core.ignoreCygwinFSTricks:: - This option is only used by Cygwin implementation of Git. If false, - the Cygwin stat() and lstat() functions are used. This may be useful - if your repository consists of a few separate directories joined in - one hierarchy using Cygwin mount. If true, Git uses native Win32 API - whenever it is possible and falls back to Cygwin functions only to - handle symbol links. The native mode is more than twice faster than - normal Cygwin l/stat() functions. True by default, unless core.filemode - is true, in which case ignoreCygwinFSTricks is ignored as Cygwin's - POSIX emulation is required to support core.filemode. - core.ignorecase:: If true, this option enables various workarounds to enable Git to work better on filesystems that are not case sensitive, @@@ -868,17 -876,16 +868,17 @@@ The values of these variables may be sp color.interactive:: When set to `always`, always use colors for interactive prompts - and displays (such as those used by "git-add --interactive"). - When false (or `never`), never. When set to `true` or `auto`, use - colors only when the output is to the terminal. Defaults to false. + and displays (such as those used by "git-add --interactive" and + "git-clean --interactive"). When false (or `never`), never. + When set to `true` or `auto`, use colors only when the output is + to the terminal. Defaults to false. color.interactive.:: - Use customized color for 'git add --interactive' - output. `` may be `prompt`, `header`, `help` or `error`, for - four distinct types of normal output from interactive - commands. The values of these variables may be specified as - in color.branch.. + Use customized color for 'git add --interactive' and 'git clean + --interactive' output. `` may be `prompt`, `header`, `help` + or `error`, for four distinct types of normal output from + interactive commands. The values of these variables may be + specified as in color.branch.. color.pager:: A boolean to enable/disable colored output when the pager is in @@@ -912,21 -919,17 +912,21 @@@ color.ui: as `color.diff` and `color.grep` that control the use of color per command family. Its scope will expand as more commands learn configuration to set a default for the `--color` option. Set it - to `always` if you want all output not intended for machine - consumption to use color, to `true` or `auto` if you want such - output to use color when written to the terminal, or to `false` or - `never` if you prefer Git commands not to use color unless enabled - explicitly with some other configuration or the `--color` option. + to `false` or `never` if you prefer Git commands not to use + color unless enabled explicitly with some other configuration + or the `--color` option. Set it to `always` if you want all + output not intended for machine consumption to use color, to + `true` or `auto` (this is the default since Git 1.8.4) if you + want such output to use color when written to the terminal. column.ui:: Specify whether supported commands should output in columns. This variable consists of a list of tokens separated by spaces or commas: + +These options control when the feature should be enabled +(defaults to 'never'): ++ -- `always`;; always show in columns @@@ -934,39 -937,24 +934,39 @@@ never show in columns `auto`;; show in columns if the output is to the terminal +-- ++ +These options control layout (defaults to 'column'). Setting any +of these implies 'always' if none of 'always', 'never', or 'auto' are +specified. ++ +-- `column`;; - fill columns before rows (default) + fill columns before rows `row`;; fill rows before columns `plain`;; show in one column +-- ++ +Finally, these options can be combined with a layout option (defaults +to 'nodense'): ++ +-- `dense`;; make unequal size columns to utilize more space `nodense`;; make equal size columns -- -+ -This option defaults to 'never'. column.branch:: Specify whether to output branch listing in `git branch` in columns. See `column.ui` for details. +column.clean:: + Specify the layout when list items in `git clean -i`, which always + shows files and directories in columns. See `column.ui` for details. + column.status:: Specify whether to output untracked files in `git status` in columns. See `column.ui` for details. @@@ -1061,6 -1049,10 +1061,10 @@@ fetch.unpackLimit: especially on slow filesystems. If not set, the value of `transfer.unpackLimit` is used instead. + fetch.prune:: + If true, fetch will automatically behave as if the `--prune` + option was given on the command line. See also `remote..prune`. + format.attach:: Enable multipart/mixed attachments as the default for 'format-patch'. The value can also be a double quoted string @@@ -1838,59 -1830,39 +1842,59 @@@ pull.twohead: The default merge strategy to use when pulling a single branch. push.default:: - Defines the action `git push` should take if no refspec is given - on the command line, no refspec is configured in the remote, and - no refspec is implied by any of the options given on the command - line. Possible values are: + Defines the action `git push` should take if no refspec is + explicitly given. Different values are well-suited for + specific workflows; for instance, in a purely central workflow + (i.e. the fetch source is equal to the push destination), + `upstream` is probably what you want. Possible values are: + -- -* `nothing` - do not push anything. -* `matching` - push all branches having the same name in both ends. - This is for those who prepare all the branches into a publishable - shape and then push them out with a single command. It is not - appropriate for pushing into a repository shared by multiple users, - since locally stalled branches will attempt a non-fast forward push - if other users updated the branch. - + - This is currently the default, but Git 2.0 will change the default - to `simple`. -* `upstream` - push the current branch to its upstream branch - (`tracking` is a deprecated synonym for this). - With this, `git push` will update the same remote ref as the one which - is merged by `git pull`, making `push` and `pull` symmetrical. - See "branch..merge" for how to configure the upstream branch. -* `simple` - like `upstream`, but refuses to push if the upstream - branch's name is different from the local one. This is the safest - option and is well-suited for beginners. It will become the default - in Git 2.0. -* `current` - push the current branch to a branch of the same name. --- + +* `nothing` - do not push anything (error out) unless a refspec is + explicitly given. This is primarily meant for people who want to + avoid mistakes by always being explicit. + +* `current` - push the current branch to update a branch with the same + name on the receiving end. Works in both central and non-central + workflows. + +* `upstream` - push the current branch back to the branch whose + changes are usually integrated into the current branch (which is + called `@{upstream}`). This mode only makes sense if you are + pushing to the same repository you would normally pull from + (i.e. central workflow). + +* `simple` - in centralized workflow, work like `upstream` with an + added safety to refuse to push if the upstream branch's name is + different from the local one. ++ +When pushing to a remote that is different from the remote you normally +pull from, work as `current`. This is the safest option and is suited +for beginners. ++ +This mode will become the default in Git 2.0. + +* `matching` - push all branches having the same name on both ends. + This makes the repository you are pushing to remember the set of + branches that will be pushed out (e.g. if you always push 'maint' + and 'master' there and no other branches, the repository you push + to will have these two branches, and your local 'maint' and + 'master' will be pushed there). ++ +To use this mode effectively, you have to make sure _all_ the +branches you would push out are ready to be pushed out before +running 'git push', as the whole point of this mode is to allow you +to push all of the branches in one go. If you usually finish work +on only one branch and push out the result, while other branches are +unfinished, this mode is not for you. Also this mode is not +suitable for pushing into a shared central repository, as other +people may add new branches there, or update the tip of existing +branches outside your control. + -The `simple`, `current` and `upstream` modes are for those who want to -push out a single branch after finishing work, even when the other -branches are not yet ready to be pushed out. If you are working with -other people to push into the same shared repository, you would want -to use one of these. +This is currently the default, but Git 2.0 will change the default +to `simple`. + +-- rebase.stat:: Whether to show a diffstat of what changed upstream since the last @@@ -1899,14 -1871,6 +1903,14 @@@ rebase.autosquash:: If set to true enable '--autosquash' option by default. +rebase.autostash:: + When set to true, automatically create a temporary stash + before the operation begins, and apply it after the operation + ends. This means that you can run rebase on a dirty worktree. + However, use with care: the final stash application after a + successful rebase might result in non-trivial conflicts. + Defaults to false. + receive.autogc:: By default, git-receive-pack will run "git-gc --auto" after receiving data from git-push and updating refs. You can stop @@@ -2024,6 -1988,12 +2028,12 @@@ remote..vcs: Setting this to a value will cause Git to interact with the remote with the git-remote- helper. + remote..prune:: + When set to true, fetching from this remote by default will also + remove any remote-tracking branches which no longer exist on the + remote (as if the `--prune` option was give on the command line). + Overrides `fetch.prune` settings, if any. + remotes.:: The list of remotes which are fetched by "git remote update ". See linkgit:git-remote[1]. @@@ -2062,10 -2032,6 +2072,10 @@@ sendemail.smtpencryption: sendemail.smtpssl:: Deprecated alias for 'sendemail.smtpencryption = ssl'. +sendemail.smtpsslcertpath:: + Path to ca-certificates (either a directory or a single file). + Set it to an empty string to disable certificate verification. + sendemail..*:: Identity-specific versions of the 'sendemail.*' parameters found below, taking precedence over those when the this @@@ -2110,14 -2076,6 +2120,14 @@@ status.relativePaths: relative to the repository root (this was the default for Git prior to v1.5.4). +status.short:: + Set to true to enable --short by default in linkgit:git-status[1]. + The option --no-short takes precedence over this variable. + +status.branch:: + Set to true to enable --branch by default in linkgit:git-status[1]. + The option --no-branch takes precedence over this variable. + status.showUntrackedFiles:: By default, linkgit:git-status[1] and linkgit:git-commit[1] show files which are not currently tracked by Git. Directories which diff --combined builtin/fetch.c index 99afed03d4,08ab948022..5936539552 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@@ -30,7 -30,11 +30,11 @@@ enum TAGS_SET = 2 }; - static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity; + static int fetch_prune_config = -1; /* unspecified */ + static int prune = -1; /* unspecified */ + #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */ + + static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity; static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT; static int tags = TAGS_DEFAULT, unshallow; static const char *depth; @@@ -54,17 -58,26 +58,26 @@@ static int option_parse_recurse_submodu return 0; } + static int git_fetch_config(const char *k, const char *v, void *cb) + { + if (!strcmp(k, "fetch.prune")) { + fetch_prune_config = git_config_bool(k, v); + return 0; + } + return 0; + } + static struct option builtin_fetch_options[] = { OPT__VERBOSITY(&verbosity), - OPT_BOOLEAN(0, "all", &all, - N_("fetch from all remotes")), - OPT_BOOLEAN('a', "append", &append, - N_("append to .git/FETCH_HEAD instead of overwriting")), + OPT_BOOL(0, "all", &all, + N_("fetch from all remotes")), + OPT_BOOL('a', "append", &append, + N_("append to .git/FETCH_HEAD instead of overwriting")), OPT_STRING(0, "upload-pack", &upload_pack, N_("path"), N_("path to upload pack on remote end")), OPT__FORCE(&force, N_("force overwrite of local branch")), - OPT_BOOLEAN('m', "multiple", &multiple, - N_("fetch from multiple remotes")), + OPT_BOOL('m', "multiple", &multiple, + N_("fetch from multiple remotes")), OPT_SET_INT('t', "tags", &tags, N_("fetch all tags and associated objects"), TAGS_SET), OPT_SET_INT('n', NULL, &tags, @@@ -74,10 -87,10 +87,10 @@@ { OPTION_CALLBACK, 0, "recurse-submodules", NULL, N_("on-demand"), N_("control recursive fetching of submodules"), PARSE_OPT_OPTARG, option_parse_recurse_submodules }, - OPT_BOOLEAN(0, "dry-run", &dry_run, - N_("dry run")), - OPT_BOOLEAN('k', "keep", &keep, N_("keep downloaded pack")), - OPT_BOOLEAN('u', "update-head-ok", &update_head_ok, + OPT_BOOL(0, "dry-run", &dry_run, + N_("dry run")), + OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")), + OPT_BOOL('u', "update-head-ok", &update_head_ok, N_("allow updating of HEAD ref")), OPT_BOOL(0, "progress", &progress, N_("force progress reporting")), OPT_STRING(0, "depth", &depth, N_("depth"), @@@ -119,7 -132,7 +132,7 @@@ static void add_merge_config(struct re for (rm = *head; rm; rm = rm->next) { if (branch_merge_matches(branch, i, rm->name)) { - rm->merge = 1; + rm->fetch_head_status = FETCH_HEAD_MERGE; break; } } @@@ -140,7 -153,7 +153,7 @@@ refspec.src = branch->merge[i]->src; get_fetch_map(remote_refs, &refspec, tail, 1); for (rm = *old_tail; rm; rm = rm->next) - rm->merge = 1; + rm->fetch_head_status = FETCH_HEAD_MERGE; } } @@@ -160,8 -173,6 +173,8 @@@ static struct ref *get_ref_map(struct t const struct ref *remote_refs = transport_get_remote_refs(transport); if (ref_count || tags == TAGS_SET) { + struct ref **old_tail; + for (i = 0; i < ref_count; i++) { get_fetch_map(remote_refs, &refs[i], &tail, 0); if (refs[i].dst && refs[i].dst[0]) @@@ -169,23 -180,9 +182,23 @@@ } /* Merge everything on the command line, but not --tags */ for (rm = ref_map; rm; rm = rm->next) - rm->merge = 1; + rm->fetch_head_status = FETCH_HEAD_MERGE; if (tags == TAGS_SET) get_fetch_map(remote_refs, tag_refspec, &tail, 0); + + /* + * For any refs that we happen to be fetching via command-line + * arguments, take the opportunity to update their configured + * counterparts. However, we do not want to mention these + * entries in FETCH_HEAD at all, as they would simply be + * duplicates of existing entries. + */ + old_tail = tail; + for (i = 0; i < transport->remote->fetch_refspec_nr; i++) + get_fetch_map(ref_map, &transport->remote->fetch[i], + &tail, 1); + for (rm = *old_tail; rm; rm = rm->next) + rm->fetch_head_status = FETCH_HEAD_IGNORE; } else { /* Use the defaults */ struct remote *remote = transport->remote; @@@ -202,7 -199,7 +215,7 @@@ *autotags = 1; if (!i && !has_merge && ref_map && !remote->fetch[0].pattern) - ref_map->merge = 1; + ref_map->fetch_head_status = FETCH_HEAD_MERGE; } /* * if the remote we're fetching from is the same @@@ -218,7 -215,7 +231,7 @@@ ref_map = get_remote_ref(remote_refs, "HEAD"); if (!ref_map) die(_("Couldn't find remote ref HEAD")); - ref_map->merge = 1; + ref_map->fetch_head_status = FETCH_HEAD_MERGE; tail = &ref_map->next; } } @@@ -405,7 -402,7 +418,7 @@@ static int store_updated_refs(const cha const char *what, *kind; struct ref *rm; char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD"); - int want_merge; + int want_status; fp = fopen(filename, "a"); if (!fp) @@@ -423,22 -420,19 +436,22 @@@ } /* - * The first pass writes objects to be merged and then the - * second pass writes the rest, in order to allow using - * FETCH_HEAD as a refname to refer to the ref to be merged. + * We do a pass for each fetch_head_status type in their enum order, so + * merged entries are written before not-for-merge. That lets readers + * use FETCH_HEAD as a refname to refer to the ref to be merged. */ - for (want_merge = 1; 0 <= want_merge; want_merge--) { + for (want_status = FETCH_HEAD_MERGE; + want_status <= FETCH_HEAD_IGNORE; + want_status++) { for (rm = ref_map; rm; rm = rm->next) { struct ref *ref = NULL; + const char *merge_status_marker = ""; commit = lookup_commit_reference_gently(rm->old_sha1, 1); if (!commit) - rm->merge = 0; + rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE; - if (rm->merge != want_merge) + if (rm->fetch_head_status != want_status) continue; if (rm->peer_ref) { @@@ -484,26 -478,16 +497,26 @@@ strbuf_addf(¬e, "%s ", kind); strbuf_addf(¬e, "'%s' of ", what); } - fprintf(fp, "%s\t%s\t%s", - sha1_to_hex(rm->old_sha1), - rm->merge ? "" : "not-for-merge", - note.buf); - for (i = 0; i < url_len; ++i) - if ('\n' == url[i]) - fputs("\\n", fp); - else - fputc(url[i], fp); - fputc('\n', fp); + switch (rm->fetch_head_status) { + case FETCH_HEAD_NOT_FOR_MERGE: + merge_status_marker = "not-for-merge"; + /* fall-through */ + case FETCH_HEAD_MERGE: + fprintf(fp, "%s\t%s\t%s", + sha1_to_hex(rm->old_sha1), + merge_status_marker, + note.buf); + for (i = 0; i < url_len; ++i) + if ('\n' == url[i]) + fputs("\\n", fp); + else + fputc(url[i], fp); + fputc('\n', fp); + break; + default: + /* do not write anything to FETCH_HEAD */ + break; + } strbuf_reset(¬e); if (ref) { @@@ -600,8 -584,7 +613,8 @@@ static int add_existing(const char *ref { struct string_list *list = (struct string_list *)cbdata; struct string_list_item *item = string_list_insert(list, refname); - item->util = (void *)sha1; + item->util = xmalloc(20); + hashcpy(item->util, sha1); return 0; } @@@ -620,7 -603,7 +633,7 @@@ static void find_non_local_tags(struct struct ref **head, struct ref ***tail) { - struct string_list existing_refs = STRING_LIST_INIT_NODUP; + struct string_list existing_refs = STRING_LIST_INIT_DUP; struct string_list remote_refs = STRING_LIST_INIT_NODUP; const struct ref *ref; struct string_list_item *item = NULL; @@@ -666,7 -649,7 +679,7 @@@ item = string_list_insert(&remote_refs, ref->name); item->util = (void *)ref->old_sha1; } - string_list_clear(&existing_refs, 0); + string_list_clear(&existing_refs, 1); /* * We may have a final lightweight tag that needs to be @@@ -723,11 -706,11 +736,11 @@@ static int truncate_fetch_head(void static int do_fetch(struct transport *transport, struct refspec *refs, int ref_count) { - struct string_list existing_refs = STRING_LIST_INIT_NODUP; - struct string_list_item *peer_item = NULL; + struct string_list existing_refs = STRING_LIST_INIT_DUP; struct ref *ref_map; struct ref *rm; int autotags = (transport->remote->fetch_tags == 1); + int retcode = 0; for_each_ref(add_existing, &existing_refs); @@@ -743,9 -726,9 +756,9 @@@ /* if not appending, truncate FETCH_HEAD */ if (!append && !dry_run) { - int errcode = truncate_fetch_head(); - if (errcode) - return errcode; + retcode = truncate_fetch_head(); + if (retcode) + goto cleanup; } ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags); @@@ -754,9 -737,8 +767,9 @@@ for (rm = ref_map; rm; rm = rm->next) { if (rm->peer_ref) { - peer_item = string_list_lookup(&existing_refs, - rm->peer_ref->name); + struct string_list_item *peer_item = + string_list_lookup(&existing_refs, + rm->peer_ref->name); if (peer_item) hashcpy(rm->peer_ref->old_sha1, peer_item->util); @@@ -767,11 -749,13 +780,14 @@@ transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1"); if (fetch_refs(transport, ref_map)) { free_refs(ref_map); - return 1; + retcode = 1; + goto cleanup; } if (prune) { - /* If --tags was specified, pretend the user gave us the canonical tags refspec */ + /* + * If --tags was specified, pretend that the user gave us + * the canonical tags refspec + */ if (tags == TAGS_SET) { const char *tags_str = "refs/tags/*:refs/tags/*"; struct refspec *tags_refspec, *refspec; @@@ -811,9 -795,7 +827,9 @@@ free_refs(ref_map); } - return 0; + cleanup: + string_list_clear(&existing_refs, 1); + return retcode; } static void set_option(const char *name, const char *value) @@@ -882,7 -864,7 +898,7 @@@ static void add_options_to_argv(struct { if (dry_run) argv_array_push(argv, "--dry-run"); - if (prune) + if (prune > 0) argv_array_push(argv, "--prune"); if (update_head_ok) argv_array_push(argv, "--update-head-ok"); @@@ -950,6 -932,17 +966,17 @@@ static int fetch_one(struct remote *rem "remote name from which new revisions should be fetched.")); transport = transport_get(remote, NULL); + + if (prune < 0) { + /* no command line request */ + if (0 <= transport->remote->prune) + prune = transport->remote->prune; + else if (0 <= fetch_prune_config) + prune = fetch_prune_config; + else + prune = PRUNE_BY_DEFAULT; + } + transport_set_verbosity(transport, verbosity, progress); if (upload_pack) set_option(TRANS_OPT_UPLOADPACK, upload_pack); @@@ -1007,6 -1000,8 +1034,8 @@@ int cmd_fetch(int argc, const char **ar for (i = 1; i < argc; i++) strbuf_addf(&default_rla, " %s", argv[i]); + git_config(git_fetch_config, NULL); + argc = parse_options(argc, argv, prefix, builtin_fetch_options, builtin_fetch_usage, 0); diff --combined remote.c index efcba931ec,89be21166d..8f0f2dd10e --- a/remote.c +++ b/remote.c @@@ -148,6 -148,7 +148,7 @@@ static struct remote *make_remote(cons } ret = xcalloc(1, sizeof(struct remote)); + ret->prune = -1; /* unspecified */ ALLOC_GROW(remotes, remotes_nr + 1, remotes_alloc); remotes[remotes_nr++] = ret; if (len) @@@ -276,9 -277,10 +277,9 @@@ static void read_remotes_file(struct re static void read_branches_file(struct remote *remote) { - const char *slash = strchr(remote->name, '/'); char *frag; struct strbuf branch = STRBUF_INIT; - int n = slash ? slash - remote->name : 1000; + int n = 1000; FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r"); char *s, *p; int len; @@@ -298,11 -300,21 +299,11 @@@ while (isspace(p[-1])) *--p = 0; len = p - s; - if (slash) - len += strlen(slash); p = xmalloc(len + 1); strcpy(p, s); - if (slash) - strcat(p, slash); /* - * With "slash", e.g. "git fetch jgarzik/netdev-2.6" when - * reading from $GIT_DIR/branches/jgarzik fetches "HEAD" from - * the partial URL obtained from the branches file plus - * "/netdev-2.6" and does not store it in any tracking ref. - * #branch specifier in the file is ignored. - * - * Otherwise, the branches file would have URL and optionally + * The branches file would have URL and optionally * #branch specified. The "master" (or specified) branch is * fetched and stored in the local branch of the same name. */ @@@ -312,8 -324,12 +313,8 @@@ strbuf_addf(&branch, "refs/heads/%s", frag); } else strbuf_addstr(&branch, "refs/heads/master"); - if (!slash) { - strbuf_addf(&branch, ":refs/heads/%s", remote->name); - } else { - strbuf_reset(&branch); - strbuf_addstr(&branch, "HEAD:"); - } + + strbuf_addf(&branch, ":refs/heads/%s", remote->name); add_url_alias(remote, p); add_fetch_refspec(remote, strbuf_detach(&branch, NULL)); /* @@@ -404,6 -420,8 +405,8 @@@ static int handle_config(const char *ke remote->skip_default_update = git_config_bool(key, value); else if (!strcmp(subkey, ".skipfetchall")) remote->skip_default_update = git_config_bool(key, value); + else if (!strcmp(subkey, ".prune")) + remote->prune = git_config_bool(key, value); else if (!strcmp(subkey, ".url")) { const char *v; if (git_config_string(&v, key, value)) @@@ -1302,14 -1320,6 +1305,14 @@@ static void add_missing_tags(struct re free(sent_tips.tip); } +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); +} + /* * Given the set of refs the local repository has, the set of refs the * remote repository has, and the refspec used for push, determine @@@ -1328,7 -1338,6 +1331,7 @@@ int match_push_refs(struct ref *src, st int errs; static const char *default_refspec[] = { ":", NULL }; struct ref *ref, **dst_tail = tail_ref(dst); + struct string_list dst_ref_index = STRING_LIST_INIT_NODUP; if (!nr_refspec) { nr_refspec = 1; @@@ -1339,7 -1348,6 +1342,7 @@@ /* pick the remainder */ for (ref = src; ref; ref = ref->next) { + struct string_list_item *dst_item; struct ref *dst_peer; const struct refspec *pat = NULL; char *dst_name; @@@ -1348,11 -1356,7 +1351,11 @@@ if (!dst_name) continue; - dst_peer = find_ref_by_name(*dst, dst_name); + 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; if (dst_peer) { if (dst_peer->peer_ref) /* We're already sending something to this ref. */ @@@ -1369,8 -1373,6 +1372,8 @@@ /* Create a new one and link it */ dst_peer = make_linked_ref(dst_name, &dst_tail); hashcpy(dst_peer->new_sha1, ref->new_sha1); + string_list_insert(&dst_ref_index, + dst_peer->name)->util = dst_peer; } dst_peer->peer_ref = copy_ref(ref); dst_peer->force = pat->force; @@@ -1378,13 -1380,10 +1381,13 @@@ free(dst_name); } + string_list_clear(&dst_ref_index, 0); + if (flags & MATCH_REFS_FOLLOW_TAGS) add_missing_tags(src, dst, &dst_tail); if (send_prune) { + struct string_list src_ref_index = STRING_LIST_INIT_NODUP; /* check for missing refs on the remote */ for (ref = *dst; ref; ref = ref->next) { char *src_name; @@@ -1395,15 -1394,11 +1398,15 @@@ src_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_DST, NULL); if (src_name) { - if (!find_ref_by_name(src, src_name)) + if (!src_ref_index.nr) + prepare_ref_index(&src_ref_index, src); + if (!string_list_has_string(&src_ref_index, + src_name)) ref->peer_ref = alloc_delete_ref(); free(src_name); } } + string_list_clear(&src_ref_index, 0); } if (errs) return -1; @@@ -1482,7 -1477,8 +1485,7 @@@ struct branch *branch_get(const char *n ret->remote = remote_get(ret->remote_name); if (ret->merge_nr) { int i; - ret->merge = xcalloc(sizeof(*ret->merge), - ret->merge_nr); + ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge)); for (i = 0; i < ret->merge_nr; i++) { ret->merge[i] = xcalloc(1, sizeof(**ret->merge)); ret->merge[i]->src = xstrdup(ret->merge_name[i]); diff --combined t/t5510-fetch.sh index fde689166a,019535f18e..1f0f8e6827 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@@ -370,39 -370,30 +370,39 @@@ test_expect_success 'bundle should reco ' -test_expect_success 'explicit fetch should not update tracking' ' +test_expect_success 'mark initial state of origin/master' ' + ( + cd three && + git tag base-origin-master refs/remotes/origin/master + ) +' + +test_expect_success 'explicit fetch should update tracking' ' cd "$D" && git branch -f side && ( cd three && + git update-ref refs/remotes/origin/master base-origin-master && o=$(git rev-parse --verify refs/remotes/origin/master) && git fetch origin master && n=$(git rev-parse --verify refs/remotes/origin/master) && - test "$o" = "$n" && + test "$o" != "$n" && test_must_fail git rev-parse --verify refs/remotes/origin/side ) ' -test_expect_success 'explicit pull should not update tracking' ' +test_expect_success 'explicit pull should update tracking' ' cd "$D" && git branch -f side && ( cd three && + git update-ref refs/remotes/origin/master base-origin-master && o=$(git rev-parse --verify refs/remotes/origin/master) && git pull origin master && n=$(git rev-parse --verify refs/remotes/origin/master) && - test "$o" = "$n" && + test "$o" != "$n" && test_must_fail git rev-parse --verify refs/remotes/origin/side ) ' @@@ -413,7 -404,6 +413,7 @@@ test_expect_success 'configured fetch u git branch -f side && ( cd three && + git update-ref refs/remotes/origin/master base-origin-master && o=$(git rev-parse --verify refs/remotes/origin/master) && git fetch origin && n=$(git rev-parse --verify refs/remotes/origin/master) && @@@ -422,22 -412,6 +422,22 @@@ ) ' +test_expect_success 'non-matching refspecs do not confuse tracking update' ' + cd "$D" && + git update-ref refs/odd/location HEAD && + ( + cd three && + git update-ref refs/remotes/origin/master base-origin-master && + git config --add remote.origin.fetch \ + refs/odd/location:refs/remotes/origin/odd && + o=$(git rev-parse --verify refs/remotes/origin/master) && + git fetch origin master && + n=$(git rev-parse --verify refs/remotes/origin/master) && + test "$o" != "$n" && + test_must_fail git rev-parse --verify refs/remotes/origin/odd + ) +' + test_expect_success 'pushing nonexistent branch by mistake should not segv' ' cd "$D" && @@@ -497,6 -471,88 +497,88 @@@ test_expect_success "should be able to ) ' + # configured prune tests + + set_config_tristate () { + # var=$1 val=$2 + case "$2" in + unset) test_unconfig "$1" ;; + *) git config "$1" "$2" ;; + esac + } + + test_configured_prune () { + fetch_prune=$1 remote_origin_prune=$2 cmdline=$3 expected=$4 + + test_expect_success "prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; $4" ' + # make sure a newbranch is there in . and also in one + git branch -f newbranch && + ( + cd one && + test_unconfig fetch.prune && + test_unconfig remote.origin.prune && + git fetch && + git rev-parse --verify refs/remotes/origin/newbranch + ) + + # now remove it + git branch -d newbranch && + + # then test + ( + cd one && + set_config_tristate fetch.prune $fetch_prune && + set_config_tristate remote.origin.prune $remote_origin_prune && + + git fetch $cmdline && + case "$expected" in + pruned) + test_must_fail git rev-parse --verify refs/remotes/origin/newbranch + ;; + kept) + git rev-parse --verify refs/remotes/origin/newbranch + ;; + esac + ) + ' + } + + test_configured_prune unset unset "" kept + test_configured_prune unset unset "--no-prune" kept + test_configured_prune unset unset "--prune" pruned + + test_configured_prune false unset "" kept + test_configured_prune false unset "--no-prune" kept + test_configured_prune false unset "--prune" pruned + + test_configured_prune true unset "" pruned + test_configured_prune true unset "--prune" pruned + test_configured_prune true unset "--no-prune" kept + + test_configured_prune unset false "" kept + test_configured_prune unset false "--no-prune" kept + test_configured_prune unset false "--prune" pruned + + test_configured_prune false false "" kept + test_configured_prune false false "--no-prune" kept + test_configured_prune false false "--prune" pruned + + test_configured_prune true false "" kept + test_configured_prune true false "--prune" pruned + test_configured_prune true false "--no-prune" kept + + test_configured_prune unset true "" pruned + test_configured_prune unset true "--no-prune" kept + test_configured_prune unset true "--prune" pruned + + test_configured_prune false true "" pruned + test_configured_prune false true "--no-prune" kept + test_configured_prune false true "--prune" pruned + + test_configured_prune true true "" pruned + test_configured_prune true true "--prune" pruned + test_configured_prune true true "--no-prune" kept + test_expect_success 'all boundary commits are excluded' ' test_commit base && test_commit oneside &&