Revision 8b0eaa41f2371cafd28324b00128405a80740b16 authored by SZEDER Gábor on 22 March 2018, 14:16:04 UTC, committed by Junio C Hamano on 22 March 2018, 17:22:09 UTC
The established way to update the completion script in an already running shell is to simply source it again: this brings in any new --options and features, and clears caching variables. E.g. it clears the variables caching the list of (all|porcelain) git commands, so when they are later lazy-initialized again, then they will list and cache any newly installed commmands as well. Unfortunately, since d401f3debc (git-completion.bash: introduce __gitcomp_builtin, 2018-02-09) and subsequent patches this doesn't work for a lot of git commands' options. To eliminate a lot of hard-to-maintain hard-coded lists of options, those commits changed the completion script to use a bunch of programmatically created and lazy-initialized variables to cache the options of those builtin porcelain commands that use parse-options. These variables are not cleared upon sourcing the completion script, therefore they continue caching the old lists of options, even when some commands recently learned new options or when deprecated options were removed. Always 'unset' these variables caching the options of builtin commands when sourcing the completion script. Redirect 'unset's stderr to /dev/null, because ZSH's 'unset' complains if it's invoked without any arguments, i.e. no variables caching builtin's options are set. This can happen, if someone were to source the completion script twice without completing any --options in between. Bash stays silent in this case. Add tests to ensure that these variables are indeed cleared when the completion script is sourced; not just the variables caching options, but all other caching variables, i.e. the variables caching commands, porcelain commands and merge strategies as well. Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 27b42d0
merge-file.c
#include "builtin.h"
#include "cache.h"
#include "config.h"
#include "xdiff/xdiff.h"
#include "xdiff-interface.h"
#include "parse-options.h"
static const char *const merge_file_usage[] = {
N_("git merge-file [<options>] [-L <name1> [-L <orig> [-L <name2>]]] <file1> <orig-file> <file2>"),
NULL
};
static int label_cb(const struct option *opt, const char *arg, int unset)
{
static int label_count = 0;
const char **names = (const char **)opt->value;
if (label_count >= 3)
return error("too many labels on the command line");
names[label_count++] = arg;
return 0;
}
int cmd_merge_file(int argc, const char **argv, const char *prefix)
{
const char *names[3] = { NULL, NULL, NULL };
mmfile_t mmfs[3];
mmbuffer_t result = {NULL, 0};
xmparam_t xmp = {{0}};
int ret = 0, i = 0, to_stdout = 0;
int quiet = 0;
struct option options[] = {
OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")),
OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3),
OPT_SET_INT(0, "ours", &xmp.favor, N_("for conflicts, use our version"),
XDL_MERGE_FAVOR_OURS),
OPT_SET_INT(0, "theirs", &xmp.favor, N_("for conflicts, use their version"),
XDL_MERGE_FAVOR_THEIRS),
OPT_SET_INT(0, "union", &xmp.favor, N_("for conflicts, use a union version"),
XDL_MERGE_FAVOR_UNION),
OPT_INTEGER(0, "marker-size", &xmp.marker_size,
N_("for conflicts, use this marker size")),
OPT__QUIET(&quiet, N_("do not warn about conflicts")),
OPT_CALLBACK('L', NULL, names, N_("name"),
N_("set labels for file1/orig-file/file2"), &label_cb),
OPT_END(),
};
xmp.level = XDL_MERGE_ZEALOUS_ALNUM;
xmp.style = 0;
xmp.favor = 0;
if (startup_info->have_repository) {
/* Read the configuration file */
git_config(git_xmerge_config, NULL);
if (0 <= git_xmerge_style)
xmp.style = git_xmerge_style;
}
argc = parse_options(argc, argv, prefix, options, merge_file_usage, 0);
if (argc != 3)
usage_with_options(merge_file_usage, options);
if (quiet) {
if (!freopen("/dev/null", "w", stderr))
return error_errno("failed to redirect stderr to /dev/null");
}
for (i = 0; i < 3; i++) {
char *fname;
int ret;
if (!names[i])
names[i] = argv[i];
fname = prefix_filename(prefix, argv[i]);
ret = read_mmfile(mmfs + i, fname);
free(fname);
if (ret)
return -1;
if (mmfs[i].size > MAX_XDIFF_SIZE ||
buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
return error("Cannot merge binary files: %s",
argv[i]);
}
xmp.ancestor = names[1];
xmp.file1 = names[0];
xmp.file2 = names[2];
ret = xdl_merge(mmfs + 1, mmfs + 0, mmfs + 2, &xmp, &result);
for (i = 0; i < 3; i++)
free(mmfs[i].ptr);
if (ret >= 0) {
const char *filename = argv[0];
char *fpath = prefix_filename(prefix, argv[0]);
FILE *f = to_stdout ? stdout : fopen(fpath, "wb");
if (!f)
ret = error_errno("Could not open %s for writing",
filename);
else if (result.size &&
fwrite(result.ptr, result.size, 1, f) != 1)
ret = error_errno("Could not write to %s", filename);
else if (fclose(f))
ret = error_errno("Could not close %s", filename);
free(result.ptr);
free(fpath);
}
if (ret > 127)
ret = 127;
return ret;
}
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...