From: Junio C Hamano Date: Thu, 25 Jul 2013 02:22:49 +0000 (-0700) Subject: Merge branch 'ml/cygwin-updates' X-Git-Tag: v1.8.4-rc0~4 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/f01723aaa1dc6ab676073d27b8d471e29abae208?hp=c28facd216b501d41ca76f040d0ce7749075aba0 Merge branch 'ml/cygwin-updates' The tip one does _not_ revert c869753e (Force core.filemode to false on Cygwin., 2006-12-30) on purpose, so that people can still retain the old behaviour if they wanted to. * ml/cygwin-updates: cygwin: stop forcing core.filemode=false Cygwin 1.7 supports mmap Cygwin 1.7 has thread-safe pread Cygwin 1.7 needs compat/regex --- diff --git a/.gitignore b/.gitignore index efa8db0035..6b1fd1bfb0 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ /git-cat-file /git-check-attr /git-check-ignore +/git-check-mailmap /git-check-ref-format /git-checkout /git-checkout-index diff --git a/Documentation/RelNotes/1.8.3.4.txt b/Documentation/RelNotes/1.8.3.4.txt index 24ee2b8017..56f106e262 100644 --- a/Documentation/RelNotes/1.8.3.4.txt +++ b/Documentation/RelNotes/1.8.3.4.txt @@ -13,3 +13,8 @@ Fixes since v1.8.3.3 * The test coverage framework was left broken for some time. * The test suite for HTTP transport did not run with Apache 2.4. + + * "git diff" used to fail when core.safecrlf is set and the working + tree contents had mixed CRLF/LF line endings. Committing such a + content must be prohibited, but "git diff" should help the user to + locate and fix such problems without failing. diff --git a/Documentation/RelNotes/1.8.4.txt b/Documentation/RelNotes/1.8.4.txt index 2226abeffb..ad58ac6dbd 100644 --- a/Documentation/RelNotes/1.8.4.txt +++ b/Documentation/RelNotes/1.8.4.txt @@ -83,6 +83,28 @@ Foreign interfaces, subsystems and ports. UI, Workflows & Features + * "git clean" command learned an interactive mode. + + * The "--head" option to "git show-ref" was only to add "HEAD" to the + list of candidate refs to be filtered by the usual rules + (e.g. "--heads" that only show refs under refs/heads). The meaning + of the option has been changed to always show "HEAD" regardless of + what filtering will be applied to any other ref. + + This is a backward incompatible change and might cause breakages to + people's existing scripts. + + * "git show -s" was less discoverable than it should have been. It + now has a natural synonym "git show --no-patch". + + * "git check-mailmap" is a new command that lets you map usernames + and e-mail addresses through the mailmap mechanism, just like many + built-in commands do. + + * "git name-rev" learned to name an annotated tag object back to its + tagname; "git name-rev $(git rev-parse v1.0.0)" gives "tags/v1.0.0", + for example. + * "git cat-file --batch-check=" is added, primarily to allow on-disk footprint of objects in packfiles (often they are a lot smaller than their true size, when expressed as deltas) to be @@ -212,6 +234,14 @@ UI, Workflows & Features Performance, Internal Implementation, etc. + * The function attributes extensions are used to catch mistakes in + use of our own variadic functions that use NULL sentinel at the end + (i.e. like execl(3)) and format strings (i.e. like printf(3)). + + * The code to allow configuration data to be read from in-tree blob + objects is in. This may help working in a bare repository and + submodule updates. + * Fetching between repositories with many refs employed O(n^2) algorithm to match up the common objects, which has been corrected. @@ -280,6 +310,26 @@ Unless otherwise noted, all the fixes since v1.8.3 in the maintenance track are contained in this release (see release notes to them for details). + * Newer Net::SMTP::SSL module does not want the user programs to use + the default behaviour to let server certificate go without + verification, so by default enable the verification with a + mechanism to turn it off if needed. + (merge 35035bb rr/send-email-ssl-verify later to maint). + + * When "git" is spawned in such a way that any of the low 3 file + descriptors is closed, our first open() may yield file descriptor 2, + and writing error message to it would screw things up in a big way. + (merge a11c396 tr/protect-low-3-fds later to maint). + + * The mailmap mechanism unnecessarily downcased the e-mail addresses + in the output, and also ignored the human name when it is a single + character name. + (merge bd23794 jc/mailmap-case-insensitivity later to maint). + + * In two places we did not check return value (expected to be a file + descriptor) correctly. + (merge a77f106 tr/fd-gotcha-fixes later to maint). + * Logic to auto-detect character encodings in the commit log message did not reject overlong and invalid UTF-8 characters. (merge 81050ac bc/commit-invalid-utf8 later to maint). diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt index e9f984ba01..4e55b1564e 100644 --- a/Documentation/blame-options.txt +++ b/Documentation/blame-options.txt @@ -9,9 +9,13 @@ --show-stats:: Include additional statistics at the end of blame output. --L ,, -L ::: - Annotate only the given line range. and can take - one of these forms: +-L ,:: +-L ::: + Annotate only the given line range. and are optional. + ``-L '' or ``-L ,'' spans from to end of file. + ``-L ,'' spans from start of file to . ++ + and can take one of these forms: include::line-range-format.txt[] diff --git a/Documentation/config.txt b/Documentation/config.txt index 81856dd5a2..e0b923f428 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -879,16 +879,17 @@ The values of these variables may be specified as in color.branch.. 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 @@ -973,6 +974,10 @@ 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. @@ -2068,6 +2073,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 diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index 87e92d6537..bbed2cd79c 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -26,6 +26,11 @@ ifndef::git-format-patch[] {git-diff? This is the default.} endif::git-format-patch[] +-s:: +--no-patch:: + Suppress diff output. Useful for commands like `git show` that + show the patch by default, or to cancel the effect of `--patch`. + -U:: --unified=:: Generate diffs with lines of context instead of diff --git a/Documentation/git-check-mailmap.txt b/Documentation/git-check-mailmap.txt new file mode 100644 index 0000000000..39028ee1a3 --- /dev/null +++ b/Documentation/git-check-mailmap.txt @@ -0,0 +1,47 @@ +git-check-mailmap(1) +==================== + +NAME +---- +git-check-mailmap - Show canonical names and email addresses of contacts + + +SYNOPSIS +-------- +[verse] +'git check-mailmap' [options] ... + + +DESCRIPTION +----------- + +For each ``Name $$$$'' or ``$$$$'' from the command-line +or standard input (when using `--stdin`), look up the person's canonical name +and email address (see "Mapping Authors" below). If found, print them; +otherwise print the input as-is. + + +OPTIONS +------- +--stdin:: + Read contacts, one per line, from the standard input after exhausting + contacts provided on the command-line. + + +OUTPUT +------ + +For each contact, a single line is output, terminated by a newline. If the +name is provided or known to the 'mailmap', ``Name $$$$'' is +printed; otherwise only ``$$$$'' is printed. + + +MAPPING AUTHORS +--------------- + +include::mailmap.txt[] + + +GIT +--- +Part of the linkgit:git[1] suite diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt index bdc3ab80c7..75fb543393 100644 --- a/Documentation/git-clean.txt +++ b/Documentation/git-clean.txt @@ -8,7 +8,7 @@ git-clean - Remove untracked files from the working tree SYNOPSIS -------- [verse] -'git clean' [-d] [-f] [-n] [-q] [-e ] [-x | -X] [--] ... +'git clean' [-d] [-f] [-i] [-n] [-q] [-e ] [-x | -X] [--] ... DESCRIPTION ----------- @@ -34,7 +34,13 @@ OPTIONS -f:: --force:: If the Git configuration variable clean.requireForce is not set - to false, 'git clean' will refuse to run unless given -f or -n. + to false, 'git clean' will refuse to run unless given -f, -n or + -i. + +-i:: +--interactive:: + Show what would be done and clean files interactively. See + ``Interactive mode'' for details. -n:: --dry-run:: @@ -63,6 +69,67 @@ OPTIONS Remove only files ignored by Git. This may be useful to rebuild everything from scratch, but keep manually created files. +Interactive mode +---------------- +When the command enters the interactive mode, it shows the +files and directories to be cleaned, and goes into its +interactive command loop. + +The command loop shows the list of subcommands available, and +gives a prompt "What now> ". In general, when the prompt ends +with a single '>', you can pick only one of the choices given +and type return, like this: + +------------ + *** Commands *** + 1: clean 2: filter by pattern 3: select by numbers + 4: ask each 5: quit 6: help + What now> 1 +------------ + +You also could say `c` or `clean` above as long as the choice is unique. + +The main command loop has 6 subcommands. + +clean:: + + Start cleaning files and directories, and then quit. + +filter by pattern:: + + This shows the files and directories to be deleted and issues an + "Input ignore patterns>>" prompt. You can input space-seperated + patterns to exclude files and directories from deletion. + E.g. "*.c *.h" will excludes files end with ".c" and ".h" from + deletion. When you are satisfied with the filtered result, press + ENTER (empty) back to the main menu. + +select by numbers:: + + This shows the files and directories to be deleted and issues an + "Select items to delete>>" prompt. When the prompt ends with double + '>>' like this, you can make more than one selection, concatenated + with whitespace or comma. Also you can say ranges. E.g. "2-5 7,9" + to choose 2,3,4,5,7,9 from the list. If the second number in a + range is omitted, all remaining patches are taken. E.g. "7-" to + choose 7,8,9 from the list. You can say '*' to choose everything. + Also when you are satisfied with the filtered result, press ENTER + (empty) back to the main menu. + +ask each:: + + This will start to clean, and you must confirm one by one in order + to delete items. Please note that this action is not as efficient + as the above two actions. + +quit:: + + This lets you quit without do cleaning. + +help:: + + Show brief usage of interactive git-clean. + SEE ALSO -------- linkgit:gitignore[5] diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 99dc497b6f..34b0894646 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -127,6 +127,13 @@ See also <>. --file config-file:: Use the given config file instead of the one specified by GIT_CONFIG. +--blob blob:: + Similar to '--file' but use the given blob instead of a file. E.g. + you can use 'master:.gitmodules' to read values from the file + '.gitmodules' in the master branch. See "SPECIFYING REVISIONS" + section in linkgit:gitrevisions[7] for a more complete list of + ways to spell blob names. + --remove-section:: Remove the given section from the configuration file. diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index 2ee6962a72..ac2694d04c 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -97,7 +97,7 @@ include::rev-list-options.txt[] include::pretty-formats.txt[] -Common diff options +COMMON DIFF OPTIONS ------------------- :git-log: 1 @@ -105,7 +105,7 @@ include::diff-options.txt[] include::diff-generate-patch.txt[] -Examples +EXAMPLES -------- `git log --no-merges`:: @@ -161,12 +161,12 @@ Examples `git log -3`:: Limits the number of commits to show to 3. -Discussion +DISCUSSION ---------- include::i18n.txt[] -Configuration +CONFIGURATION ------------- See linkgit:git-config[1] for core variables and linkgit:git-diff[1] diff --git a/Documentation/git-pack-refs.txt b/Documentation/git-pack-refs.txt index f131677478..154081f2de 100644 --- a/Documentation/git-pack-refs.txt +++ b/Documentation/git-pack-refs.txt @@ -33,8 +33,8 @@ Subsequent updates to branches always create new files under `$GIT_DIR/refs` directory hierarchy. A recommended practice to deal with a repository with too many -refs is to pack its refs with `--all --prune` once, and -occasionally run `git pack-refs --prune`. Tags are by +refs is to pack its refs with `--all` once, and +occasionally run `git pack-refs`. Tags are by definition stationary and are not expected to change. Branch heads will be packed with the initial `pack-refs --all`, but only the currently active branch heads will become unpacked, diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt index 80d01b0571..bf824108c1 100644 --- a/Documentation/git-prune.txt +++ b/Documentation/git-prune.txt @@ -59,7 +59,7 @@ borrows from your repository via its `.git/objects/info/alternates`: ------------ -$ git prune $(cd ../another && $(git rev-parse --all)) +$ git prune $(cd ../another && git rev-parse --all) ------------ Notes diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 40a9a9abc1..f0e57a597b 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -198,6 +198,12 @@ must be used for each option. --smtp-ssl:: Legacy alias for '--smtp-encryption ssl'. +--smtp-ssl-cert-path:: + Path to ca-certificates (either a directory or a single file). + Set it to an empty string to disable certificate verification. + Defaults to the value set to the 'sendemail.smtpsslcertpath' + configuration variable, if set, or `/etc/ssl/certs` otherwise. + --smtp-user=:: Username for SMTP-AUTH. Default is the value of 'sendemail.smtpuser'; if a username is not specified (with '--smtp-user' or 'sendemail.smtpuser'), diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt index de4d352da2..b0a309b117 100644 --- a/Documentation/git-show-ref.txt +++ b/Documentation/git-show-ref.txt @@ -21,6 +21,8 @@ commit IDs. Results can be filtered using a pattern and tags can be dereferenced into object IDs. Additionally, it can be used to test whether a particular ref exists. +By default, shows the tags, heads, and remote refs. + The --exclude-existing form is a filter that does the inverse, it shows the refs from stdin that don't exist in the local repository. @@ -32,14 +34,14 @@ OPTIONS --head:: - Show the HEAD reference. + Show the HEAD reference, even if it would normally be filtered out. --tags:: --heads:: - Limit to only "refs/heads" and "refs/tags", respectively. These - options are not mutually exclusive; when given both, references stored - in "refs/heads" and "refs/tags" are displayed. + Limit to "refs/heads" and "refs/tags", respectively. These options + are not mutually exclusive; when given both, references stored in + "refs/heads" and "refs/tags" are displayed. -d:: --dereference:: diff --git a/Documentation/git-show.txt b/Documentation/git-show.txt index ae4edcccfb..4e617e6979 100644 --- a/Documentation/git-show.txt +++ b/Documentation/git-show.txt @@ -45,6 +45,15 @@ include::pretty-options.txt[] include::pretty-formats.txt[] +COMMON DIFF OPTIONS +------------------- + +:git-log: 1 +include::diff-options.txt[] + +include::diff-generate-patch.txt[] + + EXAMPLES -------- diff --git a/Documentation/git.txt b/Documentation/git.txt index e9b4922110..3bdd56e8f4 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,9 +43,10 @@ unreleased) version of Git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v1.8.3.3/git.html[documentation for release 1.8.3.3] +* link:v1.8.3.4/git.html[documentation for release 1.8.3.4] * release notes for + link:RelNotes/1.8.3.4.txt[1.8.3.4], link:RelNotes/1.8.3.3.txt[1.8.3.3], link:RelNotes/1.8.3.2.txt[1.8.3.2], link:RelNotes/1.8.3.1.txt[1.8.3.1], diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index e157ec3fe7..e632e8576e 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -849,7 +849,4 @@ options may be given. See linkgit:git-diff-files[1] for more options. -t:: Show the tree objects in the diff output. This implies '-r'. - --s:: - Suppress diff output. endif::git-rev-list[] diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index e364007d7c..fe723e4722 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -4675,5 +4675,5 @@ Write a chapter on using plumbing and writing scripts. Alternates, clone -reference, etc. More on recovery from repository corruption. See: - http://marc.theaimsgroup.com/?l=git&m=117263864820799&w=2 - http://marc.theaimsgroup.com/?l=git&m=117147855503798&w=2 + http://marc.info/?l=git&m=117263864820799&w=2 + http://marc.info/?l=git&m=117147855503798&w=2 diff --git a/Makefile b/Makefile index 0600eb46db..ef442ebad0 100644 --- a/Makefile +++ b/Makefile @@ -912,6 +912,7 @@ BUILTIN_OBJS += builtin/bundle.o BUILTIN_OBJS += builtin/cat-file.o BUILTIN_OBJS += builtin/check-attr.o BUILTIN_OBJS += builtin/check-ignore.o +BUILTIN_OBJS += builtin/check-mailmap.o BUILTIN_OBJS += builtin/check-ref-format.o BUILTIN_OBJS += builtin/checkout-index.o BUILTIN_OBJS += builtin/checkout.o diff --git a/advice.h b/advice.h index 93a7d110ea..08fbc8ee3c 100644 --- a/advice.h +++ b/advice.h @@ -21,6 +21,7 @@ extern int advice_object_name_warning; extern int advice_rm_hints; int git_default_advice_config(const char *var, const char *value); +__attribute__((format (printf, 1, 2))) void advise(const char *advice, ...); int error_resolve_conflict(const char *me); extern void NORETURN die_resolve_conflict(const char *me); diff --git a/argv-array.h b/argv-array.h index 40248d424c..85ba438ac1 100644 --- a/argv-array.h +++ b/argv-array.h @@ -15,6 +15,7 @@ void argv_array_init(struct argv_array *); void argv_array_push(struct argv_array *, const char *); __attribute__((format (printf,2,3))) void argv_array_pushf(struct argv_array *, const char *fmt, ...); +LAST_ARG_MUST_BE_NULL void argv_array_pushl(struct argv_array *, ...); void argv_array_pop(struct argv_array *); void argv_array_clear(struct argv_array *); diff --git a/builtin.h b/builtin.h index 1ed8edb0cb..8afa2def49 100644 --- a/builtin.h +++ b/builtin.h @@ -40,6 +40,7 @@ extern int cmd_checkout(int argc, const char **argv, const char *prefix); extern int cmd_checkout_index(int argc, const char **argv, const char *prefix); extern int cmd_check_attr(int argc, const char **argv, const char *prefix); extern int cmd_check_ignore(int argc, const char **argv, const char *prefix); +extern int cmd_check_mailmap(int argc, const char **argv, const char *prefix); extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix); extern int cmd_cherry(int argc, const char **argv, const char *prefix); extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix); diff --git a/builtin/add.c b/builtin/add.c index f45d9d4865..8266a9cb70 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -343,6 +343,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix) argc = setup_revisions(argc, argv, &rev, NULL); rev.diffopt.output_format = DIFF_FORMAT_PATCH; + rev.diffopt.use_color = 0; DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES); out = open(file, O_CREAT | O_WRONLY, 0666); if (out < 0) diff --git a/builtin/apply.c b/builtin/apply.c index ff01ebff3d..50912c928f 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -473,7 +473,7 @@ static char *find_name_gnu(const char *line, const char *def, int p_value) /* * Proposed "new-style" GNU patch/diff format; see - * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2 + * http://marc.info/?l=git&m=112927316408690&w=2 */ if (unquote_c_style(&name, line, NULL)) { strbuf_release(&name); @@ -2999,7 +2999,7 @@ static int read_blob_object(struct strbuf *buf, const unsigned char *sha1, unsig return 0; } -static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf) +static int read_file_or_gitlink(const struct cache_entry *ce, struct strbuf *buf) { if (!ce) return 0; @@ -3117,7 +3117,7 @@ static struct patch *previous_patch(struct patch *patch, int *gone) return previous; } -static int verify_index_match(struct cache_entry *ce, struct stat *st) +static int verify_index_match(const struct cache_entry *ce, struct stat *st) { if (S_ISGITLINK(ce->ce_mode)) { if (!S_ISDIR(st->st_mode)) @@ -3130,7 +3130,7 @@ static int verify_index_match(struct cache_entry *ce, struct stat *st) #define SUBMODULE_PATCH_WITHOUT_INDEX 1 static int load_patch_target(struct strbuf *buf, - struct cache_entry *ce, + const struct cache_entry *ce, struct stat *st, const char *name, unsigned expected_mode) @@ -3160,7 +3160,8 @@ static int load_patch_target(struct strbuf *buf, * we read from the result of a previous diff. */ static int load_preimage(struct image *image, - struct patch *patch, struct stat *st, struct cache_entry *ce) + struct patch *patch, struct stat *st, + const struct cache_entry *ce) { struct strbuf buf = STRBUF_INIT; size_t len; @@ -3273,7 +3274,7 @@ static int load_current(struct image *image, struct patch *patch) } static int try_threeway(struct image *image, struct patch *patch, - struct stat *st, struct cache_entry *ce) + struct stat *st, const struct cache_entry *ce) { unsigned char pre_sha1[20], post_sha1[20], our_sha1[20]; struct strbuf buf = STRBUF_INIT; @@ -3343,7 +3344,7 @@ static int try_threeway(struct image *image, struct patch *patch, return 0; } -static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce) +static int apply_data(struct patch *patch, struct stat *st, const struct cache_entry *ce) { struct image image; @@ -3847,7 +3848,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned const char *s = buf; if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1)) - die(_("corrupt patch for subproject %s"), path); + die(_("corrupt patch for submodule %s"), path); } else { if (!cached) { if (lstat(path, &st) < 0) diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 0e64b4159c..163ce6c77c 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -150,7 +150,9 @@ static void expand_atom(struct strbuf *sb, const char *atom, int len, if (!data->mark_query) strbuf_addstr(sb, sha1_to_hex(data->sha1)); } else if (is_atom("objecttype", atom, len)) { - if (!data->mark_query) + if (data->mark_query) + data->info.typep = &data->type; + else strbuf_addstr(sb, typename(data->type)); } else if (is_atom("objectsize", atom, len)) { if (data->mark_query) @@ -229,8 +231,7 @@ static int batch_one_object(const char *obj_name, struct batch_options *opt, return 0; } - data->type = sha1_object_info_extended(data->sha1, &data->info); - if (data->type <= 0) { + if (sha1_object_info_extended(data->sha1, &data->info) < 0) { printf("%s missing\n", obj_name); fflush(stdout); return 0; @@ -266,6 +267,15 @@ static int batch_objects(struct batch_options *opt) strbuf_expand(&buf, opt->format, expand_format, &data); data.mark_query = 0; + /* + * We are going to call get_sha1 on a potentially very large number of + * objects. In most large cases, these will be actual object sha1s. The + * cost to double-check that each one is not also a ref (just so we can + * warn) ends up dwarfing the actual cost of the object lookups + * themselves. We can work around it by just turning off the warning. + */ + warn_on_object_refname_ambiguity = 0; + while (strbuf_getline(&buf, stdin, '\n') != EOF) { char *p; int error; diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c new file mode 100644 index 0000000000..8f4d809bd8 --- /dev/null +++ b/builtin/check-mailmap.c @@ -0,0 +1,66 @@ +#include "builtin.h" +#include "mailmap.h" +#include "parse-options.h" +#include "string-list.h" + +static int use_stdin; +static const char * const check_mailmap_usage[] = { +N_("git check-mailmap [options] ..."), +NULL +}; + +static const struct option check_mailmap_options[] = { + OPT_BOOL(0, "stdin", &use_stdin, N_("also read contacts from stdin")), + OPT_END() +}; + +static void check_mailmap(struct string_list *mailmap, const char *contact) +{ + const char *name, *mail; + size_t namelen, maillen; + struct ident_split ident; + + if (split_ident_line(&ident, contact, strlen(contact))) + die(_("unable to parse contact: %s"), contact); + + name = ident.name_begin; + namelen = ident.name_end - ident.name_begin; + mail = ident.mail_begin; + maillen = ident.mail_end - ident.mail_begin; + + map_user(mailmap, &mail, &maillen, &name, &namelen); + + if (namelen) + printf("%.*s ", (int)namelen, name); + printf("<%.*s>\n", (int)maillen, mail); +} + +int cmd_check_mailmap(int argc, const char **argv, const char *prefix) +{ + int i; + struct string_list mailmap = STRING_LIST_INIT_NODUP; + + git_config(git_default_config, NULL); + argc = parse_options(argc, argv, prefix, check_mailmap_options, + check_mailmap_usage, 0); + if (argc == 0 && !use_stdin) + die(_("no contacts specified")); + + read_mailmap(&mailmap, NULL); + + for (i = 0; i < argc; ++i) + check_mailmap(&mailmap, argv[i]); + maybe_flush_or_die(stdout, "stdout"); + + if (use_stdin) { + struct strbuf buf = STRBUF_INIT; + while (strbuf_getline(&buf, stdin, '\n') != EOF) { + check_mailmap(&mailmap, buf.buf); + maybe_flush_or_die(stdout, "stdout"); + } + strbuf_release(&buf); + } + + clear_mailmap(&mailmap); + return 0; +} diff --git a/builtin/checkout.c b/builtin/checkout.c index 7fe0bffaf8..7025938ae3 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -97,7 +97,7 @@ static int read_tree_some(struct tree *tree, const char **pathspec) return 0; } -static int skip_same_name(struct cache_entry *ce, int pos) +static int skip_same_name(const struct cache_entry *ce, int pos) { while (++pos < active_nr && !strcmp(active_cache[pos]->name, ce->name)) @@ -105,7 +105,7 @@ static int skip_same_name(struct cache_entry *ce, int pos) return pos; } -static int check_stage(int stage, struct cache_entry *ce, int pos) +static int check_stage(int stage, const struct cache_entry *ce, int pos) { while (pos < active_nr && !strcmp(active_cache[pos]->name, ce->name)) { @@ -119,7 +119,7 @@ static int check_stage(int stage, struct cache_entry *ce, int pos) return error(_("path '%s' does not have their version"), ce->name); } -static int check_stages(unsigned stages, struct cache_entry *ce, int pos) +static int check_stages(unsigned stages, const struct cache_entry *ce, int pos) { unsigned seen = 0; const char *name = ce->name; @@ -321,7 +321,7 @@ static int checkout_paths(const struct checkout_opts *opts, /* Any unmerged paths? */ for (pos = 0; pos < active_nr; pos++) { - struct cache_entry *ce = active_cache[pos]; + const struct cache_entry *ce = active_cache[pos]; if (ce->ce_flags & CE_MATCHED) { if (!ce_stage(ce)) continue; diff --git a/builtin/clean.c b/builtin/clean.c index 04e396b17a..dba8387747 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -13,11 +13,16 @@ #include "refs.h" #include "string-list.h" #include "quote.h" +#include "column.h" +#include "color.h" static int force = -1; /* unset */ +static int interactive; +static struct string_list del_list = STRING_LIST_INIT_DUP; +static unsigned int colopts; static const char *const builtin_clean_usage[] = { - N_("git clean [-d] [-f] [-n] [-q] [-e ] [-x | -X] [--] ..."), + N_("git clean [-d] [-f] [-i] [-n] [-q] [-e ] [-x | -X] [--] ..."), NULL }; @@ -27,11 +32,112 @@ static const char *msg_skip_git_dir = N_("Skipping repository %s\n"); static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n"); static const char *msg_warn_remove_failed = N_("failed to remove %s"); +static int clean_use_color = -1; +static char clean_colors[][COLOR_MAXLEN] = { + GIT_COLOR_RESET, + GIT_COLOR_NORMAL, /* PLAIN */ + GIT_COLOR_BOLD_BLUE, /* PROMPT */ + GIT_COLOR_BOLD, /* HEADER */ + GIT_COLOR_BOLD_RED, /* HELP */ + GIT_COLOR_BOLD_RED, /* ERROR */ +}; +enum color_clean { + CLEAN_COLOR_RESET = 0, + CLEAN_COLOR_PLAIN = 1, + CLEAN_COLOR_PROMPT = 2, + CLEAN_COLOR_HEADER = 3, + CLEAN_COLOR_HELP = 4, + CLEAN_COLOR_ERROR = 5, +}; + +#define MENU_OPTS_SINGLETON 01 +#define MENU_OPTS_IMMEDIATE 02 +#define MENU_OPTS_LIST_ONLY 04 + +struct menu_opts { + const char *header; + const char *prompt; + int flags; +}; + +#define MENU_RETURN_NO_LOOP 10 + +struct menu_item { + char hotkey; + const char *title; + int selected; + int (*fn)(); +}; + +enum menu_stuff_type { + MENU_STUFF_TYPE_STRING_LIST = 1, + MENU_STUFF_TYPE_MENU_ITEM +}; + +struct menu_stuff { + enum menu_stuff_type type; + int nr; + void *stuff; +}; + +static int parse_clean_color_slot(const char *var) +{ + if (!strcasecmp(var, "reset")) + return CLEAN_COLOR_RESET; + if (!strcasecmp(var, "plain")) + return CLEAN_COLOR_PLAIN; + if (!strcasecmp(var, "prompt")) + return CLEAN_COLOR_PROMPT; + if (!strcasecmp(var, "header")) + return CLEAN_COLOR_HEADER; + if (!strcasecmp(var, "help")) + return CLEAN_COLOR_HELP; + if (!strcasecmp(var, "error")) + return CLEAN_COLOR_ERROR; + return -1; +} + static int git_clean_config(const char *var, const char *value, void *cb) { - if (!strcmp(var, "clean.requireforce")) + if (!prefixcmp(var, "column.")) + return git_column_config(var, value, "clean", &colopts); + + /* honors the color.interactive* config variables which also + applied in git-add--interactive and git-stash */ + if (!strcmp(var, "color.interactive")) { + clean_use_color = git_config_colorbool(var, value); + return 0; + } + if (!prefixcmp(var, "color.interactive.")) { + int slot = parse_clean_color_slot(var + + strlen("color.interactive.")); + if (slot < 0) + return 0; + if (!value) + return config_error_nonbool(var); + color_parse(value, var, clean_colors[slot]); + return 0; + } + + if (!strcmp(var, "clean.requireforce")) { force = !git_config_bool(var, value); - return git_default_config(var, value, cb); + return 0; + } + + /* inspect the color.ui config variable and others */ + return git_color_default_config(var, value, cb); +} + +static const char *clean_get_color(enum color_clean ix) +{ + if (want_color(clean_use_color)) + return clean_colors[ix]; + return ""; +} + +static void clean_print_color(enum color_clean ix) +{ + printf("%s", clean_get_color(ix)); } static int exclude_cb(const struct option *opt, const char *arg, int unset) @@ -56,7 +162,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) { if (!quiet) { - quote_path_relative(path->buf, strlen(path->buf), "ed, prefix); + quote_path_relative(path->buf, prefix, "ed); printf(dry_run ? _(msg_would_skip_git_dir) : _(msg_skip_git_dir), quoted.buf); } @@ -70,7 +176,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, /* an empty dir could be removed even if it is unreadble */ res = dry_run ? 0 : rmdir(path->buf); if (res) { - quote_path_relative(path->buf, strlen(path->buf), "ed, prefix); + quote_path_relative(path->buf, prefix, "ed); warning(_(msg_warn_remove_failed), quoted.buf); *dir_gone = 0; } @@ -94,7 +200,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone)) ret = 1; if (gone) { - quote_path_relative(path->buf, strlen(path->buf), "ed, prefix); + quote_path_relative(path->buf, prefix, "ed); string_list_append(&dels, quoted.buf); } else *dir_gone = 0; @@ -102,10 +208,10 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, } else { res = dry_run ? 0 : unlink(path->buf); if (!res) { - quote_path_relative(path->buf, strlen(path->buf), "ed, prefix); + quote_path_relative(path->buf, prefix, "ed); string_list_append(&dels, quoted.buf); } else { - quote_path_relative(path->buf, strlen(path->buf), "ed, prefix); + quote_path_relative(path->buf, prefix, "ed); warning(_(msg_warn_remove_failed), quoted.buf); *dir_gone = 0; ret = 1; @@ -127,7 +233,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, if (!res) *dir_gone = 1; else { - quote_path_relative(path->buf, strlen(path->buf), "ed, prefix); + quote_path_relative(path->buf, prefix, "ed); warning(_(msg_warn_remove_failed), quoted.buf); *dir_gone = 0; ret = 1; @@ -142,24 +248,609 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag, return ret; } +static void pretty_print_dels(void) +{ + struct string_list list = STRING_LIST_INIT_DUP; + struct string_list_item *item; + struct strbuf buf = STRBUF_INIT; + const char *qname; + struct column_options copts; + + for_each_string_list_item(item, &del_list) { + qname = quote_path_relative(item->string, NULL, &buf); + string_list_append(&list, qname); + } + + /* + * always enable column display, we only consult column.* + * about layout strategy and stuff + */ + colopts = (colopts & ~COL_ENABLE_MASK) | COL_ENABLED; + memset(&copts, 0, sizeof(copts)); + copts.indent = " "; + copts.padding = 2; + print_columns(&list, colopts, &copts); + strbuf_release(&buf); + string_list_clear(&list, 0); +} + +static void pretty_print_menus(struct string_list *menu_list) +{ + unsigned int local_colopts = 0; + struct column_options copts; + + local_colopts = COL_ENABLED | COL_ROW; + memset(&copts, 0, sizeof(copts)); + copts.indent = " "; + copts.padding = 2; + print_columns(menu_list, local_colopts, &copts); +} + +static void prompt_help_cmd(int singleton) +{ + clean_print_color(CLEAN_COLOR_HELP); + printf_ln(singleton ? + _("Prompt help:\n" + "1 - select a numbered item\n" + "foo - select item based on unique prefix\n" + " - (empty) select nothing") : + _("Prompt help:\n" + "1 - select a single item\n" + "3-5 - select a range of items\n" + "2-3,6-9 - select multiple ranges\n" + "foo - select item based on unique prefix\n" + "-... - unselect specified items\n" + "* - choose all items\n" + " - (empty) finish selecting")); + clean_print_color(CLEAN_COLOR_RESET); +} + +/* + * display menu stuff with number prefix and hotkey highlight + */ +static void print_highlight_menu_stuff(struct menu_stuff *stuff, int **chosen) +{ + struct string_list menu_list = STRING_LIST_INIT_DUP; + struct strbuf menu = STRBUF_INIT; + struct strbuf buf = STRBUF_INIT; + struct menu_item *menu_item; + struct string_list_item *string_list_item; + int i; + + switch (stuff->type) { + default: + die("Bad type of menu_staff when print menu"); + case MENU_STUFF_TYPE_MENU_ITEM: + menu_item = (struct menu_item *)stuff->stuff; + for (i = 0; i < stuff->nr; i++, menu_item++) { + const char *p; + int highlighted = 0; + + p = menu_item->title; + if ((*chosen)[i] < 0) + (*chosen)[i] = menu_item->selected ? 1 : 0; + strbuf_addf(&menu, "%s%2d: ", (*chosen)[i] ? "*" : " ", i+1); + for (; *p; p++) { + if (!highlighted && *p == menu_item->hotkey) { + strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_PROMPT)); + strbuf_addch(&menu, *p); + strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_RESET)); + highlighted = 1; + } else { + strbuf_addch(&menu, *p); + } + } + string_list_append(&menu_list, menu.buf); + strbuf_reset(&menu); + } + break; + case MENU_STUFF_TYPE_STRING_LIST: + i = 0; + for_each_string_list_item(string_list_item, (struct string_list *)stuff->stuff) { + if ((*chosen)[i] < 0) + (*chosen)[i] = 0; + strbuf_addf(&menu, "%s%2d: %s", + (*chosen)[i] ? "*" : " ", i+1, string_list_item->string); + string_list_append(&menu_list, menu.buf); + strbuf_reset(&menu); + i++; + } + break; + } + + pretty_print_menus(&menu_list); + + strbuf_release(&menu); + strbuf_release(&buf); + string_list_clear(&menu_list, 0); +} + +/* + * Parse user input, and return choice(s) for menu (menu_stuff). + * + * Input + * (for single choice) + * 1 - select a numbered item + * foo - select item based on menu title + * - (empty) select nothing + * + * (for multiple choice) + * 1 - select a single item + * 3-5 - select a range of items + * 2-3,6-9 - select multiple ranges + * foo - select item based on menu title + * -... - unselect specified items + * * - choose all items + * - (empty) finish selecting + * + * The parse result will be saved in array **chosen, and + * return number of total selections. + */ +static int parse_choice(struct menu_stuff *menu_stuff, + int is_single, + struct strbuf input, + int **chosen) +{ + struct strbuf **choice_list, **ptr; + struct menu_item *menu_item; + struct string_list_item *string_list_item; + int nr = 0; + int i; + + if (is_single) { + choice_list = strbuf_split_max(&input, '\n', 0); + } else { + char *p = input.buf; + do { + if (*p == ',') + *p = ' '; + } while (*p++); + choice_list = strbuf_split_max(&input, ' ', 0); + } + + for (ptr = choice_list; *ptr; ptr++) { + char *p; + int choose = 1; + int bottom = 0, top = 0; + int is_range, is_number; + + strbuf_trim(*ptr); + if (!(*ptr)->len) + continue; + + /* Input that begins with '-'; unchoose */ + if (*(*ptr)->buf == '-') { + choose = 0; + strbuf_remove((*ptr), 0, 1); + } + + is_range = 0; + is_number = 1; + for (p = (*ptr)->buf; *p; p++) { + if ('-' == *p) { + if (!is_range) { + is_range = 1; + is_number = 0; + } else { + is_number = 0; + is_range = 0; + break; + } + } else if (!isdigit(*p)) { + is_number = 0; + is_range = 0; + break; + } + } + + if (is_number) { + bottom = atoi((*ptr)->buf); + top = bottom; + } else if (is_range) { + bottom = atoi((*ptr)->buf); + /* a range can be specified like 5-7 or 5- */ + if (!*(strchr((*ptr)->buf, '-') + 1)) + top = menu_stuff->nr; + else + top = atoi(strchr((*ptr)->buf, '-') + 1); + } else if (!strcmp((*ptr)->buf, "*")) { + bottom = 1; + top = menu_stuff->nr; + } else { + switch (menu_stuff->type) { + default: + die("Bad type of menu_stuff when parse choice"); + case MENU_STUFF_TYPE_MENU_ITEM: + menu_item = (struct menu_item *)menu_stuff->stuff; + for (i = 0; i < menu_stuff->nr; i++, menu_item++) { + if (((*ptr)->len == 1 && + *(*ptr)->buf == menu_item->hotkey) || + !strcasecmp((*ptr)->buf, menu_item->title)) { + bottom = i + 1; + top = bottom; + break; + } + } + break; + case MENU_STUFF_TYPE_STRING_LIST: + string_list_item = ((struct string_list *)menu_stuff->stuff)->items; + for (i = 0; i < menu_stuff->nr; i++, string_list_item++) { + if (!strcasecmp((*ptr)->buf, string_list_item->string)) { + bottom = i + 1; + top = bottom; + break; + } + } + break; + } + } + + if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > top || + (is_single && bottom != top)) { + clean_print_color(CLEAN_COLOR_ERROR); + printf_ln(_("Huh (%s)?"), (*ptr)->buf); + clean_print_color(CLEAN_COLOR_RESET); + continue; + } + + for (i = bottom; i <= top; i++) + (*chosen)[i-1] = choose; + } + + strbuf_list_free(choice_list); + + for (i = 0; i < menu_stuff->nr; i++) + nr += (*chosen)[i]; + return nr; +} + +/* + * Implement a git-add-interactive compatible UI, which is borrowed + * from git-add--interactive.perl. + * + * Return value: + * + * - Return an array of integers + * - , and it is up to you to free the allocated memory. + * - The array ends with EOF. + * - If user pressed CTRL-D (i.e. EOF), no selection returned. + */ +static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff) +{ + struct strbuf choice = STRBUF_INIT; + int *chosen, *result; + int nr = 0; + int eof = 0; + int i; + + chosen = xmalloc(sizeof(int) * stuff->nr); + /* set chosen as uninitialized */ + for (i = 0; i < stuff->nr; i++) + chosen[i] = -1; + + for (;;) { + if (opts->header) { + printf_ln("%s%s%s", + clean_get_color(CLEAN_COLOR_HEADER), + _(opts->header), + clean_get_color(CLEAN_COLOR_RESET)); + } + + /* chosen will be initialized by print_highlight_menu_stuff */ + print_highlight_menu_stuff(stuff, &chosen); + + if (opts->flags & MENU_OPTS_LIST_ONLY) + break; + + if (opts->prompt) { + printf("%s%s%s%s", + clean_get_color(CLEAN_COLOR_PROMPT), + _(opts->prompt), + opts->flags & MENU_OPTS_SINGLETON ? "> " : ">> ", + clean_get_color(CLEAN_COLOR_RESET)); + } + + if (strbuf_getline(&choice, stdin, '\n') != EOF) { + strbuf_trim(&choice); + } else { + eof = 1; + break; + } + + /* help for prompt */ + if (!strcmp(choice.buf, "?")) { + prompt_help_cmd(opts->flags & MENU_OPTS_SINGLETON); + continue; + } + + /* for a multiple-choice menu, press ENTER (empty) will return back */ + if (!(opts->flags & MENU_OPTS_SINGLETON) && !choice.len) + break; + + nr = parse_choice(stuff, + opts->flags & MENU_OPTS_SINGLETON, + choice, + &chosen); + + if (opts->flags & MENU_OPTS_SINGLETON) { + if (nr) + break; + } else if (opts->flags & MENU_OPTS_IMMEDIATE) { + break; + } + } + + if (eof) { + result = xmalloc(sizeof(int)); + *result = EOF; + } else { + int j = 0; + + /* + * recalculate nr, if return back from menu directly with + * default selections. + */ + if (!nr) { + for (i = 0; i < stuff->nr; i++) + nr += chosen[i]; + } + + result = xmalloc(sizeof(int) * (nr + 1)); + memset(result, 0, sizeof(int) * (nr + 1)); + for (i = 0; i < stuff->nr && j < nr; i++) { + if (chosen[i]) + result[j++] = i; + } + result[j] = EOF; + } + + free(chosen); + strbuf_release(&choice); + return result; +} + +static int clean_cmd(void) +{ + return MENU_RETURN_NO_LOOP; +} + +static int filter_by_patterns_cmd(void) +{ + struct dir_struct dir; + struct strbuf confirm = STRBUF_INIT; + struct strbuf **ignore_list; + struct string_list_item *item; + struct exclude_list *el; + int changed = -1, i; + + for (;;) { + if (!del_list.nr) + break; + + if (changed) + pretty_print_dels(); + + clean_print_color(CLEAN_COLOR_PROMPT); + printf(_("Input ignore patterns>> ")); + clean_print_color(CLEAN_COLOR_RESET); + if (strbuf_getline(&confirm, stdin, '\n') != EOF) + strbuf_trim(&confirm); + else + putchar('\n'); + + /* quit filter_by_pattern mode if press ENTER or Ctrl-D */ + if (!confirm.len) + break; + + memset(&dir, 0, sizeof(dir)); + el = add_exclude_list(&dir, EXC_CMDL, "manual exclude"); + ignore_list = strbuf_split_max(&confirm, ' ', 0); + + for (i = 0; ignore_list[i]; i++) { + strbuf_trim(ignore_list[i]); + if (!ignore_list[i]->len) + continue; + + add_exclude(ignore_list[i]->buf, "", 0, el, -(i+1)); + } + + changed = 0; + for_each_string_list_item(item, &del_list) { + int dtype = DT_UNKNOWN; + + if (is_excluded(&dir, item->string, &dtype)) { + *item->string = '\0'; + changed++; + } + } + + if (changed) { + string_list_remove_empty_items(&del_list, 0); + } else { + clean_print_color(CLEAN_COLOR_ERROR); + printf_ln(_("WARNING: Cannot find items matched by: %s"), confirm.buf); + clean_print_color(CLEAN_COLOR_RESET); + } + + strbuf_list_free(ignore_list); + clear_directory(&dir); + } + + strbuf_release(&confirm); + return 0; +} + +static int select_by_numbers_cmd(void) +{ + struct menu_opts menu_opts; + struct menu_stuff menu_stuff; + struct string_list_item *items; + int *chosen; + int i, j; + + menu_opts.header = NULL; + menu_opts.prompt = N_("Select items to delete"); + menu_opts.flags = 0; + + menu_stuff.type = MENU_STUFF_TYPE_STRING_LIST; + menu_stuff.stuff = &del_list; + menu_stuff.nr = del_list.nr; + + chosen = list_and_choose(&menu_opts, &menu_stuff); + items = del_list.items; + for (i = 0, j = 0; i < del_list.nr; i++) { + if (i < chosen[j]) { + *(items[i].string) = '\0'; + } else if (i == chosen[j]) { + /* delete selected item */ + j++; + continue; + } else { + /* end of chosen (chosen[j] == EOF), won't delete */ + *(items[i].string) = '\0'; + } + } + + string_list_remove_empty_items(&del_list, 0); + + free(chosen); + return 0; +} + +static int ask_each_cmd(void) +{ + struct strbuf confirm = STRBUF_INIT; + struct strbuf buf = STRBUF_INIT; + struct string_list_item *item; + const char *qname; + int changed = 0, eof = 0; + + for_each_string_list_item(item, &del_list) { + /* Ctrl-D should stop removing files */ + if (!eof) { + qname = quote_path_relative(item->string, NULL, &buf); + printf(_("remove %s? "), qname); + if (strbuf_getline(&confirm, stdin, '\n') != EOF) { + strbuf_trim(&confirm); + } else { + putchar('\n'); + eof = 1; + } + } + if (!confirm.len || strncasecmp(confirm.buf, "yes", confirm.len)) { + *item->string = '\0'; + changed++; + } + } + + if (changed) + string_list_remove_empty_items(&del_list, 0); + + strbuf_release(&buf); + strbuf_release(&confirm); + return MENU_RETURN_NO_LOOP; +} + +static int quit_cmd(void) +{ + string_list_clear(&del_list, 0); + printf_ln(_("Bye.")); + return MENU_RETURN_NO_LOOP; +} + +static int help_cmd(void) +{ + clean_print_color(CLEAN_COLOR_HELP); + printf_ln(_( + "clean - start cleaning\n" + "filter by pattern - exclude items from deletion\n" + "select by numbers - select items to be deleted by numbers\n" + "ask each - confirm each deletion (like \"rm -i\")\n" + "quit - stop cleaning\n" + "help - this screen\n" + "? - help for prompt selection" + )); + clean_print_color(CLEAN_COLOR_RESET); + return 0; +} + +static void interactive_main_loop(void) +{ + while (del_list.nr) { + struct menu_opts menu_opts; + struct menu_stuff menu_stuff; + struct menu_item menus[] = { + {'c', "clean", 0, clean_cmd}, + {'f', "filter by pattern", 0, filter_by_patterns_cmd}, + {'s', "select by numbers", 0, select_by_numbers_cmd}, + {'a', "ask each", 0, ask_each_cmd}, + {'q', "quit", 0, quit_cmd}, + {'h', "help", 0, help_cmd}, + }; + int *chosen; + + menu_opts.header = N_("*** Commands ***"); + menu_opts.prompt = N_("What now"); + menu_opts.flags = MENU_OPTS_SINGLETON; + + menu_stuff.type = MENU_STUFF_TYPE_MENU_ITEM; + menu_stuff.stuff = menus; + menu_stuff.nr = sizeof(menus) / sizeof(struct menu_item); + + clean_print_color(CLEAN_COLOR_HEADER); + printf_ln(Q_("Would remove the following item:", + "Would remove the following items:", + del_list.nr)); + clean_print_color(CLEAN_COLOR_RESET); + + pretty_print_dels(); + + chosen = list_and_choose(&menu_opts, &menu_stuff); + + if (*chosen != EOF) { + int ret; + ret = menus[*chosen].fn(); + if (ret != MENU_RETURN_NO_LOOP) { + free(chosen); + chosen = NULL; + if (!del_list.nr) { + clean_print_color(CLEAN_COLOR_ERROR); + printf_ln(_("No more files to clean, exiting.")); + clean_print_color(CLEAN_COLOR_RESET); + break; + } + continue; + } + } else { + quit_cmd(); + } + + free(chosen); + chosen = NULL; + break; + } +} + int cmd_clean(int argc, const char **argv, const char *prefix) { int i, res; int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0; int ignored_only = 0, config_set = 0, errors = 0, gone = 1; int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT; - struct strbuf directory = STRBUF_INIT; + struct strbuf abs_path = STRBUF_INIT; struct dir_struct dir; static const char **pathspec; struct strbuf buf = STRBUF_INIT; struct string_list exclude_list = STRING_LIST_INIT_NODUP; struct exclude_list *el; + struct string_list_item *item; const char *qname; char *seen = NULL; struct option options[] = { OPT__QUIET(&quiet, N_("do not print names of files removed")), OPT__DRY_RUN(&dry_run, N_("dry run")), OPT__FORCE(&force, N_("force")), + OPT_BOOL('i', "interactive", &interactive, N_("interactive cleaning")), OPT_BOOLEAN('d', NULL, &remove_directories, N_("remove whole directories")), { OPTION_CALLBACK, 'e', "exclude", &exclude_list, N_("pattern"), @@ -186,12 +877,12 @@ int cmd_clean(int argc, const char **argv, const char *prefix) if (ignored && ignored_only) die(_("-x and -X cannot be used together")); - if (!dry_run && !force) { + if (!interactive && !dry_run && !force) { if (config_set) - die(_("clean.requireForce set to true and neither -n nor -f given; " + die(_("clean.requireForce set to true and neither -i, -n nor -f given; " "refusing to clean")); else - die(_("clean.requireForce defaults to true and neither -n nor -f given; " + die(_("clean.requireForce defaults to true and neither -i, -n nor -f given; " "refusing to clean")); } @@ -221,8 +912,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix) struct dir_entry *ent = dir.entries[i]; int len, pos; int matches = 0; - struct cache_entry *ce; + const struct cache_entry *ce; struct stat st; + const char *rel; /* * Remove the '/' at the end that directory @@ -242,13 +934,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix) continue; /* Yup, this one exists unmerged */ } - /* - * we might have removed this as part of earlier - * recursive directory removal, so lstat() here could - * fail with ENOENT. - */ if (lstat(ent->name, &st)) - continue; + die_errno("Cannot lstat '%s'", ent->name); if (pathspec) { memset(seen, 0, argc > 0 ? argc : 1); @@ -257,33 +944,62 @@ int cmd_clean(int argc, const char **argv, const char *prefix) } if (S_ISDIR(st.st_mode)) { - strbuf_addstr(&directory, ent->name); if (remove_directories || (matches == MATCHED_EXACTLY)) { - if (remove_dirs(&directory, prefix, rm_flags, dry_run, quiet, &gone)) - errors++; - if (gone && !quiet) { - qname = quote_path_relative(directory.buf, directory.len, &buf, prefix); - printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname); - } + rel = relative_path(ent->name, prefix, &buf); + string_list_append(&del_list, rel); } - strbuf_reset(&directory); } else { if (pathspec && !matches) continue; - res = dry_run ? 0 : unlink(ent->name); + rel = relative_path(ent->name, prefix, &buf); + string_list_append(&del_list, rel); + } + } + + if (interactive && del_list.nr > 0) + interactive_main_loop(); + + for_each_string_list_item(item, &del_list) { + struct stat st; + + if (prefix) + strbuf_addstr(&abs_path, prefix); + + strbuf_addstr(&abs_path, item->string); + + /* + * we might have removed this as part of earlier + * recursive directory removal, so lstat() here could + * fail with ENOENT. + */ + if (lstat(abs_path.buf, &st)) + continue; + + if (S_ISDIR(st.st_mode)) { + if (remove_dirs(&abs_path, prefix, rm_flags, dry_run, quiet, &gone)) + errors++; + if (gone && !quiet) { + qname = quote_path_relative(item->string, NULL, &buf); + printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname); + } + } else { + res = dry_run ? 0 : unlink(abs_path.buf); if (res) { - qname = quote_path_relative(ent->name, -1, &buf, prefix); + qname = quote_path_relative(item->string, NULL, &buf); warning(_(msg_warn_remove_failed), qname); errors++; } else if (!quiet) { - qname = quote_path_relative(ent->name, -1, &buf, prefix); + qname = quote_path_relative(item->string, NULL, &buf); printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname); } } + strbuf_reset(&abs_path); } free(seen); - strbuf_release(&directory); + strbuf_release(&abs_path); + strbuf_release(&buf); + string_list_clear(&del_list, 0); string_list_clear(&exclude_list, 0); return (errors != 0); } diff --git a/builtin/commit.c b/builtin/commit.c index 790e5ab5cf..003bd7dbec 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -207,7 +207,7 @@ static int list_paths(struct string_list *list, const char *with_tree, } for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; + const struct cache_entry *ce = active_cache[i]; struct string_list_item *item; if (ce->ce_flags & CE_UPDATE) @@ -534,7 +534,6 @@ static void determine_author_info(struct strbuf *author_ident) (lb - strlen(" ") - (a + strlen("\nauthor ")))); email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<"))); - date = xmemdupz(rb + strlen("> "), eol - (rb + strlen("> "))); len = eol - (rb + strlen("> ")); date = xmalloc(len + 2); *date = '@'; diff --git a/builtin/config.c b/builtin/config.c index 7759671eb8..4010c4320a 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -21,6 +21,7 @@ static char term = '\n'; static int use_global_config, use_system_config, use_local_config; static const char *given_config_file; +static const char *given_config_blob; static int actions, types; static const char *get_color_slot, *get_colorbool_slot; static int end_null; @@ -53,6 +54,7 @@ static struct option builtin_config_options[] = { OPT_BOOLEAN(0, "system", &use_system_config, N_("use system config file")), OPT_BOOLEAN(0, "local", &use_local_config, N_("use repository config file")), OPT_STRING('f', "file", &given_config_file, N_("file"), N_("use given config file")), + OPT_STRING(0, "blob", &given_config_blob, N_("blob-id"), N_("read config from given blob object")), OPT_GROUP(N_("Action")), OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET), OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL), @@ -218,7 +220,8 @@ static int get_value(const char *key_, const char *regex_) } git_config_with_options(collect_config, &values, - given_config_file, respect_includes); + given_config_file, given_config_blob, + respect_includes); ret = !values.nr; @@ -302,7 +305,8 @@ static void get_color(const char *def_color) get_color_found = 0; parsed_color[0] = '\0'; git_config_with_options(git_get_color_config, NULL, - given_config_file, respect_includes); + given_config_file, given_config_blob, + respect_includes); if (!get_color_found && def_color) color_parse(def_color, "command line", parsed_color); @@ -331,7 +335,8 @@ static int get_colorbool(int print) get_diff_color_found = -1; get_color_ui_found = -1; git_config_with_options(git_get_colorbool_config, NULL, - given_config_file, respect_includes); + given_config_file, given_config_blob, + respect_includes); if (get_colorbool_found < 0) { if (!strcmp(get_colorbool_slot, "color.diff")) @@ -353,6 +358,12 @@ static int get_colorbool(int print) return get_colorbool_found ? 0 : 1; } +static void check_blob_write(void) +{ + if (given_config_blob) + die("writing config blobs is not supported"); +} + int cmd_config(int argc, const char **argv, const char *prefix) { int nongit = !startup_info->have_repository; @@ -364,7 +375,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) builtin_config_usage, PARSE_OPT_STOP_AT_NON_OPTION); - if (use_global_config + use_system_config + use_local_config + !!given_config_file > 1) { + if (use_global_config + use_system_config + use_local_config + + !!given_config_file + !!given_config_blob > 1) { error("only one config file at a time."); usage_with_options(builtin_config_usage, builtin_config_options); } @@ -443,6 +455,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) check_argc(argc, 0, 0); if (git_config_with_options(show_all_config, NULL, given_config_file, + given_config_blob, respect_includes) < 0) { if (given_config_file) die_errno("unable to read config file '%s'", @@ -455,6 +468,8 @@ int cmd_config(int argc, const char **argv, const char *prefix) check_argc(argc, 0, 0); if (!given_config_file && nongit) die("not in a git directory"); + if (given_config_blob) + die("editing blobs is not supported"); git_config(git_default_config, NULL); launch_editor(given_config_file ? given_config_file : git_path("config"), @@ -462,6 +477,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) } else if (actions == ACTION_SET) { int ret; + check_blob_write(); check_argc(argc, 2, 2); value = normalize_value(argv[0], argv[1]); ret = git_config_set_in_file(given_config_file, argv[0], value); @@ -471,18 +487,21 @@ int cmd_config(int argc, const char **argv, const char *prefix) return ret; } else if (actions == ACTION_SET_ALL) { + check_blob_write(); check_argc(argc, 2, 3); value = normalize_value(argv[0], argv[1]); return git_config_set_multivar_in_file(given_config_file, argv[0], value, argv[2], 0); } else if (actions == ACTION_ADD) { + check_blob_write(); check_argc(argc, 2, 2); value = normalize_value(argv[0], argv[1]); return git_config_set_multivar_in_file(given_config_file, argv[0], value, "^$", 0); } else if (actions == ACTION_REPLACE_ALL) { + check_blob_write(); check_argc(argc, 2, 3); value = normalize_value(argv[0], argv[1]); return git_config_set_multivar_in_file(given_config_file, @@ -505,6 +524,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) return get_value(argv[0], argv[1]); } else if (actions == ACTION_UNSET) { + check_blob_write(); check_argc(argc, 1, 2); if (argc == 2) return git_config_set_multivar_in_file(given_config_file, @@ -514,12 +534,14 @@ int cmd_config(int argc, const char **argv, const char *prefix) argv[0], NULL); } else if (actions == ACTION_UNSET_ALL) { + check_blob_write(); check_argc(argc, 1, 2); return git_config_set_multivar_in_file(given_config_file, argv[0], NULL, argv[1], 1); } else if (actions == ACTION_RENAME_SECTION) { int ret; + check_blob_write(); check_argc(argc, 2, 2); ret = git_config_rename_section_in_file(given_config_file, argv[0], argv[1]); @@ -530,6 +552,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) } else if (actions == ACTION_REMOVE_SECTION) { int ret; + check_blob_write(); check_argc(argc, 1, 1); ret = git_config_rename_section_in_file(given_config_file, argv[0], NULL); diff --git a/builtin/describe.c b/builtin/describe.c index 4e675c3d0d..7d73722f59 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -7,6 +7,7 @@ #include "parse-options.h" #include "diff.h" #include "hash.h" +#include "argv-array.h" #define SEEN (1u<<0) #define MAX_TAGS (FLAG_BITS - 1) @@ -442,24 +443,24 @@ int cmd_describe(int argc, const char **argv, const char *prefix) die(_("--long is incompatible with --abbrev=0")); if (contains) { - const char **args = xmalloc((7 + argc) * sizeof(char *)); - int i = 0; - args[i++] = "name-rev"; - args[i++] = "--name-only"; - args[i++] = "--no-undefined"; + struct argv_array args; + + argv_array_init(&args); + argv_array_pushl(&args, "name-rev", + "--peel-tag", "--name-only", "--no-undefined", + NULL); if (always) - args[i++] = "--always"; + argv_array_push(&args, "--always"); if (!all) { - args[i++] = "--tags"; - if (pattern) { - char *s = xmalloc(strlen("--refs=refs/tags/") + strlen(pattern) + 1); - sprintf(s, "--refs=refs/tags/%s", pattern); - args[i++] = s; - } + argv_array_push(&args, "--tags"); + if (pattern) + argv_array_pushf(&args, "--refs=refs/tags/%s", pattern); + } + while (*argv) { + argv_array_push(&args, *argv); + argv++; } - memcpy(args + i, argv, argc * sizeof(char *)); - args[i + argc] = NULL; - return cmd_name_rev(i + argc, args, prefix); + return cmd_name_rev(args.argc, args.argv, prefix); } init_hash(&names); diff --git a/builtin/grep.c b/builtin/grep.c index 159e65d47a..d3b3b1db11 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -286,8 +286,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, struct strbuf pathbuf = STRBUF_INIT; if (opt->relative && opt->prefix_length) { - quote_path_relative(filename + tree_name_len, -1, &pathbuf, - opt->prefix); + quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf); strbuf_insert(&pathbuf, 0, filename, tree_name_len); } else { strbuf_addstr(&pathbuf, filename); @@ -318,7 +317,7 @@ static int grep_file(struct grep_opt *opt, const char *filename) struct strbuf buf = STRBUF_INIT; if (opt->relative && opt->prefix_length) - quote_path_relative(filename, -1, &buf, opt->prefix); + quote_path_relative(filename, opt->prefix, &buf); else strbuf_addstr(&buf, filename); @@ -376,7 +375,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int read_cache(); for (nr = 0; nr < active_nr; nr++) { - struct cache_entry *ce = active_cache[nr]; + const struct cache_entry *ce = active_cache[nr]; if (!S_ISREG(ce->ce_mode)) continue; if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL)) diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 3a410c35d9..5cf3e31370 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -46,10 +46,14 @@ static const char *tag_modified = ""; static const char *tag_skip_worktree = ""; static const char *tag_resolve_undo = ""; -static void write_name(const char* name, size_t len) +static void write_name(const char *name) { - write_name_quoted_relative(name, len, prefix, prefix_len, stdout, - line_terminator); + /* + * With "--full-name", prefix_len=0; this caller needs to pass + * an empty string in that case (a NULL is good for ""). + */ + write_name_quoted_relative(name, prefix_len ? prefix : NULL, + stdout, line_terminator); } static void show_dir_entry(const char *tag, struct dir_entry *ent) @@ -63,7 +67,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent) return; fputs(tag, stdout); - write_name(ent->name, ent->len); + write_name(ent->name); } static void show_other_files(struct dir_struct *dir) @@ -127,7 +131,7 @@ static void show_killed_files(struct dir_struct *dir) } } -static void show_ce_entry(const char *tag, struct cache_entry *ce) +static void show_ce_entry(const char *tag, const struct cache_entry *ce) { int len = max_prefix_len; @@ -163,9 +167,9 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce) find_unique_abbrev(ce->sha1,abbrev), ce_stage(ce)); } - write_name(ce->name, ce_namelen(ce)); + write_name(ce->name); if (debug_mode) { - struct stat_data *sd = &ce->ce_stat_data; + const struct stat_data *sd = &ce->ce_stat_data; printf(" ctime: %d:%d\n", sd->sd_ctime.sec, sd->sd_ctime.nsec); printf(" mtime: %d:%d\n", sd->sd_mtime.sec, sd->sd_mtime.nsec); @@ -198,12 +202,12 @@ static void show_ru_info(void) printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i], find_unique_abbrev(ui->sha1[i], abbrev), i + 1); - write_name(path, len); + write_name(path); } } } -static int ce_excluded(struct dir_struct *dir, struct cache_entry *ce) +static int ce_excluded(struct dir_struct *dir, const struct cache_entry *ce) { int dtype = ce_to_dtype(ce); return is_excluded(dir, ce->name, &dtype); @@ -223,7 +227,7 @@ static void show_files(struct dir_struct *dir) } if (show_cached || show_stage) { for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; + const struct cache_entry *ce = active_cache[i]; if ((dir->flags & DIR_SHOW_IGNORED) && !ce_excluded(dir, ce)) continue; @@ -237,7 +241,7 @@ static void show_files(struct dir_struct *dir) } if (show_deleted || show_modified) { for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; + const struct cache_entry *ce = active_cache[i]; struct stat st; int err; if ((dir->flags & DIR_SHOW_IGNORED) && @@ -273,7 +277,7 @@ static void prune_cache(const char *prefix) last = active_nr; while (last > first) { int next = (last + first) >> 1; - struct cache_entry *ce = active_cache[next]; + const struct cache_entry *ce = active_cache[next]; if (!strncmp(ce->name, prefix, max_prefix_len)) { first = next+1; continue; @@ -391,7 +395,7 @@ int report_path_error(const char *ps_matched, const char **pathspec, const char if (found_dup) continue; - name = quote_path_relative(pathspec[num], -1, &sb, prefix); + name = quote_path_relative(pathspec[num], prefix, &sb); error("pathspec '%s' did not match any file(s) known to git.", name); errors++; diff --git a/builtin/merge-index.c b/builtin/merge-index.c index be5e514324..b416d92849 100644 --- a/builtin/merge-index.c +++ b/builtin/merge-index.c @@ -16,7 +16,7 @@ static int merge_entry(int pos, const char *path) die("git merge-index: %s not in the cache", path); found = 0; do { - struct cache_entry *ce = active_cache[pos]; + const struct cache_entry *ce = active_cache[pos]; int stage = ce_stage(ce); if (strcmp(ce->name, path)) @@ -58,7 +58,7 @@ static void merge_all(void) { int i; for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; + const struct cache_entry *ce = active_cache[i]; if (!ce_stage(ce)) continue; i += merge_entry(i, ce->name)-1; diff --git a/builtin/merge.c b/builtin/merge.c index 9505e7e426..34a6166b52 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -903,7 +903,7 @@ static int suggest_conflicts(int renormalizing) die_errno(_("Could not open '%s' for writing"), filename); fprintf(fp, "\nConflicts:\n"); for (pos = 0; pos < active_nr; pos++) { - struct cache_entry *ce = active_cache[pos]; + const struct cache_entry *ce = active_cache[pos]; if (ce_stage(ce)) { fprintf(fp, "\t%s\n", ce->name); diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 87d485496f..0aaa19e4ab 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -4,6 +4,7 @@ #include "tag.h" #include "refs.h" #include "parse-options.h" +#include "sha1-lookup.h" #define CUTOFF_DATE_SLOP 86400 /* one day */ @@ -96,12 +97,51 @@ static int subpath_matches(const char *path, const char *filter) return -1; } +static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous) +{ + if (shorten_unambiguous) + refname = shorten_unambiguous_ref(refname, 0); + else if (!prefixcmp(refname, "refs/heads/")) + refname = refname + 11; + else if (!prefixcmp(refname, "refs/")) + refname = refname + 5; + return refname; +} + struct name_ref_data { int tags_only; int name_only; const char *ref_filter; }; +static struct tip_table { + struct tip_table_entry { + unsigned char sha1[20]; + const char *refname; + } *table; + int nr; + int alloc; + int sorted; +} tip_table; + +static void add_to_tip_table(const unsigned char *sha1, const char *refname, + int shorten_unambiguous) +{ + refname = name_ref_abbrev(refname, shorten_unambiguous); + + ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc); + hashcpy(tip_table.table[tip_table.nr].sha1, sha1); + tip_table.table[tip_table.nr].refname = xstrdup(refname); + tip_table.nr++; + tip_table.sorted = 0; +} + +static int tipcmp(const void *a_, const void *b_) +{ + const struct tip_table_entry *a = a_, *b = b_; + return hashcmp(a->sha1, b->sha1); +} + static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data) { struct object *o = parse_object(sha1); @@ -124,6 +164,8 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void } } + add_to_tip_table(sha1, path, can_abbreviate_output); + while (o && o->type == OBJ_TAG) { struct tag *t = (struct tag *) o; if (!t->tagged) @@ -134,18 +176,38 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void if (o && o->type == OBJ_COMMIT) { struct commit *commit = (struct commit *)o; - if (can_abbreviate_output) - path = shorten_unambiguous_ref(path, 0); - else if (!prefixcmp(path, "refs/heads/")) - path = path + 11; - else if (!prefixcmp(path, "refs/")) - path = path + 5; - + path = name_ref_abbrev(path, can_abbreviate_output); name_rev(commit, xstrdup(path), 0, 0, deref); } return 0; } +static const unsigned char *nth_tip_table_ent(size_t ix, void *table_) +{ + struct tip_table_entry *table = table_; + return table[ix].sha1; +} + +static const char *get_exact_ref_match(const struct object *o) +{ + int found; + + if (!tip_table.table || !tip_table.nr) + return NULL; + + if (!tip_table.sorted) { + qsort(tip_table.table, tip_table.nr, sizeof(*tip_table.table), + tipcmp); + tip_table.sorted = 1; + } + + found = sha1_pos(o->sha1, tip_table.table, tip_table.nr, + nth_tip_table_ent); + if (0 <= found) + return tip_table.table[found].refname; + return NULL; +} + /* returns a static buffer */ static const char *get_rev_name(const struct object *o) { @@ -154,7 +216,7 @@ static const char *get_rev_name(const struct object *o) struct commit *c; if (o->type != OBJ_COMMIT) - return NULL; + return get_exact_ref_match(o); c = (struct commit *) o; n = c->util; if (!n) @@ -245,7 +307,7 @@ static void name_rev_line(char *p, struct name_ref_data *data) int cmd_name_rev(int argc, const char **argv, const char *prefix) { struct object_array revs = OBJECT_ARRAY_INIT; - int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0; + int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0; struct name_ref_data data = { 0, 0, NULL }; struct option opts[] = { OPT_BOOLEAN(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")), @@ -258,6 +320,12 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) OPT_BOOLEAN(0, "undefined", &allow_undefined, N_("allow to print `undefined` names")), OPT_BOOLEAN(0, "always", &always, N_("show abbreviated commit object as fallback")), + { + /* A Hidden OPT_BOOL */ + OPTION_SET_INT, 0, "peel-tag", &peel_tag, NULL, + N_("dereference tags in the input (internal use)"), + PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1, + }, OPT_END(), }; @@ -272,7 +340,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) for (; argc; argc--, argv++) { unsigned char sha1[20]; - struct object *o; + struct object *object; struct commit *commit; if (get_sha1(*argv, sha1)) { @@ -281,17 +349,34 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) continue; } - o = deref_tag(parse_object(sha1), *argv, 0); - if (!o || o->type != OBJ_COMMIT) { - fprintf(stderr, "Could not get commit for %s. Skipping.\n", + commit = NULL; + object = parse_object(sha1); + if (object) { + struct object *peeled = deref_tag(object, *argv, 0); + if (peeled && peeled->type == OBJ_COMMIT) + commit = (struct commit *)peeled; + } + + if (!object) { + fprintf(stderr, "Could not get object for %s. Skipping.\n", *argv); continue; } - commit = (struct commit *)o; - if (cutoff > commit->date) - cutoff = commit->date; - add_object_array((struct object *)commit, *argv, &revs); + if (commit) { + if (cutoff > commit->date) + cutoff = commit->date; + } + + if (peel_tag) { + if (!commit) { + fprintf(stderr, "Could not get commit for %s. Skipping.\n", + *argv); + continue; + } + object = (struct object *)commit; + } + add_object_array(object, *argv, &revs); } if (cutoff) diff --git a/builtin/revert.c b/builtin/revert.c index 0401fdb02c..1d2648b756 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -54,6 +54,7 @@ static int option_parse_x(const struct option *opt, return 0; } +LAST_ARG_MUST_BE_NULL static void verify_opt_compatible(const char *me, const char *base_opt, ...) { const char *this_opt; @@ -70,6 +71,7 @@ static void verify_opt_compatible(const char *me, const char *base_opt, ...) die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt); } +LAST_ARG_MUST_BE_NULL static void verify_opt_mutually_compatible(const char *me, ...) { const char *opt1, *opt2 = NULL; diff --git a/builtin/rm.c b/builtin/rm.c index 06025a2e75..18916e022a 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -67,7 +67,7 @@ static int check_submodules_use_gitfiles(void) for (i = 0; i < list.nr; i++) { const char *name = list.entry[i].name; int pos; - struct cache_entry *ce; + const struct cache_entry *ce; struct stat st; pos = cache_name_pos(name, strlen(name)); @@ -120,7 +120,7 @@ static int check_local_mod(unsigned char *head, int index_only) for (i = 0; i < list.nr; i++) { struct stat st; int pos; - struct cache_entry *ce; + const struct cache_entry *ce; const char *name = list.entry[i].name; unsigned char sha1[20]; unsigned mode; @@ -321,7 +321,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) seen = xcalloc(i, 1); for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; + const struct cache_entry *ce = active_cache[i]; if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen)) continue; ALLOC_GROW(list.entry, list.nr + 1, list.alloc); diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 99ec4af224..9788eb115b 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -673,8 +673,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) OPT_SET_INT(0, "sparse", &dense, N_("show merges reachable from only one tip"), 0), OPT_SET_INT(0, "date-order", &sort_order, - N_("show commits where no parent comes before its " - "children"), + N_("topologically sort, maintaining date order " + "where possible"), REV_SORT_BY_COMMIT_DATE), { OPTION_CALLBACK, 'g', "reflog", &reflog_base, N_("[,]"), N_("show most recent ref-log entries starting at " diff --git a/builtin/show-ref.c b/builtin/show-ref.c index 4a0310da37..87806ad5b0 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -31,6 +31,9 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo const char *hex; unsigned char peeled[20]; + if (show_head && !strcmp(refname, "HEAD")) + goto match; + if (tags_only || heads_only) { int match; @@ -167,9 +170,10 @@ static const struct option show_ref_options[] = { OPT_BOOLEAN(0, "verify", &verify, N_("stricter reference checking, " "requires exact ref path")), { OPTION_BOOLEAN, 'h', NULL, &show_head, NULL, - N_("show the HEAD reference"), + N_("show the HEAD reference, even if it would be filtered out"), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, - OPT_BOOLEAN(0, "head", &show_head, N_("show the HEAD reference")), + OPT_BOOLEAN(0, "head", &show_head, + N_("show the HEAD reference, even if it would be filtered out")), OPT_BOOLEAN('d', "dereference", &deref_tags, N_("dereference tags into object IDs")), { OPTION_CALLBACK, 's', "hash", &abbrev, N_("n"), diff --git a/builtin/update-index.c b/builtin/update-index.c index 5c7762eef4..c317981516 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -83,7 +83,7 @@ static int process_lstat_error(const char *path, int err) return error("lstat(\"%s\"): %s", path, strerror(errno)); } -static int add_one_path(struct cache_entry *old, const char *path, int len, struct stat *st) +static int add_one_path(const struct cache_entry *old, const char *path, int len, struct stat *st) { int option, size; struct cache_entry *ce; @@ -142,7 +142,7 @@ static int process_directory(const char *path, int len, struct stat *st) /* Exact match: file or existing gitlink */ if (pos >= 0) { - struct cache_entry *ce = active_cache[pos]; + const struct cache_entry *ce = active_cache[pos]; if (S_ISGITLINK(ce->ce_mode)) { /* Do nothing to the index if there is no HEAD! */ @@ -158,7 +158,7 @@ static int process_directory(const char *path, int len, struct stat *st) /* Inexact match: is there perhaps a subdirectory match? */ pos = -pos-1; while (pos < active_nr) { - struct cache_entry *ce = active_cache[pos++]; + const struct cache_entry *ce = active_cache[pos++]; if (strncmp(ce->name, path, len)) break; @@ -183,7 +183,7 @@ static int process_path(const char *path) { int pos, len; struct stat st; - struct cache_entry *ce; + const struct cache_entry *ce; len = strlen(path); if (has_symlink_leading_path(path, len)) @@ -448,7 +448,7 @@ static int unresolve_one(const char *path) /* already merged */ pos = unmerge_cache_entry_at(pos); if (pos < active_nr) { - struct cache_entry *ce = active_cache[pos]; + const struct cache_entry *ce = active_cache[pos]; if (ce_stage(ce) && ce_namelen(ce) == namelen && !memcmp(ce->name, path, namelen)) @@ -462,7 +462,7 @@ static int unresolve_one(const char *path) */ pos = -pos-1; if (pos < active_nr) { - struct cache_entry *ce = active_cache[pos]; + const struct cache_entry *ce = active_cache[pos]; if (ce_namelen(ce) == namelen && !memcmp(ce->name, path, namelen)) { fprintf(stderr, @@ -558,7 +558,7 @@ static int do_reupdate(int ac, const char **av, has_head = 0; redo: for (pos = 0; pos < active_nr; pos++) { - struct cache_entry *ce = active_cache[pos]; + const struct cache_entry *ce = active_cache[pos]; struct cache_entry *old = NULL; int save_nr; diff --git a/cache-tree.c b/cache-tree.c index 37e4d008b5..0bbec43216 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -149,7 +149,7 @@ void cache_tree_invalidate_path(struct cache_tree *it, const char *path) cache_tree_invalidate_path(down->cache_tree, slash + 1); } -static int verify_cache(struct cache_entry **cache, +static int verify_cache(const struct cache_entry * const *cache, int entries, int flags) { int i, funny; @@ -158,7 +158,7 @@ static int verify_cache(struct cache_entry **cache, /* Verify that the tree is merged */ funny = 0; for (i = 0; i < entries; i++) { - struct cache_entry *ce = cache[i]; + const struct cache_entry *ce = cache[i]; if (ce_stage(ce)) { if (silent) return -1; @@ -234,7 +234,7 @@ int cache_tree_fully_valid(struct cache_tree *it) } static int update_one(struct cache_tree *it, - struct cache_entry **cache, + const struct cache_entry * const *cache, int entries, const char *base, int baselen, @@ -265,7 +265,7 @@ static int update_one(struct cache_tree *it, */ i = 0; while (i < entries) { - struct cache_entry *ce = cache[i]; + const struct cache_entry *ce = cache[i]; struct cache_tree_sub *sub; const char *path, *slash; int pathlen, sublen, subcnt, subskip; @@ -312,7 +312,7 @@ static int update_one(struct cache_tree *it, i = 0; while (i < entries) { - struct cache_entry *ce = cache[i]; + const struct cache_entry *ce = cache[i]; struct cache_tree_sub *sub; const char *path, *slash; int pathlen, entlen; @@ -397,7 +397,7 @@ static int update_one(struct cache_tree *it, } int cache_tree_update(struct cache_tree *it, - struct cache_entry **cache, + const struct cache_entry * const *cache, int entries, int flags) { @@ -599,8 +599,8 @@ int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix) was_valid = cache_tree_fully_valid(active_cache_tree); if (!was_valid) { if (cache_tree_update(active_cache_tree, - active_cache, active_nr, - flags) < 0) + (const struct cache_entry * const *)active_cache, + active_nr, flags) < 0) return WRITE_TREE_UNMERGED_INDEX; if (0 <= newfd) { if (!write_cache(newfd, active_cache, active_nr) && @@ -701,5 +701,6 @@ int update_main_cache_tree(int flags) if (!the_index.cache_tree) the_index.cache_tree = cache_tree(); return cache_tree_update(the_index.cache_tree, - the_index.cache, the_index.cache_nr, flags); + (const struct cache_entry * const *)the_index.cache, + the_index.cache_nr, flags); } diff --git a/cache-tree.h b/cache-tree.h index 55d0f59f2b..f1923ad1e9 100644 --- a/cache-tree.h +++ b/cache-tree.h @@ -30,7 +30,7 @@ void cache_tree_write(struct strbuf *, struct cache_tree *root); struct cache_tree *cache_tree_read(const char *buffer, unsigned long size); int cache_tree_fully_valid(struct cache_tree *); -int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int); +int cache_tree_update(struct cache_tree *, const struct cache_entry * const *, int, int); int update_main_cache_tree(int); diff --git a/cache.h b/cache.h index 2d06169155..3a69638bd9 100644 --- a/cache.h +++ b/cache.h @@ -425,6 +425,8 @@ extern int path_inside_repo(const char *prefix, const char *path); extern int set_git_dir_init(const char *git_dir, const char *real_git_dir, int); extern int init_db(const char *template_dir, unsigned int flags); +extern void sanitize_stdfds(void); + #define alloc_nr(x) (((x)+16)*3/2) /* @@ -476,7 +478,7 @@ extern int remove_file_from_index(struct index_state *, const char *path); extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags); extern int add_file_to_index(struct index_state *, const char *path, int flags); extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh); -extern int ce_same_name(struct cache_entry *a, struct cache_entry *b); +extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b); extern int index_name_is_other(const struct index_state *, const char *, int); extern void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *); @@ -575,6 +577,7 @@ extern int assume_unchanged; extern int prefer_symlink_refs; extern int log_all_ref_updates; extern int warn_ambiguous_refs; +extern int warn_on_object_refname_ambiguity; extern int shared_repository; extern const char *apply_default_whitespace; extern const char *apply_default_ignorewhitespace; @@ -758,7 +761,7 @@ int is_directory(const char *); const char *real_path(const char *path); const char *real_path_if_valid(const char *path); const char *absolute_path(const char *path); -const char *relative_path(const char *abs, const char *base); +const char *relative_path(const char *in, const char *prefix, struct strbuf *sb); int normalize_path_copy(char *dst, const char *src); int longest_ancestor_length(const char *path, struct string_list *prefixes); char *strip_path_suffix(const char *path, const char *suffix); @@ -1129,6 +1132,7 @@ extern int unpack_object_header(struct packed_git *, struct pack_window **, off_ struct object_info { /* Request */ + enum object_type *typep; unsigned long *sizep; unsigned long *disk_sizep; @@ -1174,11 +1178,15 @@ extern int update_server_info(int); typedef int (*config_fn_t)(const char *, const char *, void *); extern int git_default_config(const char *, const char *, void *); extern int git_config_from_file(config_fn_t fn, const char *, void *); +extern int git_config_from_buf(config_fn_t fn, const char *name, + const char *buf, size_t len, void *data); extern void git_config_push_parameter(const char *text); extern int git_config_from_parameters(config_fn_t fn, void *data); extern int git_config(config_fn_t fn, void *); extern int git_config_with_options(config_fn_t fn, void *, - const char *filename, int respect_includes); + const char *filename, + const char *blob_ref, + int respect_includes); extern int git_config_early(config_fn_t fn, void *, const char *repo_config); extern int git_parse_ulong(const char *, unsigned long *); extern int git_config_int(const char *, const char *); diff --git a/command-list.txt b/command-list.txt index bf83303c48..08b04e2c66 100644 --- a/command-list.txt +++ b/command-list.txt @@ -13,6 +13,7 @@ git-bundle mainporcelain git-cat-file plumbinginterrogators git-check-attr purehelpers git-check-ignore purehelpers +git-check-mailmap purehelpers git-checkout mainporcelain common git-checkout-index plumbingmanipulators git-check-ref-format purehelpers diff --git a/compat/unsetenv.c b/compat/unsetenv.c index 4ea18569c2..bf5fd7063b 100644 --- a/compat/unsetenv.c +++ b/compat/unsetenv.c @@ -2,6 +2,9 @@ void gitunsetenv (const char *name) { +#if !defined(__MINGW32__) + extern char **environ; +#endif int src, dst; size_t nmln; diff --git a/config.c b/config.c index d04e8157ab..e13a7b65e7 100644 --- a/config.c +++ b/config.c @@ -10,20 +10,69 @@ #include "strbuf.h" #include "quote.h" -typedef struct config_file { - struct config_file *prev; - FILE *f; +struct config_source { + struct config_source *prev; + union { + FILE *file; + struct config_buf { + const char *buf; + size_t len; + size_t pos; + } buf; + } u; const char *name; + int die_on_error; int linenr; int eof; struct strbuf value; struct strbuf var; -} config_file; -static config_file *cf; + int (*fgetc)(struct config_source *c); + int (*ungetc)(int c, struct config_source *conf); + long (*ftell)(struct config_source *c); +}; + +static struct config_source *cf; static int zlib_compression_seen; +static int config_file_fgetc(struct config_source *conf) +{ + return fgetc(conf->u.file); +} + +static int config_file_ungetc(int c, struct config_source *conf) +{ + return ungetc(c, conf->u.file); +} + +static long config_file_ftell(struct config_source *conf) +{ + return ftell(conf->u.file); +} + + +static int config_buf_fgetc(struct config_source *conf) +{ + if (conf->u.buf.pos < conf->u.buf.len) + return conf->u.buf.buf[conf->u.buf.pos++]; + + return EOF; +} + +static int config_buf_ungetc(int c, struct config_source *conf) +{ + if (conf->u.buf.pos > 0) + return conf->u.buf.buf[--conf->u.buf.pos]; + + return EOF; +} + +static long config_buf_ftell(struct config_source *conf) +{ + return conf->u.buf.pos; +} + #define MAX_INCLUDE_DEPTH 10 static const char include_depth_advice[] = "exceeded maximum include depth (%d) while including\n" @@ -168,27 +217,22 @@ int git_config_from_parameters(config_fn_t fn, void *data) static int get_next_char(void) { - int c; - FILE *f; - - c = '\n'; - if (cf && ((f = cf->f) != NULL)) { - c = fgetc(f); - if (c == '\r') { - /* DOS like systems */ - c = fgetc(f); - if (c != '\n') { - ungetc(c, f); - c = '\r'; - } - } - if (c == '\n') - cf->linenr++; - if (c == EOF) { - cf->eof = 1; - c = '\n'; + int c = cf->fgetc(cf); + + if (c == '\r') { + /* DOS like systems */ + c = cf->fgetc(cf); + if (c != '\n') { + cf->ungetc(c, cf); + c = '\r'; } } + if (c == '\n') + cf->linenr++; + if (c == EOF) { + cf->eof = 1; + c = '\n'; + } return c; } @@ -339,7 +383,7 @@ static int get_base_var(struct strbuf *name) } } -static int git_parse_file(config_fn_t fn, void *data) +static int git_parse_source(config_fn_t fn, void *data) { int comment = 0; int baselen = 0; @@ -399,7 +443,10 @@ static int git_parse_file(config_fn_t fn, void *data) if (get_value(fn, data, var) < 0) break; } - die("bad config file line %d in %s", cf->linenr, cf->name); + if (cf->die_on_error) + die("bad config file line %d in %s", cf->linenr, cf->name); + else + return error("bad config file line %d in %s", cf->linenr, cf->name); } static int parse_unit_factor(const char *end, uintmax_t *val) @@ -906,6 +953,33 @@ int git_default_config(const char *var, const char *value, void *dummy) return 0; } +/* + * All source specific fields in the union, die_on_error, name and the callbacks + * fgetc, ungetc, ftell of top need to be initialized before calling + * this function. + */ +static int do_config_from(struct config_source *top, config_fn_t fn, void *data) +{ + int ret; + + /* push config-file parsing state stack */ + top->prev = cf; + top->linenr = 1; + top->eof = 0; + strbuf_init(&top->value, 1024); + strbuf_init(&top->var, 1024); + cf = top; + + ret = git_parse_source(fn, data); + + /* pop config-file parsing state stack */ + strbuf_release(&top->value); + strbuf_release(&top->var); + cf = top->prev; + + return ret; +} + int git_config_from_file(config_fn_t fn, const char *filename, void *data) { int ret; @@ -913,30 +987,74 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data) ret = -1; if (f) { - config_file top; + struct config_source top; - /* push config-file parsing state stack */ - top.prev = cf; - top.f = f; + top.u.file = f; top.name = filename; - top.linenr = 1; - top.eof = 0; - strbuf_init(&top.value, 1024); - strbuf_init(&top.var, 1024); - cf = ⊤ - - ret = git_parse_file(fn, data); + top.die_on_error = 1; + top.fgetc = config_file_fgetc; + top.ungetc = config_file_ungetc; + top.ftell = config_file_ftell; - /* pop config-file parsing state stack */ - strbuf_release(&top.value); - strbuf_release(&top.var); - cf = top.prev; + ret = do_config_from(&top, fn, data); fclose(f); } return ret; } +int git_config_from_buf(config_fn_t fn, const char *name, const char *buf, + size_t len, void *data) +{ + struct config_source top; + + top.u.buf.buf = buf; + top.u.buf.len = len; + top.u.buf.pos = 0; + top.name = name; + top.die_on_error = 0; + top.fgetc = config_buf_fgetc; + top.ungetc = config_buf_ungetc; + top.ftell = config_buf_ftell; + + return do_config_from(&top, fn, data); +} + +static int git_config_from_blob_sha1(config_fn_t fn, + const char *name, + const unsigned char *sha1, + void *data) +{ + enum object_type type; + char *buf; + unsigned long size; + int ret; + + buf = read_sha1_file(sha1, &type, &size); + if (!buf) + return error("unable to load config blob object '%s'", name); + if (type != OBJ_BLOB) { + free(buf); + return error("reference '%s' does not point to a blob", name); + } + + ret = git_config_from_buf(fn, name, buf, size, data); + free(buf); + + return ret; +} + +static int git_config_from_blob_ref(config_fn_t fn, + const char *name, + void *data) +{ + unsigned char sha1[20]; + + if (get_sha1(name, sha1) < 0) + return error("unable to resolve config blob '%s'", name); + return git_config_from_blob_sha1(fn, name, sha1, data); +} + const char *git_etc_gitconfig(void) { static const char *system_wide; @@ -1002,7 +1120,9 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config) } int git_config_with_options(config_fn_t fn, void *data, - const char *filename, int respect_includes) + const char *filename, + const char *blob_ref, + int respect_includes) { char *repo_config = NULL; int ret; @@ -1021,6 +1141,8 @@ int git_config_with_options(config_fn_t fn, void *data, */ if (filename) return git_config_from_file(fn, filename, data); + else if (blob_ref) + return git_config_from_blob_ref(fn, blob_ref, data); repo_config = git_pathdup("config"); ret = git_config_early(fn, data, repo_config); @@ -1031,7 +1153,7 @@ int git_config_with_options(config_fn_t fn, void *data, int git_config(config_fn_t fn, void *data) { - return git_config_with_options(fn, data, NULL, 1); + return git_config_with_options(fn, data, NULL, NULL, 1); } /* @@ -1063,7 +1185,6 @@ static int store_aux(const char *key, const char *value, void *cb) { const char *ep; size_t section_len; - FILE *f = cf->f; switch (store.state) { case KEY_SEEN: @@ -1075,7 +1196,7 @@ static int store_aux(const char *key, const char *value, void *cb) return 1; } - store.offset[store.seen] = ftell(f); + store.offset[store.seen] = cf->ftell(cf); store.seen++; } break; @@ -1102,19 +1223,19 @@ static int store_aux(const char *key, const char *value, void *cb) * Do not increment matches: this is no match, but we * just made sure we are in the desired section. */ - store.offset[store.seen] = ftell(f); + store.offset[store.seen] = cf->ftell(cf); /* fallthru */ case SECTION_END_SEEN: case START: if (matches(key, value)) { - store.offset[store.seen] = ftell(f); + store.offset[store.seen] = cf->ftell(cf); store.state = KEY_SEEN; store.seen++; } else { if (strrchr(key, '.') - key == store.baselen && !strncmp(key, store.key, store.baselen)) { store.state = SECTION_SEEN; - store.offset[store.seen] = ftell(f); + store.offset[store.seen] = cf->ftell(cf); } } } diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index fa3ae1e853..cd509a5d63 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -648,6 +648,7 @@ __git_list_porcelain_commands () cat-file) : plumbing;; check-attr) : plumbing;; check-ignore) : plumbing;; + check-mailmap) : plumbing;; check-ref-format) : plumbing;; checkout-index) : plumbing;; commit-tree) : plumbing;; diff --git a/daemon.c b/daemon.c index 6aeddcb98d..34916c5e10 100644 --- a/daemon.c +++ b/daemon.c @@ -760,7 +760,7 @@ static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen) snprintf(portbuf, sizeof(portbuf), "REMOTE_PORT=%d", ntohs(sin_addr->sin_port)); #ifndef NO_IPV6 - } else if (addr && addr->sa_family == AF_INET6) { + } else if (addr->sa_family == AF_INET6) { struct sockaddr_in6 *sin6_addr = (void *) addr; char *buf = addrbuf + 12; @@ -1047,18 +1047,6 @@ static int service_loop(struct socketlist *socklist) } } -/* if any standard file descriptor is missing open it to /dev/null */ -static void sanitize_stdfds(void) -{ - int fd = open("/dev/null", O_RDWR, 0); - while (fd != -1 && fd < 2) - fd = dup(fd); - if (fd == -1) - die_errno("open /dev/null or dup failed"); - if (fd > 2) - close(fd); -} - #ifdef NO_POSIX_GOODIES struct credentials; diff --git a/diff.c b/diff.c index e53ddad7d4..4629de2092 100644 --- a/diff.c +++ b/diff.c @@ -1683,9 +1683,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options) del = deleted; if (graph_width <= max_change) { - int total = add + del; - - total = scale_linear(add + del, graph_width, max_change); + int total = scale_linear(add + del, graph_width, max_change); if (total < 2 && add && del) /* width >= 2 due to the sanity check */ total = 2; @@ -2586,7 +2584,7 @@ void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1, */ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int want_file) { - struct cache_entry *ce; + const struct cache_entry *ce; struct stat st; int pos, len; @@ -3505,6 +3503,11 @@ static int parse_submodule_opt(struct diff_options *options, const char *value) return 1; } +static void enable_patch_output(int *fmt) { + *fmt &= ~DIFF_FORMAT_NO_OUTPUT; + *fmt |= DIFF_FORMAT_PATCH; +} + int diff_opt_parse(struct diff_options *options, const char **av, int ac) { const char *arg = av[0]; @@ -3512,15 +3515,15 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) int argcount; /* Output format options */ - if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch")) - options->output_format |= DIFF_FORMAT_PATCH; - else if (opt_arg(arg, 'U', "unified", &options->context)) - options->output_format |= DIFF_FORMAT_PATCH; + if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch") + || opt_arg(arg, 'U', "unified", &options->context)) + enable_patch_output(&options->output_format); else if (!strcmp(arg, "--raw")) options->output_format |= DIFF_FORMAT_RAW; - else if (!strcmp(arg, "--patch-with-raw")) - options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW; - else if (!strcmp(arg, "--numstat")) + else if (!strcmp(arg, "--patch-with-raw")) { + enable_patch_output(&options->output_format); + options->output_format |= DIFF_FORMAT_RAW; + } else if (!strcmp(arg, "--numstat")) options->output_format |= DIFF_FORMAT_NUMSTAT; else if (!strcmp(arg, "--shortstat")) options->output_format |= DIFF_FORMAT_SHORTSTAT; @@ -3542,13 +3545,14 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->output_format |= DIFF_FORMAT_CHECKDIFF; else if (!strcmp(arg, "--summary")) options->output_format |= DIFF_FORMAT_SUMMARY; - else if (!strcmp(arg, "--patch-with-stat")) - options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT; - else if (!strcmp(arg, "--name-only")) + else if (!strcmp(arg, "--patch-with-stat")) { + enable_patch_output(&options->output_format); + options->output_format |= DIFF_FORMAT_DIFFSTAT; + } else if (!strcmp(arg, "--name-only")) options->output_format |= DIFF_FORMAT_NAME; else if (!strcmp(arg, "--name-status")) options->output_format |= DIFF_FORMAT_NAME_STATUS; - else if (!strcmp(arg, "-s")) + else if (!strcmp(arg, "-s") || !strcmp(arg, "--no-patch")) options->output_format |= DIFF_FORMAT_NO_OUTPUT; else if (!prefixcmp(arg, "--stat")) /* --stat, --stat-width, --stat-name-width, or --stat-count */ @@ -3621,7 +3625,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) /* flags options */ else if (!strcmp(arg, "--binary")) { - options->output_format |= DIFF_FORMAT_PATCH; + enable_patch_output(&options->output_format); DIFF_OPT_SET(options, BINARY); } else if (!strcmp(arg, "--full-index")) diff --git a/dir.c b/dir.c index 0480419544..910bfcde4e 100644 --- a/dir.c +++ b/dir.c @@ -933,7 +933,7 @@ enum exist_status { */ static enum exist_status directory_exists_in_index_icase(const char *dirname, int len) { - struct cache_entry *ce = index_name_exists(&the_index, dirname, len + 1, ignore_case); + const struct cache_entry *ce = index_name_exists(&the_index, dirname, len + 1, ignore_case); unsigned char endchar; if (!ce) @@ -977,7 +977,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len) if (pos < 0) pos = -pos-1; while (pos < active_nr) { - struct cache_entry *ce = active_cache[pos++]; + const struct cache_entry *ce = active_cache[pos++]; unsigned char endchar; if (strncmp(ce->name, dirname, len)) @@ -1113,7 +1113,7 @@ static int exclude_matches_pathspec(const char *path, int len, static int get_index_dtype(const char *path, int len) { int pos; - struct cache_entry *ce; + const struct cache_entry *ce; ce = cache_name_exists(path, len, 0); if (ce) { diff --git a/entry.c b/entry.c index d7c131d453..acc892f9a6 100644 --- a/entry.c +++ b/entry.c @@ -79,7 +79,7 @@ static int create_file(const char *path, unsigned int mode) return open(path, O_WRONLY | O_CREAT | O_EXCL, mode); } -static void *read_blob_entry(struct cache_entry *ce, unsigned long *size) +static void *read_blob_entry(const struct cache_entry *ce, unsigned long *size) { enum object_type type; void *new = read_sha1_file(ce->sha1, &type, size); @@ -92,7 +92,7 @@ static void *read_blob_entry(struct cache_entry *ce, unsigned long *size) return NULL; } -static int open_output_fd(char *path, struct cache_entry *ce, int to_tempfile) +static int open_output_fd(char *path, const struct cache_entry *ce, int to_tempfile) { int symlink = (ce->ce_mode & S_IFMT) != S_IFREG; if (to_tempfile) { @@ -115,7 +115,7 @@ static int fstat_output(int fd, const struct checkout *state, struct stat *st) return 0; } -static int streaming_write_entry(struct cache_entry *ce, char *path, +static int streaming_write_entry(const struct cache_entry *ce, char *path, struct stream_filter *filter, const struct checkout *state, int to_tempfile, int *fstat_done, struct stat *statbuf) @@ -136,7 +136,8 @@ static int streaming_write_entry(struct cache_entry *ce, char *path, return result; } -static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile) +static int write_entry(struct cache_entry *ce, + char *path, const struct checkout *state, int to_tempfile) { unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT; int fd, ret, fstat_done = 0; @@ -199,9 +200,9 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout break; case S_IFGITLINK: if (to_tempfile) - return error("cannot create temporary subproject %s", path); + return error("cannot create temporary submodule %s", path); if (mkdir(path, 0777) < 0) - return error("cannot create subproject directory %s", path); + return error("cannot create submodule directory %s", path); break; default: return error("unknown file mode for %s in index", path); @@ -233,7 +234,8 @@ static int check_path(const char *path, int len, struct stat *st, int skiplen) return lstat(path, st); } -int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath) +int checkout_entry(struct cache_entry *ce, + const struct checkout *state, char *topath) { static char path[PATH_MAX + 1]; struct stat st; diff --git a/environment.c b/environment.c index 0cb67b22cf..5398c36dd4 100644 --- a/environment.c +++ b/environment.c @@ -22,6 +22,7 @@ int prefer_symlink_refs; int is_bare_repository_cfg = -1; /* unspecified */ int log_all_ref_updates = -1; /* unspecified */ int warn_ambiguous_refs = 1; +int warn_on_object_refname_ambiguity = 1; int repository_format_version; const char *git_commit_encoding; const char *git_log_output_encoding; diff --git a/exec_cmd.h b/exec_cmd.h index e2b546b615..e4c9702f02 100644 --- a/exec_cmd.h +++ b/exec_cmd.h @@ -7,6 +7,7 @@ extern const char *git_exec_path(void); extern void setup_path(void); extern const char **prepare_git_cmd(const char **argv); extern int execv_git_cmd(const char **argv); /* NULL terminated */ +LAST_ARG_MUST_BE_NULL extern int execl_git_cmd(const char *cmd, ...); extern const char *system_path(const char *path); diff --git a/git-compat-util.h b/git-compat-util.h index ff193f4aa2..cc4ba4d18f 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -303,6 +303,13 @@ extern char *gitbasename(char *); #endif #endif +/* The sentinel attribute is valid from gcc version 4.0 */ +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define LAST_ARG_MUST_BE_NULL __attribute__((sentinel)) +#else +#define LAST_ARG_MUST_BE_NULL +#endif + #include "compat/bswap.h" #ifdef USE_WILDMATCH diff --git a/git-request-pull.sh b/git-request-pull.sh index d566015975..ebf1269d29 100755 --- a/git-request-pull.sh +++ b/git-request-pull.sh @@ -51,8 +51,18 @@ fi tag_name=$(git describe --exact "$head^0" 2>/dev/null) test -n "$base" && test -n "$url" || usage -baserev=$(git rev-parse --verify "$base"^0) && -headrev=$(git rev-parse --verify "$head"^0) || exit + +baserev=$(git rev-parse --verify --quiet "$base"^0) +if test -z "$baserev" +then + die "fatal: Not a valid revision: $base" +fi + +headrev=$(git rev-parse --verify --quiet "$head"^0) +if test -z "$headrev" +then + die "fatal: Not a valid revision: $head" +fi merge_base=$(git merge-base $baserev $headrev) || die "fatal: No commits in common between $base and $head" diff --git a/git-send-email.perl b/git-send-email.perl index 45c3863377..2162478392 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -69,6 +69,9 @@ sub usage { --smtp-pass * Password for SMTP-AUTH; not necessary. --smtp-encryption * tls or ssl; anything else disables. --smtp-ssl * Deprecated. Use '--smtp-encryption ssl'. + --smtp-ssl-cert-path * Path to ca-certificates (either directory or file). + Pass an empty string to disable certificate + verification. --smtp-domain * The domain name sent to HELO/EHLO handshake --smtp-debug <0|1> * Disable, enable Net::SMTP debug. @@ -194,7 +197,7 @@ sub do_edit { my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc); my ($to_cmd, $cc_cmd); my ($smtp_server, $smtp_server_port, @smtp_server_options); -my ($smtp_authuser, $smtp_encryption); +my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path); my ($identity, $aliasfiletype, @alias_files, $smtp_domain); my ($validate, $confirm); my (@suppress_cc); @@ -220,6 +223,7 @@ sub do_edit { "smtpserveroption" => \@smtp_server_options, "smtpuser" => \$smtp_authuser, "smtppass" => \$smtp_authpass, + "smtpsslcertpath" => \$smtp_ssl_cert_path, "smtpdomain" => \$smtp_domain, "to" => \@initial_to, "tocmd" => \$to_cmd, @@ -287,6 +291,7 @@ sub signal_handler { "smtp-pass:s" => \$smtp_authpass, "smtp-ssl" => sub { $smtp_encryption = 'ssl' }, "smtp-encryption=s" => \$smtp_encryption, + "smtp-ssl-cert-path" => \$smtp_ssl_cert_path, "smtp-debug:i" => \$debug_net_smtp, "smtp-domain:s" => \$smtp_domain, "identity=s" => \$identity, @@ -1079,6 +1084,34 @@ sub smtp_auth_maybe { return $auth; } +sub ssl_verify_params { + eval { + require IO::Socket::SSL; + IO::Socket::SSL->import(qw/SSL_VERIFY_PEER SSL_VERIFY_NONE/); + }; + if ($@) { + print STDERR "Not using SSL_VERIFY_PEER due to out-of-date IO::Socket::SSL.\n"; + return; + } + + if (!defined $smtp_ssl_cert_path) { + $smtp_ssl_cert_path = "/etc/ssl/certs"; + } + + if ($smtp_ssl_cert_path eq "") { + return (SSL_verify_mode => SSL_VERIFY_NONE()); + } elsif (-d $smtp_ssl_cert_path) { + return (SSL_verify_mode => SSL_VERIFY_PEER(), + SSL_ca_path => $smtp_ssl_cert_path); + } elsif (-f $smtp_ssl_cert_path) { + return (SSL_verify_mode => SSL_VERIFY_PEER(), + SSL_ca_file => $smtp_ssl_cert_path); + } else { + print STDERR "Not using SSL_VERIFY_PEER because the CA path does not exist.\n"; + return (SSL_verify_mode => SSL_VERIFY_NONE()); + } +} + # Returns 1 if the message was sent, and 0 otherwise. # In actuality, the whole program dies when there # is an error sending a message. @@ -1183,7 +1216,8 @@ sub send_message { $smtp_domain ||= maildomain(); $smtp ||= Net::SMTP::SSL->new($smtp_server, Hello => $smtp_domain, - Port => $smtp_server_port); + Port => $smtp_server_port, + ssl_verify_params()); } else { require Net::SMTP; @@ -1198,7 +1232,8 @@ sub send_message { $smtp->command('STARTTLS'); $smtp->response(); if ($smtp->code == 220) { - $smtp = Net::SMTP::SSL->start_SSL($smtp) + $smtp = Net::SMTP::SSL->start_SSL($smtp, + ssl_verify_params()) or die "STARTTLS failed! ".$smtp->message; $smtp_encryption = ''; # Send EHLO again to receive fresh diff --git a/git.c b/git.c index 4359086fd6..2025f77d01 100644 --- a/git.c +++ b/git.c @@ -324,6 +324,7 @@ static void handle_internal_command(int argc, const char **argv) { "cat-file", cmd_cat_file, RUN_SETUP }, { "check-attr", cmd_check_attr, RUN_SETUP }, { "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE }, + { "check-mailmap", cmd_check_mailmap, RUN_SETUP }, { "check-ref-format", cmd_check_ref_format }, { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE }, { "checkout-index", cmd_checkout_index, @@ -525,6 +526,13 @@ int main(int argc, char **av) if (!cmd) cmd = "git-help"; + /* + * Always open file descriptors 0/1/2 to avoid clobbering files + * in die(). It also avoids messing up when the pipes are dup'ed + * onto stdin/stdout/stderr in the child processes we spawn. + */ + sanitize_stdfds(); + git_setup_gettext(); /* diff --git a/line-log.c b/line-log.c index 8cc29a0000..c2d01dccc2 100644 --- a/line-log.c +++ b/line-log.c @@ -110,12 +110,14 @@ static void range_set_check_invariants(struct range_set *rs) static void sort_and_merge_range_set(struct range_set *rs) { int i; - int o = 1; /* output cursor */ + int o = 0; /* output cursor */ qsort(rs->ranges, rs->nr, sizeof(struct range), range_cmp); - for (i = 1; i < rs->nr; i++) { - if (rs->ranges[i].start <= rs->ranges[o-1].end) { + for (i = 0; i < rs->nr; i++) { + if (rs->ranges[i].start == rs->ranges[i].end) + continue; + if (o > 0 && rs->ranges[i].start <= rs->ranges[o-1].end) { if (rs->ranges[o-1].end < rs->ranges[i].end) rs->ranges[o-1].end = rs->ranges[i].end; } else { @@ -297,6 +299,7 @@ static void line_log_data_insert(struct line_log_data **list, p = xcalloc(1, sizeof(struct line_log_data)); p->path = path; range_set_append(&p->ranges, begin, end); + sort_and_merge_range_set(&p->ranges); if (ip) { p->next = ip->next; ip->next = p; diff --git a/line-range.c b/line-range.c index 8faf943745..3942475c2f 100644 --- a/line-range.c +++ b/line-range.c @@ -211,6 +211,8 @@ int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb, void *cb_data, long lines, long *begin, long *end, const char *path) { + *begin = *end = 0; + if (*arg == ':') { arg = parse_range_funcname(arg, nth_line_cb, cb_data, lines, begin, end, path); if (!arg || *arg) @@ -226,6 +228,11 @@ int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb, if (*arg) return -1; + if (*begin && *end && *end < *begin) { + long tmp; + tmp = *end; *end = *begin; *begin = tmp; + } + return 0; } diff --git a/mailmap.c b/mailmap.c index 2a7b36628c..44614fc411 100644 --- a/mailmap.c +++ b/mailmap.c @@ -5,8 +5,10 @@ #define DEBUG_MAILMAP 0 #if DEBUG_MAILMAP #define debug_mm(...) fprintf(stderr, __VA_ARGS__) +#define debug_str(X) ((X) ? (X) : "(none)") #else static inline void debug_mm(const char *format, ...) {} +static inline const char *debug_str(const char *s) { return s; } #endif const char *git_mailmap_file; @@ -29,7 +31,8 @@ struct mailmap_entry { static void free_mailmap_info(void *p, const char *s) { struct mailmap_info *mi = (struct mailmap_info *)p; - debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n", s, mi->name, mi->email); + debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n", + s, debug_str(mi->name), debug_str(mi->email)); free(mi->name); free(mi->email); } @@ -37,8 +40,11 @@ static void free_mailmap_info(void *p, const char *s) static void free_mailmap_entry(void *p, const char *s) { struct mailmap_entry *me = (struct mailmap_entry *)p; - debug_mm("mailmap: removing entries for <%s>, with %d sub-entries\n", s, me->namemap.nr); - debug_mm("mailmap: - simple: '%s' <%s>\n", me->name, me->email); + debug_mm("mailmap: removing entries for <%s>, with %d sub-entries\n", + s, me->namemap.nr); + debug_mm("mailmap: - simple: '%s' <%s>\n", + debug_str(me->name), debug_str(me->email)); + free(me->name); free(me->email); @@ -47,18 +53,11 @@ static void free_mailmap_entry(void *p, const char *s) } static void add_mapping(struct string_list *map, - char *new_name, char *new_email, char *old_name, char *old_email) + char *new_name, char *new_email, + char *old_name, char *old_email) { struct mailmap_entry *me; int index; - char *p; - - if (old_email) - for (p = old_email; *p; p++) - *p = tolower(*p); - if (new_email) - for (p = new_email; *p; p++) - *p = tolower(*p); if (old_email == NULL) { old_email = new_email; @@ -68,16 +67,21 @@ static void add_mapping(struct string_list *map, if ((index = string_list_find_insert_index(map, old_email, 1)) < 0) { /* mailmap entry exists, invert index value */ index = -1 - index; + me = (struct mailmap_entry *)map->items[index].util; } else { /* create mailmap entry */ - struct string_list_item *item = string_list_insert_at_index(map, index, old_email); - item->util = xcalloc(1, sizeof(struct mailmap_entry)); - ((struct mailmap_entry *)item->util)->namemap.strdup_strings = 1; + struct string_list_item *item; + + item = string_list_insert_at_index(map, index, old_email); + me = xcalloc(1, sizeof(struct mailmap_entry)); + me->namemap.strdup_strings = 1; + me->namemap.cmp = strcasecmp; + item->util = me; } - me = (struct mailmap_entry *)map->items[index].util; if (old_name == NULL) { - debug_mm("mailmap: adding (simple) entry for %s at index %d\n", old_email, index); + debug_mm("mailmap: adding (simple) entry for %s at index %d\n", + old_email, index); /* Replace current name and new email for simple entry */ if (new_name) { free(me->name); @@ -89,7 +93,8 @@ static void add_mapping(struct string_list *map, } } else { struct mailmap_info *mi = xcalloc(1, sizeof(struct mailmap_info)); - debug_mm("mailmap: adding (complex) entry for %s at index %d\n", old_email, index); + debug_mm("mailmap: adding (complex) entry for %s at index %d\n", + old_email, index); if (new_name) mi->name = xstrdup(new_name); if (new_email) @@ -98,11 +103,12 @@ static void add_mapping(struct string_list *map, } debug_mm("mailmap: '%s' <%s> -> '%s' <%s>\n", - old_name, old_email, new_name, new_email); + debug_str(old_name), old_email, + debug_str(new_name), debug_str(new_email)); } static char *parse_name_and_email(char *buffer, char **name, - char **email, int allow_empty_email) + char **email, int allow_empty_email) { char *left, *right, *nstart, *nend; *name = *email = NULL; @@ -122,7 +128,7 @@ static char *parse_name_and_email(char *buffer, char **name, while (nend > nstart && isspace(*nend)) --nend; - *name = (nstart < nend ? nstart : NULL); + *name = (nstart <= nend ? nstart : NULL); *email = left+1; *(nend+1) = '\0'; *right++ = '\0'; @@ -306,21 +312,25 @@ static struct string_list_item *lookup_prefix(struct string_list *map, } int map_user(struct string_list *map, - const char **email, size_t *emaillen, - const char **name, size_t *namelen) + const char **email, size_t *emaillen, + const char **name, size_t *namelen) { struct string_list_item *item; struct mailmap_entry *me; debug_mm("map_user: map '%.*s' <%.*s>\n", - *name, *namelen, *emaillen, *email); + (int)*namelen, debug_str(*name), + (int)*emaillen, debug_str(*email)); item = lookup_prefix(map, *email, *emaillen); if (item != NULL) { me = (struct mailmap_entry *)item->util; if (me->namemap.nr) { - /* The item has multiple items, so we'll look up on name too */ - /* If the name is not found, we choose the simple entry */ + /* + * The item has multiple items, so we'll look up on + * name too. If the name is not found, we choose the + * simple entry. + */ struct string_list_item *subitem; subitem = lookup_prefix(&me->namemap, *name, *namelen); if (subitem) @@ -341,8 +351,9 @@ int map_user(struct string_list *map, *name = mi->name; *namelen = strlen(*name); } - debug_mm("map_user: to '%.*s' <.*%s>\n", *namelen, *name, - *emaillen, *email); + debug_mm("map_user: to '%.*s' <%.*s>\n", + (int)*namelen, debug_str(*name), + (int)*emaillen, debug_str(*email)); return 1; } debug_mm("map_user: --\n"); diff --git a/merge-recursive.c b/merge-recursive.c index ea9dbd307c..f95933b0aa 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -251,7 +251,7 @@ struct tree *write_tree_from_memory(struct merge_options *o) int i; fprintf(stderr, "BUG: There are unmerged index entries:\n"); for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; + const struct cache_entry *ce = active_cache[i]; if (ce_stage(ce)) fprintf(stderr, "BUG: %d %.*s\n", ce_stage(ce), (int)ce_namelen(ce), ce->name); @@ -264,7 +264,8 @@ struct tree *write_tree_from_memory(struct merge_options *o) if (!cache_tree_fully_valid(active_cache_tree) && cache_tree_update(active_cache_tree, - active_cache, active_nr, 0) < 0) + (const struct cache_entry * const *)active_cache, + active_nr, 0) < 0) die(_("error building trees")); result = lookup_tree(active_cache_tree->sha1); @@ -339,7 +340,7 @@ static struct string_list *get_unmerged(void) for (i = 0; i < active_nr; i++) { struct string_list_item *item; struct stage_data *e; - struct cache_entry *ce = active_cache[i]; + const struct cache_entry *ce = active_cache[i]; if (!ce_stage(ce)) continue; diff --git a/object.c b/object.c index cbc7333a7e..d8a4b1ffbe 100644 --- a/object.c +++ b/object.c @@ -145,7 +145,7 @@ struct object *lookup_unknown_object(const unsigned char *sha1) struct object *parse_object_buffer(const unsigned char *sha1, enum object_type type, unsigned long size, void *buffer, int *eaten_p) { struct object *obj; - int eaten = 0; + *eaten_p = 0; obj = NULL; if (type == OBJ_BLOB) { @@ -164,7 +164,7 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t if (!tree->object.parsed) { if (parse_tree_buffer(tree, buffer, size)) return NULL; - eaten = 1; + *eaten_p = 1; } } } else if (type == OBJ_COMMIT) { @@ -174,7 +174,7 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t return NULL; if (!commit->buffer) { commit->buffer = buffer; - eaten = 1; + *eaten_p = 1; } obj = &commit->object; } @@ -191,7 +191,6 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t } if (obj && obj->type == OBJ_NONE) obj->type = type; - *eaten_p = eaten; return obj; } diff --git a/path.c b/path.c index 04ff1487ed..7f3324aeea 100644 --- a/path.c +++ b/path.c @@ -441,42 +441,100 @@ int adjust_shared_perm(const char *path) return 0; } -const char *relative_path(const char *abs, const char *base) +/* + * Give path as relative to prefix. + * + * The strbuf may or may not be used, so do not assume it contains the + * returned path. + */ +const char *relative_path(const char *in, const char *prefix, + struct strbuf *sb) { - static char buf[PATH_MAX + 1]; + int in_len = in ? strlen(in) : 0; + int prefix_len = prefix ? strlen(prefix) : 0; + int in_off = 0; + int prefix_off = 0; int i = 0, j = 0; - if (!base || !base[0]) - return abs; - while (base[i]) { - if (is_dir_sep(base[i])) { - if (!is_dir_sep(abs[j])) - return abs; - while (is_dir_sep(base[i])) + if (!in_len) + return "./"; + else if (!prefix_len) + return in; + + while (i < prefix_len && j < in_len && prefix[i] == in[j]) { + if (is_dir_sep(prefix[i])) { + while (is_dir_sep(prefix[i])) i++; - while (is_dir_sep(abs[j])) + while (is_dir_sep(in[j])) j++; + prefix_off = i; + in_off = j; + } else { + i++; + j++; + } + } + + if ( + /* "prefix" seems like prefix of "in" */ + i >= prefix_len && + /* + * but "/foo" is not a prefix of "/foobar" + * (i.e. prefix not end with '/') + */ + prefix_off < prefix_len) { + if (j >= in_len) { + /* in="/a/b", prefix="/a/b" */ + in_off = in_len; + } else if (is_dir_sep(in[j])) { + /* in="/a/b/c", prefix="/a/b" */ + while (is_dir_sep(in[j])) + j++; + in_off = j; + } else { + /* in="/a/bbb/c", prefix="/a/b" */ + i = prefix_off; + } + } else if ( + /* "in" is short than "prefix" */ + j >= in_len && + /* "in" not end with '/' */ + in_off < in_len) { + if (is_dir_sep(prefix[i])) { + /* in="/a/b", prefix="/a/b/c/" */ + while (is_dir_sep(prefix[i])) + i++; + in_off = in_len; + } + } + in += in_off; + in_len -= in_off; + + if (i >= prefix_len) { + if (!in_len) + return "./"; + else + return in; + } + + strbuf_reset(sb); + strbuf_grow(sb, in_len); + + while (i < prefix_len) { + if (is_dir_sep(prefix[i])) { + strbuf_addstr(sb, "../"); + while (is_dir_sep(prefix[i])) + i++; continue; - } else if (abs[j] != base[i]) { - return abs; } i++; - j++; } - if ( - /* "/foo" is a prefix of "/foo" */ - abs[j] && - /* "/foo" is not a prefix of "/foobar" */ - !is_dir_sep(base[i-1]) && !is_dir_sep(abs[j]) - ) - return abs; - while (is_dir_sep(abs[j])) - j++; - if (!abs[j]) - strcpy(buf, "."); - else - strcpy(buf, abs + j); - return buf; + if (!is_dir_sep(prefix[prefix_len - 1])) + strbuf_addstr(sb, "../"); + + strbuf_addstr(sb, in); + + return sb->buf; } /* diff --git a/pathspec.c b/pathspec.c index 284f3970a3..6ea0867493 100644 --- a/pathspec.c +++ b/pathspec.c @@ -32,7 +32,7 @@ void add_pathspec_matches_against_index(const char **pathspec, if (!num_unmatched) return; for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; + const struct cache_entry *ce = active_cache[i]; match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen); } } @@ -67,7 +67,7 @@ const char *check_path_for_gitlink(const char *path) { int i, path_len = strlen(path); for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; + const struct cache_entry *ce = active_cache[i]; if (S_ISGITLINK(ce->ce_mode)) { int ce_len = ce_namelen(ce); if (path_len <= ce_len || path[ce_len] != '/' || diff --git a/quote.c b/quote.c index 911229fdf3..5c8808160e 100644 --- a/quote.c +++ b/quote.c @@ -312,82 +312,27 @@ void write_name_quotedpfx(const char *pfx, size_t pfxlen, fputc(terminator, fp); } -static const char *path_relative(const char *in, int len, - struct strbuf *sb, const char *prefix, - int prefix_len); - -void write_name_quoted_relative(const char *name, size_t len, - const char *prefix, size_t prefix_len, +void write_name_quoted_relative(const char *name, const char *prefix, FILE *fp, int terminator) { struct strbuf sb = STRBUF_INIT; - name = path_relative(name, len, &sb, prefix, prefix_len); + name = relative_path(name, prefix, &sb); write_name_quoted(name, fp, terminator); strbuf_release(&sb); } -/* - * Give path as relative to prefix. - * - * The strbuf may or may not be used, so do not assume it contains the - * returned path. - */ -static const char *path_relative(const char *in, int len, - struct strbuf *sb, const char *prefix, - int prefix_len) -{ - int off, i; - - if (len < 0) - len = strlen(in); - if (prefix_len < 0) { - if (prefix) - prefix_len = strlen(prefix); - else - prefix_len = 0; - } - - off = 0; - i = 0; - while (i < prefix_len && i < len && prefix[i] == in[i]) { - if (prefix[i] == '/') - off = i + 1; - i++; - } - in += off; - len -= off; - - if (i >= prefix_len) - return in; - - strbuf_reset(sb); - strbuf_grow(sb, len); - - while (i < prefix_len) { - if (prefix[i] == '/') - strbuf_addstr(sb, "../"); - i++; - } - strbuf_add(sb, in, len); - - return sb->buf; -} - /* quote path as relative to the given prefix */ -char *quote_path_relative(const char *in, int len, - struct strbuf *out, const char *prefix) +char *quote_path_relative(const char *in, const char *prefix, + struct strbuf *out) { struct strbuf sb = STRBUF_INIT; - const char *rel = path_relative(in, len, &sb, prefix, -1); + const char *rel = relative_path(in, prefix, &sb); strbuf_reset(out); quote_c_style_counted(rel, strlen(rel), out, NULL, 0); strbuf_release(&sb); - if (!out->len) - strbuf_addstr(out, "./"); - return out->buf; } diff --git a/quote.h b/quote.h index 133155a48b..ed110a5d8d 100644 --- a/quote.h +++ b/quote.h @@ -60,13 +60,12 @@ extern void quote_two_c_style(struct strbuf *, const char *, const char *, int); extern void write_name_quoted(const char *name, FILE *, int terminator); extern void write_name_quotedpfx(const char *pfx, size_t pfxlen, const char *name, FILE *, int terminator); -extern void write_name_quoted_relative(const char *name, size_t len, - const char *prefix, size_t prefix_len, +extern void write_name_quoted_relative(const char *name, const char *prefix, FILE *fp, int terminator); /* quote path as relative to the given prefix */ -extern char *quote_path_relative(const char *in, int len, - struct strbuf *out, const char *prefix); +extern char *quote_path_relative(const char *in, const char *prefix, + struct strbuf *out); /* quoting as a string literal for other languages */ extern void perl_quote_print(FILE *stream, const char *src); diff --git a/read-cache.c b/read-cache.c index d5201f9b06..d4474eab7b 100644 --- a/read-cache.c +++ b/read-cache.c @@ -722,7 +722,7 @@ struct cache_entry *make_cache_entry(unsigned int mode, return ce; } -int ce_same_name(struct cache_entry *a, struct cache_entry *b) +int ce_same_name(const struct cache_entry *a, const struct cache_entry *b) { int len = ce_namelen(a); return ce_namelen(b) == len && !memcmp(a->name, b->name, len); diff --git a/refs.c b/refs.c index 4302206649..a3f2302292 100644 --- a/refs.c +++ b/refs.c @@ -2174,11 +2174,14 @@ int lock_packed_refs(int flags) { struct packed_ref_cache *packed_ref_cache; - /* Discard the old cache because it might be invalid: */ - clear_packed_ref_cache(&ref_cache); if (hold_lock_file_for_update(&packlock, git_path("packed-refs"), flags) < 0) return -1; - /* Read the current packed-refs while holding the lock: */ + /* + * Get the current packed-refs while holding the lock. If the + * packed-refs file has been modified since we last read it, + * this will automatically invalidate the cache and re-read + * the packed-refs file. + */ packed_ref_cache = get_packed_ref_cache(&ref_cache); packed_ref_cache->lock = &packlock; /* Increment the reference count to prevent it from being freed: */ diff --git a/rerere.c b/rerere.c index 98e3e294d0..6fde8f94c2 100644 --- a/rerere.c +++ b/rerere.c @@ -299,7 +299,7 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu { mmfile_t mmfile[3] = {{NULL}}; mmbuffer_t result = {NULL, 0}; - struct cache_entry *ce; + const struct cache_entry *ce; int pos, len, i, hunk_no; struct rerere_io_mem io; int marker_size = ll_merge_marker_size(path); @@ -359,7 +359,7 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu static int check_one_conflict(int i, int *type) { - struct cache_entry *e = active_cache[i]; + const struct cache_entry *e = active_cache[i]; if (!ce_stage(e)) { *type = RESOLVED; @@ -374,8 +374,8 @@ static int check_one_conflict(int i, int *type) /* Only handle regular files with both stages #2 and #3 */ if (i + 1 < active_nr) { - struct cache_entry *e2 = active_cache[i]; - struct cache_entry *e3 = active_cache[i + 1]; + const struct cache_entry *e2 = active_cache[i]; + const struct cache_entry *e3 = active_cache[i + 1]; if (ce_stage(e2) == 2 && ce_stage(e3) == 3 && ce_same_name(e, e3) && @@ -398,7 +398,7 @@ static int find_conflict(struct string_list *conflict) for (i = 0; i < active_nr;) { int conflict_type; - struct cache_entry *e = active_cache[i]; + const struct cache_entry *e = active_cache[i]; i = check_one_conflict(i, &conflict_type); if (conflict_type == THREE_STAGED) string_list_insert(conflict, (const char *)e->name); @@ -414,7 +414,7 @@ int rerere_remaining(struct string_list *merge_rr) for (i = 0; i < active_nr;) { int conflict_type; - struct cache_entry *e = active_cache[i]; + const struct cache_entry *e = active_cache[i]; i = check_one_conflict(i, &conflict_type); if (conflict_type == PUNTED) string_list_insert(merge_rr, (const char *)e->name); diff --git a/resolve-undo.c b/resolve-undo.c index 639eb9c59f..77101f51c1 100644 --- a/resolve-undo.c +++ b/resolve-undo.c @@ -115,7 +115,7 @@ void resolve_undo_clear_index(struct index_state *istate) int unmerge_index_entry_at(struct index_state *istate, int pos) { - struct cache_entry *ce; + const struct cache_entry *ce; struct string_list_item *item; struct resolve_undo_info *ru; int i, err = 0, matched; @@ -167,7 +167,7 @@ void unmerge_marked_index(struct index_state *istate) return; for (i = 0; i < istate->cache_nr; i++) { - struct cache_entry *ce = istate->cache[i]; + const struct cache_entry *ce = istate->cache[i]; if (ce->ce_flags & CE_MATCHED) i = unmerge_index_entry_at(istate, i); } @@ -181,7 +181,7 @@ void unmerge_index(struct index_state *istate, const char **pathspec) return; for (i = 0; i < istate->cache_nr; i++) { - struct cache_entry *ce = istate->cache[i]; + const struct cache_entry *ce = istate->cache[i]; if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) continue; i = unmerge_index_entry_at(istate, i); diff --git a/revision.c b/revision.c index 2f0142f22e..84ccc0529b 100644 --- a/revision.c +++ b/revision.c @@ -1358,7 +1358,7 @@ static void prepare_show_merge(struct rev_info *revs) if (!active_nr) read_cache(); for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; + const struct cache_entry *ce = active_cache[i]; if (!ce_stage(ce)) continue; if (ce_path_match(ce, &revs->prune_data)) { diff --git a/run-command.c b/run-command.c index aece872e33..1b7f88eeb1 100644 --- a/run-command.c +++ b/run-command.c @@ -76,7 +76,10 @@ static inline void close_pair(int fd[2]) static inline void dup_devnull(int to) { int fd = open("/dev/null", O_RDWR); - dup2(fd, to); + if (fd < 0) + die_errno(_("open /dev/null failed")); + if (dup2(fd, to) < 0) + die_errno(_("dup2(%d,%d) failed"), fd, to); close(fd); } #endif diff --git a/run-command.h b/run-command.h index 221ce33140..6b985afd07 100644 --- a/run-command.h +++ b/run-command.h @@ -46,6 +46,7 @@ int finish_command(struct child_process *); int run_command(struct child_process *); extern char *find_hook(const char *name); +LAST_ARG_MUST_BE_NULL extern int run_hook(const char *index_file, const char *name, ...); #define RUN_COMMAND_NO_STDIN 1 diff --git a/sequencer.c b/sequencer.c index 425207ad5f..351548f57d 100644 --- a/sequencer.c +++ b/sequencer.c @@ -331,7 +331,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next, int i; strbuf_addstr(msgbuf, "\nConflicts:\n"); for (i = 0; i < active_nr;) { - struct cache_entry *ce = active_cache[i++]; + const struct cache_entry *ce = active_cache[i++]; if (ce_stage(ce)) { strbuf_addch(msgbuf, '\t'); strbuf_addstr(msgbuf, ce->name); @@ -371,8 +371,9 @@ static int is_index_unchanged(void) active_cache_tree = cache_tree(); if (!cache_tree_fully_valid(active_cache_tree)) - if (cache_tree_update(active_cache_tree, active_cache, - active_nr, 0)) + if (cache_tree_update(active_cache_tree, + (const struct cache_entry * const *)active_cache, + active_nr, 0)) return error(_("Unable to update cache tree\n")); return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.sha1); diff --git a/setup.c b/setup.c index 94c1e61bda..5262319be6 100644 --- a/setup.c +++ b/setup.c @@ -360,6 +360,7 @@ int is_inside_work_tree(void) void setup_work_tree(void) { + struct strbuf sb = STRBUF_INIT; const char *work_tree, *git_dir; static int initialized = 0; @@ -379,8 +380,10 @@ void setup_work_tree(void) if (getenv(GIT_WORK_TREE_ENVIRONMENT)) setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1); - set_git_dir(relative_path(git_dir, work_tree)); + set_git_dir(relative_path(git_dir, work_tree, &sb)); initialized = 1; + + strbuf_release(&sb); } static int check_repository_format_gently(const char *gitdir, int *nongit_ok) @@ -908,3 +911,15 @@ const char *resolve_gitdir(const char *suspect) return suspect; return read_gitfile(suspect); } + +/* if any standard file descriptor is missing open it to /dev/null */ +void sanitize_stdfds(void) +{ + int fd = open("/dev/null", O_RDWR, 0); + while (fd != -1 && fd < 2) + fd = dup(fd); + if (fd == -1) + die_errno("open /dev/null or dup failed"); + if (fd > 2) + close(fd); +} diff --git a/sha1_file.c b/sha1_file.c index 4c2365f48f..8e27db1bd2 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1306,6 +1306,26 @@ static int git_open_noatime(const char *name) } } +static int stat_sha1_file(const unsigned char *sha1, struct stat *st) +{ + char *name = sha1_file_name(sha1); + struct alternate_object_database *alt; + + if (!lstat(name, st)) + return 0; + + prepare_alt_odb(); + errno = ENOENT; + for (alt = alt_odb_list; alt; alt = alt->next) { + name = alt->name; + fill_sha1_path(name, sha1); + if (!lstat(alt->base, st)) + return 0; + } + + return -1; +} + static int open_sha1_file(const unsigned char *sha1) { int fd; @@ -1693,52 +1713,21 @@ static int retry_bad_packed_offset(struct packed_git *p, off_t obj_offset) return type; } - #define POI_STACK_PREALLOC 64 -static int packed_object_info(struct packed_git *p, off_t obj_offset, - unsigned long *sizep, int *rtype, - unsigned long *disk_sizep) +static enum object_type packed_to_object_type(struct packed_git *p, + off_t obj_offset, + enum object_type type, + struct pack_window **w_curs, + off_t curpos) { - struct pack_window *w_curs = NULL; - unsigned long size; - off_t curpos = obj_offset; - enum object_type type; off_t small_poi_stack[POI_STACK_PREALLOC]; off_t *poi_stack = small_poi_stack; int poi_stack_nr = 0, poi_stack_alloc = POI_STACK_PREALLOC; - type = unpack_object_header(p, &w_curs, &curpos, &size); - - if (rtype) - *rtype = type; /* representation type */ - - if (sizep) { - if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) { - off_t tmp_pos = curpos; - off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos, - type, obj_offset); - if (!base_offset) { - type = OBJ_BAD; - goto out; - } - *sizep = get_size_from_delta(p, &w_curs, tmp_pos); - if (*sizep == 0) { - type = OBJ_BAD; - goto out; - } - } else { - *sizep = size; - } - } - - if (disk_sizep) { - struct revindex_entry *revidx = find_pack_revindex(p, obj_offset); - *disk_sizep = revidx[1].offset - obj_offset; - } - while (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) { off_t base_offset; + unsigned long size; /* Push the object we're going to leave behind */ if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) { poi_stack_alloc = alloc_nr(poi_stack_nr); @@ -1749,11 +1738,11 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset, } poi_stack[poi_stack_nr++] = obj_offset; /* If parsing the base offset fails, just unwind */ - base_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset); + base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset); if (!base_offset) goto unwind; curpos = obj_offset = base_offset; - type = unpack_object_header(p, &w_curs, &curpos, &size); + type = unpack_object_header(p, w_curs, &curpos, &size); if (type <= OBJ_NONE) { /* If getting the base itself fails, we first * retry the base, otherwise unwind */ @@ -1780,7 +1769,6 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset, out: if (poi_stack != small_poi_stack) free(poi_stack); - unuse_pack(&w_curs); return type; unwind: @@ -1794,6 +1782,57 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset, goto out; } +static int packed_object_info(struct packed_git *p, off_t obj_offset, + struct object_info *oi) +{ + struct pack_window *w_curs = NULL; + unsigned long size; + off_t curpos = obj_offset; + enum object_type type; + + /* + * We always get the representation type, but only convert it to + * a "real" type later if the caller is interested. + */ + type = unpack_object_header(p, &w_curs, &curpos, &size); + + if (oi->sizep) { + if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) { + off_t tmp_pos = curpos; + off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos, + type, obj_offset); + if (!base_offset) { + type = OBJ_BAD; + goto out; + } + *oi->sizep = get_size_from_delta(p, &w_curs, tmp_pos); + if (*oi->sizep == 0) { + type = OBJ_BAD; + goto out; + } + } else { + *oi->sizep = size; + } + } + + if (oi->disk_sizep) { + struct revindex_entry *revidx = find_pack_revindex(p, obj_offset); + *oi->disk_sizep = revidx[1].offset - obj_offset; + } + + if (oi->typep) { + *oi->typep = packed_to_object_type(p, obj_offset, type, &w_curs, curpos); + if (*oi->typep < 0) { + type = OBJ_BAD; + goto out; + } + } + +out: + unuse_pack(&w_curs); + return type; +} + static void *unpack_compressed_entry(struct packed_git *p, struct pack_window **w_curs, off_t curpos, @@ -2363,8 +2402,8 @@ struct packed_git *find_sha1_pack(const unsigned char *sha1, } -static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *sizep, - unsigned long *disk_sizep) +static int sha1_loose_object_info(const unsigned char *sha1, + struct object_info *oi) { int status; unsigned long mapsize, size; @@ -2372,21 +2411,37 @@ static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *size git_zstream stream; char hdr[32]; + /* + * If we don't care about type or size, then we don't + * need to look inside the object at all. + */ + if (!oi->typep && !oi->sizep) { + if (oi->disk_sizep) { + struct stat st; + if (stat_sha1_file(sha1, &st) < 0) + return -1; + *oi->disk_sizep = st.st_size; + } + return 0; + } + map = map_sha1_file(sha1, &mapsize); if (!map) return -1; - if (disk_sizep) - *disk_sizep = mapsize; + if (oi->disk_sizep) + *oi->disk_sizep = mapsize; if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0) status = error("unable to unpack %s header", sha1_to_hex(sha1)); else if ((status = parse_sha1_header(hdr, &size)) < 0) status = error("unable to parse %s header", sha1_to_hex(sha1)); - else if (sizep) - *sizep = size; + else if (oi->sizep) + *oi->sizep = size; git_inflate_end(&stream); munmap(map, mapsize); - return status; + if (oi->typep) + *oi->typep = status; + return 0; } /* returns enum object_type or negative */ @@ -2394,37 +2449,37 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi) { struct cached_object *co; struct pack_entry e; - int status, rtype; + int rtype; co = find_cached_object(sha1); if (co) { + if (oi->typep) + *(oi->typep) = co->type; if (oi->sizep) *(oi->sizep) = co->size; if (oi->disk_sizep) *(oi->disk_sizep) = 0; oi->whence = OI_CACHED; - return co->type; + return 0; } if (!find_pack_entry(sha1, &e)) { /* Most likely it's a loose object. */ - status = sha1_loose_object_info(sha1, oi->sizep, oi->disk_sizep); - if (status >= 0) { + if (!sha1_loose_object_info(sha1, oi)) { oi->whence = OI_LOOSE; - return status; + return 0; } /* Not a loose object; someone else may have just packed it. */ reprepare_packed_git(); if (!find_pack_entry(sha1, &e)) - return status; + return -1; } - status = packed_object_info(e.p, e.offset, oi->sizep, &rtype, - oi->disk_sizep); - if (status < 0) { + rtype = packed_object_info(e.p, e.offset, oi); + if (rtype < 0) { mark_bad_packed_object(e.p, sha1); - status = sha1_object_info_extended(sha1, oi); + return sha1_object_info_extended(sha1, oi); } else if (in_delta_base_cache(e.p, e.offset)) { oi->whence = OI_DBCACHED; } else { @@ -2435,15 +2490,19 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi) rtype == OBJ_OFS_DELTA); } - return status; + return 0; } int sha1_object_info(const unsigned char *sha1, unsigned long *sizep) { - struct object_info oi = {0}; + enum object_type type; + struct object_info oi = {NULL}; + oi.typep = &type; oi.sizep = sizep; - return sha1_object_info_extended(sha1, &oi); + if (sha1_object_info_extended(sha1, &oi) < 0) + return -1; + return type; } static void *read_packed_sha1(const unsigned char *sha1, diff --git a/sha1_name.c b/sha1_name.c index 1a75fac05f..0cf0c28a6f 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -452,13 +452,15 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) int at, reflog_len, nth_prior = 0; if (len == 40 && !get_sha1_hex(str, sha1)) { - refs_found = dwim_ref(str, len, tmp_sha1, &real_ref); - if (refs_found > 0 && warn_ambiguous_refs) { - warning(warn_msg, len, str); - if (advice_object_name_warning) - fprintf(stderr, "%s\n", _(object_name_msg)); + if (warn_on_object_refname_ambiguity) { + refs_found = dwim_ref(str, len, tmp_sha1, &real_ref); + if (refs_found > 0 && warn_ambiguous_refs) { + warning(warn_msg, len, str); + if (advice_object_name_warning) + fprintf(stderr, "%s\n", _(object_name_msg)); + } + free(real_ref); } - free(real_ref); return 0; } @@ -1234,7 +1236,7 @@ static void diagnose_invalid_index_path(int stage, const char *filename) { struct stat st; - struct cache_entry *ce; + const struct cache_entry *ce; int pos; unsigned namelen = strlen(filename); unsigned fullnamelen; @@ -1328,7 +1330,7 @@ static int get_sha1_with_context_1(const char *name, */ if (name[0] == ':') { int stage = 0; - struct cache_entry *ce; + const struct cache_entry *ce; char *new_path = NULL; int pos; if (!only_to_die && namelen > 2 && name[1] == '/') { diff --git a/shallow.c b/shallow.c index cbe2526d8c..8a9c96d019 100644 --- a/shallow.c +++ b/shallow.c @@ -110,17 +110,12 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth, continue; *pointer = cur_depth; } - if (cur_depth < depth) { - if (p->next) - add_object_array(&p->item->object, - NULL, &stack); - else { - commit = p->item; - cur_depth = *(int *)commit->util; - } - } else { - commit_list_insert(p->item, &result); - p->item->object.flags |= shallow_flag; + if (p->next) + add_object_array(&p->item->object, + NULL, &stack); + else { + commit = p->item; + cur_depth = *(int *)commit->util; } } } diff --git a/shell.c b/shell.c index 1429870a8f..66350b220c 100644 --- a/shell.c +++ b/shell.c @@ -147,7 +147,6 @@ int main(int argc, char **argv) char *prog; const char **user_argv; struct commands *cmd; - int devnull_fd; int count; git_setup_gettext(); @@ -156,15 +155,10 @@ int main(int argc, char **argv) /* * Always open file descriptors 0/1/2 to avoid clobbering files - * in die(). It also avoids not messing up when the pipes are - * dup'ed onto stdin/stdout/stderr in the child processes we spawn. + * in die(). It also avoids messing up when the pipes are dup'ed + * onto stdin/stdout/stderr in the child processes we spawn. */ - devnull_fd = open("/dev/null", O_RDWR); - while (devnull_fd >= 0 && devnull_fd <= 2) - devnull_fd = dup(devnull_fd); - if (devnull_fd == -1) - die_errno("opening /dev/null failed"); - close (devnull_fd); + sanitize_stdfds(); /* * Special hack to pretend to be a CVS server diff --git a/streaming.c b/streaming.c index cac282f06b..debe904523 100644 --- a/streaming.c +++ b/streaming.c @@ -111,11 +111,11 @@ static enum input_source istream_source(const unsigned char *sha1, unsigned long size; int status; + oi->typep = type; oi->sizep = &size; status = sha1_object_info_extended(sha1, oi); if (status < 0) return stream_error; - *type = status; switch (oi->whence) { case OI_LOOSE: @@ -135,7 +135,7 @@ struct git_istream *open_istream(const unsigned char *sha1, struct stream_filter *filter) { struct git_istream *st; - struct object_info oi = {0}; + struct object_info oi = {NULL}; const unsigned char *real = lookup_replace_object(sha1); enum input_source src = istream_source(real, type, &oi); @@ -149,7 +149,7 @@ struct git_istream *open_istream(const unsigned char *sha1, return NULL; } } - if (st && filter) { + if (filter) { /* Add "&& !is_null_stream_filter(filter)" for performance */ struct git_istream *nst = attach_stream_filter(st, filter); if (!nst) diff --git a/submodule.c b/submodule.c index 85415d0057..3f0a3f9419 100644 --- a/submodule.c +++ b/submodule.c @@ -624,7 +624,7 @@ int fetch_populated_submodules(const struct argv_array *options, struct strbuf submodule_path = STRBUF_INIT; struct strbuf submodule_git_dir = STRBUF_INIT; struct strbuf submodule_prefix = STRBUF_INIT; - struct cache_entry *ce = active_cache[i]; + const struct cache_entry *ce = active_cache[i]; const char *git_dir, *name, *default_argv; if (!S_ISGITLINK(ce->ce_mode)) diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh index c56a77d237..0bfee001b4 100644 --- a/t/annotate-tests.sh +++ b/t/annotate-tests.sh @@ -2,11 +2,21 @@ # sourced from t8001-annotate.sh and t8002-blame.sh. check_count () { - head= - case "$1" in -h) head="$2"; shift; shift ;; esac - echo "$PROG file $head" >&4 - $PROG file $head >.result || return 1 - cat .result | perl -e ' + head= && + file='file' && + options= && + while : + do + case "$1" in + -h) head="$2"; shift; shift ;; + -f) file="$2"; shift; shift ;; + -*) options="$options $1"; shift ;; + *) break ;; + esac + done && + echo "$PROG $options $file $head" >&4 && + $PROG $options $file $head >actual && + perl -e ' my %expect = (@ARGV); my %count = map { $_ => 0 } keys %expect; while () { @@ -31,107 +41,246 @@ check_count () { print STDERR "Author $author (expected $value, attributed $count) $ok\n"; } exit($bad); - ' "$@" + ' "$@" file && - echo "lazy dog" >>file && - git add file && - GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" git commit -a -m "Initial."' - -test_expect_success \ - 'check all lines blamed on A' \ - 'check_count A 2' - -test_expect_success \ - 'Setup new lines blamed on B' \ - 'echo "2A quick brown fox jumps over the" >>file && - echo "lazy dog" >> file && - GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" git commit -a -m "Second."' - -test_expect_success \ - 'Two lines blamed on A, two on B' \ - 'check_count A 2 B 2' - -test_expect_success \ - 'merge-setup part 1' \ - 'git checkout -b branch1 master && - echo "3A slow green fox jumps into the" >> file && - echo "well." >> file && - GIT_AUTHOR_NAME="B1" GIT_AUTHOR_EMAIL="B1@test.git" git commit -a -m "Branch1-1"' - -test_expect_success \ - 'Two lines blamed on A, two on B, two on B1' \ - 'check_count A 2 B 2 B1 2' - -test_expect_success \ - 'merge-setup part 2' \ - 'git checkout -b branch2 master && - sed -e "s/2A quick brown/4A quick brown lazy dog/" < file > file.new && - mv file.new file && - GIT_AUTHOR_NAME="B2" GIT_AUTHOR_EMAIL="B2@test.git" git commit -a -m "Branch2-1"' - -test_expect_success \ - 'Two lines blamed on A, one on B, one on B2' \ - 'check_count A 2 B 1 B2 1' - -test_expect_success \ - 'merge-setup part 3' \ - 'git pull . branch1' - -test_expect_success \ - 'Two lines blamed on A, one on B, two on B1, one on B2' \ - 'check_count A 2 B 1 B1 2 B2 1' - -test_expect_success \ - 'Annotating an old revision works' \ - 'check_count -h master A 2 B 2' - -test_expect_success \ - 'Annotating an old revision works' \ - 'check_count -h master^ A 2' - -test_expect_success \ - 'merge-setup part 4' \ - 'echo "evil merge." >>file && - git commit -a --amend' - -test_expect_success \ - 'Two lines blamed on A, one on B, two on B1, one on B2, one on A U Thor' \ - 'check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1' - -test_expect_success \ - 'an incomplete line added' \ - 'echo "incomplete" | tr -d "\\012" >>file && - GIT_AUTHOR_NAME="C" GIT_AUTHOR_EMAIL="C@test.git" git commit -a -m "Incomplete"' - -test_expect_success \ - 'With incomplete lines.' \ - 'check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1 C 1' - -test_expect_success \ - 'some edit' \ - 'mv file file.orig && - { - cat file.orig && - echo - } | sed -e "s/^3A/99/" -e "/^1A/d" -e "/^incomplete/d" > file && - echo "incomplete" | tr -d "\\012" >>file && - GIT_AUTHOR_NAME="D" GIT_AUTHOR_EMAIL="D@test.git" git commit -a -m "edit"' - -test_expect_success \ - 'some edit' \ - 'check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1' - -test_expect_success \ - 'an obfuscated email added' \ - 'echo "No robots allowed" > file.new && - cat file >> file.new && - mv file.new file && - GIT_AUTHOR_NAME="E" GIT_AUTHOR_EMAIL="E at test dot git" git commit -a -m "norobots"' - -test_expect_success \ - 'obfuscated email parsed' \ - 'check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1' +test_expect_success 'setup A lines' ' + echo "1A quick brown fox jumps over the" >file && + echo "lazy dog" >>file && + git add file && + GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" \ + git commit -a -m "Initial." +' + +test_expect_success 'blame 1 author' ' + check_count A 2 +' + +test_expect_success 'setup B lines' ' + echo "2A quick brown fox jumps over the" >>file && + echo "lazy dog" >>file && + GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" \ + git commit -a -m "Second." +' + +test_expect_success 'blame 2 authors' ' + check_count A 2 B 2 +' + +test_expect_success 'setup B1 lines (branch1)' ' + git checkout -b branch1 master && + echo "3A slow green fox jumps into the" >>file && + echo "well." >>file && + GIT_AUTHOR_NAME="B1" GIT_AUTHOR_EMAIL="B1@test.git" \ + git commit -a -m "Branch1-1" +' + +test_expect_success 'blame 2 authors + 1 branch1 author' ' + check_count A 2 B 2 B1 2 +' + +test_expect_success 'setup B2 lines (branch2)' ' + git checkout -b branch2 master && + sed -e "s/2A quick brown/4A quick brown lazy dog/" file.new && + mv file.new file && + GIT_AUTHOR_NAME="B2" GIT_AUTHOR_EMAIL="B2@test.git" \ + git commit -a -m "Branch2-1" +' + +test_expect_success 'blame 2 authors + 1 branch2 author' ' + check_count A 2 B 1 B2 1 +' + +test_expect_success 'merge branch1 & branch2' ' + git pull . branch1 +' + +test_expect_success 'blame 2 authors + 2 merged-in authors' ' + check_count A 2 B 1 B1 2 B2 1 +' + +test_expect_success 'blame ancestor' ' + check_count -h master A 2 B 2 +' + +test_expect_success 'blame great-ancestor' ' + check_count -h master^ A 2 +' + +test_expect_success 'setup evil merge' ' + echo "evil merge." >>file && + git commit -a --amend +' + +test_expect_success 'blame evil merge' ' + check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1 +' + +test_expect_success 'setup incomplete line' ' + echo "incomplete" | tr -d "\\012" >>file && + GIT_AUTHOR_NAME="C" GIT_AUTHOR_EMAIL="C@test.git" \ + git commit -a -m "Incomplete" +' + +test_expect_success 'blame incomplete line' ' + check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1 C 1 +' + +test_expect_success 'setup edits' ' + mv file file.orig && + { + cat file.orig && + echo + } | sed -e "s/^3A/99/" -e "/^1A/d" -e "/^incomplete/d" >file && + echo "incomplete" | tr -d "\\012" >>file && + GIT_AUTHOR_NAME="D" GIT_AUTHOR_EMAIL="D@test.git" \ + git commit -a -m "edit" +' + +test_expect_success 'blame edits' ' + check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 +' + +test_expect_success 'setup obfuscated email' ' + echo "No robots allowed" >file.new && + cat file >>file.new && + mv file.new file && + GIT_AUTHOR_NAME="E" GIT_AUTHOR_EMAIL="E at test dot git" \ + git commit -a -m "norobots" +' + +test_expect_success 'blame obfuscated email' ' + check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1 +' + +test_expect_success 'blame -L 1 (all)' ' + check_count -L1 A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1 +' + +test_expect_success 'blame -L , (all)' ' + check_count -L, A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1 +' + +test_expect_success 'blame -L X (X to end)' ' + check_count -L5 B1 1 C 1 D 1 "A U Thor" 1 +' + +test_expect_success 'blame -L X, (X to end)' ' + check_count -L5, B1 1 C 1 D 1 "A U Thor" 1 +' + +test_expect_success 'blame -L ,Y (up to Y)' ' + check_count -L,3 A 1 B2 1 E 1 +' + +test_expect_success 'blame -L X,X' ' + check_count -L3,3 B2 1 +' + +test_expect_success 'blame -L X,Y' ' + check_count -L3,6 B 1 B1 1 B2 1 D 1 +' + +test_expect_success 'blame -L Y,X (undocumented)' ' + check_count -L6,3 B 1 B1 1 B2 1 D 1 +' + +test_expect_success 'blame -L X,+1' ' + check_count -L3,+1 B2 1 +' + +test_expect_success 'blame -L X,+N' ' + check_count -L3,+4 B 1 B1 1 B2 1 D 1 +' + +test_expect_success 'blame -L X,-1' ' + check_count -L3,-1 B2 1 +' + +test_expect_success 'blame -L X,-N' ' + check_count -L6,-4 B 1 B1 1 B2 1 D 1 +' + +test_expect_success 'blame -L /RE/ (RE to end)' ' + check_count -L/evil/ C 1 "A U Thor" 1 +' + +test_expect_success 'blame -L /RE/,/RE2/' ' + check_count -L/robot/,/green/ A 1 B 1 B2 1 D 1 E 1 +' + +test_expect_success 'blame -L X,/RE/' ' + check_count -L5,/evil/ B1 1 D 1 "A U Thor" 1 +' + +test_expect_success 'blame -L /RE/,Y' ' + check_count -L/99/,7 B1 1 D 1 "A U Thor" 1 +' + +test_expect_success 'blame -L /RE/,+N' ' + check_count -L/99/,+3 B1 1 D 1 "A U Thor" 1 +' + +test_expect_success 'blame -L /RE/,-N' ' + check_count -L/99/,-3 B 1 B2 1 D 1 +' + +test_expect_success 'blame -L X (X > nlines)' ' + test_must_fail $PROG -L12345 file +' + +test_expect_success 'blame -L ,Y (Y > nlines)' ' + test_must_fail $PROG -L,12345 file +' + +test_expect_success 'setup -L :regex' ' + tr Q "\\t" >hello.c <<-\EOF && + int main(int argc, const char *argv[]) + { + Qputs("hello"); + } + EOF + git add hello.c && + GIT_AUTHOR_NAME="F" GIT_AUTHOR_EMAIL="F@test.git" \ + git commit -m "hello" && + + mv hello.c hello.orig && + sed -e "/}/i\\ + Qputs(\"goodbye\");" hello.c && + GIT_AUTHOR_NAME="G" GIT_AUTHOR_EMAIL="G@test.git" \ + git commit -a -m "goodbye" && + + mv hello.c hello.orig && + echo "#include " >hello.c && + cat hello.orig >>hello.c && + tr Q "\\t" >>hello.c <<-\EOF + void mail() + { + Qputs("mail"); + } + EOF + GIT_AUTHOR_NAME="H" GIT_AUTHOR_EMAIL="H@test.git" \ + git commit -a -m "mail" +' + +test_expect_success 'blame -L :literal' ' + check_count -f hello.c -L:main F 4 G 1 +' + +test_expect_success 'blame -L :regex' ' + check_count -f hello.c "-L:m[a-z][a-z]l" H 4 +' + +test_expect_success 'blame -L :nomatch' ' + test_must_fail $PROG -L:nomatch hello.c +' + +test_expect_success 'blame -L bogus' ' + test_must_fail $PROG -L file && + test_must_fail $PROG -L1,+ file && + test_must_fail $PROG -L1,- file && + test_must_fail $PROG -LX file && + test_must_fail $PROG -L1,X file && + test_must_fail $PROG -L1,+N file && + test_must_fail $PROG -L1,-N file +' diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh index a56db804cb..c29342d6bc 100755 --- a/t/t0008-ignores.sh +++ b/t/t0008-ignores.sh @@ -697,13 +697,21 @@ test_expect_success PIPE 'streaming support for --stdin' ' # shell, and then echo to the fd. We make sure to close it at # the end, so that the subprocess does get EOF and dies # properly. + # + # Similarly, we must keep "out" open so that check-ignore does + # not ever get SIGPIPE trying to write to us. Not only would that + # produce incorrect results, but then there would be no writer on the + # other end of the pipe, and we would potentially block forever trying + # to open it. exec 9>in && + exec 8&-" && + test_when_finished "exec 8<&-" && echo >&9 one && - read response &9 two && - read response $2" \ - "test \"\$(test-path-utils normalize_path_copy '$1')\" = '$2'" + "test \"\$(test-path-utils normalize_path_copy '$1')\" = '$expected'" +} + +relative_path() { + expected=$(test-path-utils mingw_path "$3") + test_expect_success $4 "relative path: $1 $2 => $3" \ + "test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'" } # On Windows, we are using MSYS's bash, which mangles the paths. @@ -34,8 +41,8 @@ ancestor() { test \"\$actual\" = '$expected'" } -# Absolute path tests must be skipped on Windows because due to path mangling -# the test program never sees a POSIX-style absolute path +# Some absolute path tests should be skipped on Windows due to path mangling +# on POSIX-style absolute paths case $(uname -s) in *MINGW*) ;; @@ -68,30 +75,30 @@ norm_path d1/s1//../s2/../../d2 d2 norm_path d1/.../d2 d1/.../d2 norm_path d1/..././../d2 d1/d2 -norm_path / / POSIX +norm_path / / norm_path // / POSIX norm_path /// / POSIX -norm_path /. / POSIX +norm_path /. / norm_path /./ / POSIX norm_path /./.. ++failed++ POSIX -norm_path /../. ++failed++ POSIX +norm_path /../. ++failed++ norm_path /./../.// ++failed++ POSIX norm_path /dir/.. / POSIX norm_path /dir/sub/../.. / POSIX norm_path /dir/sub/../../.. ++failed++ POSIX -norm_path /dir /dir POSIX -norm_path /dir// /dir/ POSIX -norm_path /./dir /dir POSIX -norm_path /dir/. /dir/ POSIX -norm_path /dir///./ /dir/ POSIX -norm_path /dir//sub/.. /dir/ POSIX -norm_path /dir/sub/../ /dir/ POSIX +norm_path /dir /dir +norm_path /dir// /dir/ +norm_path /./dir /dir +norm_path /dir/. /dir/ +norm_path /dir///./ /dir/ +norm_path /dir//sub/.. /dir/ +norm_path /dir/sub/../ /dir/ norm_path //dir/sub/../. /dir/ POSIX -norm_path /dir/s1/../s2/ /dir/s2/ POSIX -norm_path /d1/s1///s2/..//../s3/ /d1/s3/ POSIX -norm_path /d1/s1//../s2/../../d2 /d2 POSIX -norm_path /d1/.../d2 /d1/.../d2 POSIX -norm_path /d1/..././../d2 /d1/d2 POSIX +norm_path /dir/s1/../s2/ /dir/s2/ +norm_path /d1/s1///s2/..//../s3/ /d1/s3/ +norm_path /d1/s1//../s2/../../d2 /d2 +norm_path /d1/.../d2 /d1/.../d2 +norm_path /d1/..././../d2 /d1/d2 ancestor / / -1 ancestor /foo / 0 @@ -183,4 +190,33 @@ test_expect_success SYMLINKS 'real path works on symlinks' ' test "$sym" = "$(test-path-utils real_path "$dir2/syml")" ' +relative_path /a/b/c/ /a/b/ c/ +relative_path /a/b/c/ /a/b c/ +relative_path /a//b//c/ //a/b// c/ POSIX +relative_path /a/b /a/b ./ +relative_path /a/b/ /a/b ./ +relative_path /a /a/b ../ +relative_path / /a/b/ ../../ +relative_path /a/c /a/b/ ../c +relative_path /a/c /a/b ../c +relative_path /x/y /a/b/ ../../x/y +relative_path /a/b "" /a/b +relative_path /a/b "" /a/b +relative_path a/b/c/ a/b/ c/ +relative_path a/b/c/ a/b c/ +relative_path a/b//c a//b c +relative_path a/b/ a/b/ ./ +relative_path a/b/ a/b ./ +relative_path a a/b ../ +relative_path x/y a/b ../../x/y +relative_path a/c a/b ../c +relative_path a/b "" a/b +relative_path a/b "" a/b +relative_path "" /a/b ./ +relative_path "" "" ./ +relative_path "" "" ./ +relative_path "" "" ./ +relative_path "" "" ./ +relative_path "" /a/b ./ + test_done diff --git a/t/t1307-config-blob.sh b/t/t1307-config-blob.sh new file mode 100755 index 0000000000..fdc257e66f --- /dev/null +++ b/t/t1307-config-blob.sh @@ -0,0 +1,70 @@ +#!/bin/sh + +test_description='support for reading config from a blob' +. ./test-lib.sh + +test_expect_success 'create config blob' ' + cat >config <<-\EOF && + [some] + value = 1 + EOF + git add config && + git commit -m foo +' + +test_expect_success 'list config blob contents' ' + echo some.value=1 >expect && + git config --blob=HEAD:config --list >actual && + test_cmp expect actual +' + +test_expect_success 'fetch value from blob' ' + echo true >expect && + git config --blob=HEAD:config --bool some.value >actual && + test_cmp expect actual +' + +test_expect_success 'reading non-existing value from blob is an error' ' + test_must_fail git config --blob=HEAD:config non.existing +' + +test_expect_success 'reading from blob and file is an error' ' + test_must_fail git config --blob=HEAD:config --system --list +' + +test_expect_success 'reading from missing ref is an error' ' + test_must_fail git config --blob=HEAD:doesnotexist --list +' + +test_expect_success 'reading from non-blob is an error' ' + test_must_fail git config --blob=HEAD --list +' + +test_expect_success 'setting a value in a blob is an error' ' + test_must_fail git config --blob=HEAD:config some.value foo +' + +test_expect_success 'deleting a value in a blob is an error' ' + test_must_fail git config --blob=HEAD:config --unset some.value +' + +test_expect_success 'editing a blob is an error' ' + test_must_fail git config --blob=HEAD:config --edit +' + +test_expect_success 'parse errors in blobs are properly attributed' ' + cat >config <<-\EOF && + [some] + value = " + EOF + git add config && + git commit -m broken && + + test_must_fail git config --blob=HEAD:config some.value 2>err && + + # just grep for our token as the exact error message is likely to + # change or be internationalized + grep "HEAD:config" err +' + +test_done diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh new file mode 100755 index 0000000000..3e500ed7da --- /dev/null +++ b/t/t1403-show-ref.sh @@ -0,0 +1,167 @@ +#!/bin/sh + +test_description='show-ref' +. ./test-lib.sh + +test_expect_success setup ' + test_commit A && + git tag -f -a -m "annotated A" A && + git checkout -b side && + test_commit B && + git tag -f -a -m "annotated B" B && + git checkout master && + test_commit C && + git branch B A^0 +' + +test_expect_success 'show-ref' ' + echo $(git rev-parse refs/tags/A) refs/tags/A >expect && + + git show-ref A >actual && + test_cmp expect actual && + + git show-ref tags/A >actual && + test_cmp expect actual && + + git show-ref refs/tags/A >actual && + test_cmp expect actual && + + >expect && + + test_must_fail git show-ref D >actual + test_cmp expect actual +' + +test_expect_success 'show-ref -q' ' + >expect && + + git show-ref -q A >actual && + test_cmp expect actual && + + git show-ref -q tags/A >actual && + test_cmp expect actual && + + git show-ref -q refs/tags/A >actual && + test_cmp expect actual && + + test_must_fail git show-ref -q D >actual && + test_cmp expect actual +' + +test_expect_success 'show-ref --verify' ' + echo $(git rev-parse refs/tags/A) refs/tags/A >expect && + + git show-ref --verify refs/tags/A >actual && + test_cmp expect actual && + + >expect && + + test_must_fail git show-ref --verify A >actual && + test_cmp expect actual && + + test_must_fail git show-ref --verify tags/A >actual && + test_cmp expect actual && + + test_must_fail git show-ref --verify D >actual + test_cmp expect actual +' + +test_expect_success 'show-ref --verify -q' ' + >expect && + + git show-ref --verify -q refs/tags/A >actual && + test_cmp expect actual && + + test_must_fail git show-ref --verify -q A >actual && + test_cmp expect actual && + + test_must_fail git show-ref --verify -q tags/A >actual && + test_cmp expect actual && + + test_must_fail git show-ref --verify -q D >actual + test_cmp expect actual +' + +test_expect_success 'show-ref -d' ' + { + echo $(git rev-parse refs/tags/A) refs/tags/A && + echo $(git rev-parse refs/tags/A^0) "refs/tags/A^{}" + echo $(git rev-parse refs/tags/C) refs/tags/C + } >expect && + git show-ref -d A C >actual && + test_cmp expect actual && + + git show-ref -d tags/A tags/C >actual && + test_cmp expect actual && + + git show-ref -d refs/tags/A refs/tags/C >actual && + test_cmp expect actual && + + echo $(git rev-parse refs/heads/master) refs/heads/master >expect && + git show-ref -d master >actual && + test_cmp expect actual && + + git show-ref -d heads/master >actual && + test_cmp expect actual && + + git show-ref -d refs/heads/master >actual && + test_cmp expect actual + + git show-ref -d --verify refs/heads/master >actual && + test_cmp expect actual + + >expect && + + test_must_fail git show-ref -d --verify master >actual && + test_cmp expect actual && + + test_must_fail git show-ref -d --verify heads/master >actual && + test_cmp expect actual + +' + +test_expect_success 'show-ref --heads, --tags, --head, pattern' ' + for branch in B master side + do + echo $(git rev-parse refs/heads/$branch) refs/heads/$branch + done >expect.heads && + git show-ref --heads >actual && + test_cmp expect.heads actual && + + for tag in A B C + do + echo $(git rev-parse refs/tags/$tag) refs/tags/$tag + done >expect.tags && + git show-ref --tags >actual && + test_cmp expect.tags actual && + + cat expect.heads expect.tags >expect && + git show-ref --heads --tags >actual && + test_cmp expect actual && + + { + echo $(git rev-parse HEAD) HEAD && + cat expect.heads expect.tags + } >expect && + git show-ref --heads --tags --head >actual && + test_cmp expect actual && + + { + echo $(git rev-parse HEAD) HEAD && + echo $(git rev-parse refs/heads/B) refs/heads/B + echo $(git rev-parse refs/tags/B) refs/tags/B + } >expect && + git show-ref --head B >actual && + test_cmp expect actual && + + { + echo $(git rev-parse HEAD) HEAD && + echo $(git rev-parse refs/heads/B) refs/heads/B + echo $(git rev-parse refs/tags/B) refs/tags/B + echo $(git rev-parse refs/tags/B^0) "refs/tags/B^{}" + } >expect && + git show-ref --head -d B >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t2202-add-addremove.sh b/t/t2202-add-addremove.sh index 6a8151064c..fc8b59e7f7 100755 --- a/t/t2202-add-addremove.sh +++ b/t/t2202-add-addremove.sh @@ -41,4 +41,14 @@ test_expect_success 'git add --all' ' test_cmp expect actual ' +test_expect_success 'Just "git add" is a no-op' ' + git reset --hard && + echo >will-remove && + >will-not-be-added && + git add && + git diff-index --name-status --cached HEAD >actual && + >expect && + test_cmp expect actual +' + test_done diff --git a/t/t3032-merge-recursive-options.sh b/t/t3032-merge-recursive-options.sh index 2b17311cb0..5fd7bbb652 100755 --- a/t/t3032-merge-recursive-options.sh +++ b/t/t3032-merge-recursive-options.sh @@ -14,7 +14,7 @@ test_description='merge-recursive options . ./test-lib.sh test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b -test_have_prereq MINGW && export GREP_OPTIONS=-U +test_have_prereq GREP_STRIPS_CR && export GREP_OPTIONS=-U test_expect_success 'setup' ' conflict_hunks () { diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh index 6ddd46915d..8de36b7d12 100755 --- a/t/t4000-diff-format.sh +++ b/t/t4000-diff-format.sh @@ -15,17 +15,17 @@ line 3' cat path0 >path1 chmod +x path1 -test_expect_success \ - 'update-index --add two files with and without +x.' \ - 'git update-index --add path0 path1' +test_expect_success 'update-index --add two files with and without +x.' ' + git update-index --add path0 path1 +' mv path0 path0- sed -e 's/line/Line/' path0 chmod +x path0 rm -f path1 -test_expect_success \ - 'git diff-files -p after editing work tree.' \ - 'git diff-files -p >current' +test_expect_success 'git diff-files -p after editing work tree.' ' + git diff-files -p >actual +' # that's as far as it comes if [ "$(git config --get core.filemode)" = false ] @@ -55,8 +55,38 @@ deleted file mode 100755 -line 3 EOF -test_expect_success \ - 'validate git diff-files -p output.' \ - 'compare_diff_patch current expected' +test_expect_success 'validate git diff-files -p output.' ' + compare_diff_patch expected actual +' + +test_expect_success 'git diff-files -s after editing work tree' ' + git diff-files -s >actual 2>err && + test_must_be_empty actual && + test_must_be_empty err +' + +test_expect_success 'git diff-files --no-patch as synonym for -s' ' + git diff-files --no-patch >actual 2>err && + test_must_be_empty actual && + test_must_be_empty err +' + +test_expect_success 'git diff-files --no-patch --patch shows the patch' ' + git diff-files --no-patch --patch >actual && + compare_diff_patch expected actual +' + +test_expect_success 'git diff-files --no-patch --patch-with-raw shows the patch and raw data' ' + git diff-files --no-patch --patch-with-raw >actual && + grep -q "^:100644 100755 .* 0000000000000000000000000000000000000000 M path0\$" actual && + tail -n +4 actual >actual-patch && + compare_diff_patch expected actual-patch +' + +test_expect_success 'git diff-files --patch --no-patch does not show the patch' ' + git diff-files --patch --no-patch >actual 2>err && + test_must_be_empty actual && + test_must_be_empty err +' test_done diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh index 842b7549ec..baa4685dcc 100755 --- a/t/t4203-mailmap.sh +++ b/t/t4203-mailmap.sh @@ -13,6 +13,11 @@ fuzz_blame () { } test_expect_success setup ' + cat >contacts <<-\EOF && + A U Thor + nick1 + EOF + echo one >one && git add one && test_tick && @@ -23,6 +28,44 @@ test_expect_success setup ' git commit --author "nick1 " -m second ' +test_expect_success 'check-mailmap no arguments' ' + test_must_fail git check-mailmap +' + +test_expect_success 'check-mailmap arguments' ' + cat >expect <<-\EOF && + A U Thor + nick1 + EOF + git check-mailmap \ + "A U Thor " \ + "nick1 " >actual && + test_cmp expect actual +' + +test_expect_success 'check-mailmap --stdin' ' + cat >expect <<-\EOF && + A U Thor + nick1 + EOF + git check-mailmap --stdin actual && + test_cmp expect actual +' + +test_expect_success 'check-mailmap --stdin arguments' ' + cat >expect <<-\EOF && + Internal Guy + EOF + cat >expect && + git check-mailmap --stdin "Internal Guy " \ + actual && + test_cmp expect actual +' + +test_expect_success 'check-mailmap bogus contact' ' + test_must_fail git check-mailmap bogus +' + cat >expect <<\EOF A U Thor (1): initial @@ -247,6 +290,24 @@ test_expect_success 'cleanup after mailmap.blob tests' ' rm -f .mailmap ' +test_expect_success 'single-character name' ' + echo " 1 A " >expect && + echo " 1 nick1 " >>expect && + echo "A " >.mailmap && + test_when_finished "rm .mailmap" && + git shortlog -es HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'preserve canonical email case' ' + echo " 1 A U Thor " >expect && + echo " 1 nick1 " >>expect && + echo " " >.mailmap && + test_when_finished "rm .mailmap" && + git shortlog -es HEAD >actual && + test_cmp expect actual +' + # Extended mailmap configurations should give us the following output for shortlog cat >expect <<\EOF A U Thor (1): diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh index 7776f93e3d..7665d6785c 100755 --- a/t/t4211-line-log.sh +++ b/t/t4211-line-log.sh @@ -64,4 +64,17 @@ test_bad_opts "-L 1,1000:b.c" "has only.*lines" test_bad_opts "-L :b.c" "argument.*not of the form" test_bad_opts "-L :foo:b.c" "no match" +# There is a separate bug when an empty -L range is the first -L encountered, +# thus to demonstrate this particular bug, the empty -L range must follow a +# non-empty -L range. +test_expect_success '-L {empty-range} (any -L)' ' + n=$(expr $(wc -l expect && + tag_object=$(git rev-parse refs/tags/A) && + git name-rev --tags --name-only $tag_object >actual && + test_cmp expect actual && + + echo "A^0" >expect && + tagged_commit=$(git rev-parse "refs/tags/A^0") && + git name-rev --tags --name-only $tagged_commit >actual && + test_cmp expect actual +' + +test_expect_success 'describe --contains with the exact tags' ' + echo "A^0" >expect && + tag_object=$(git rev-parse refs/tags/A) && + git describe --contains $tag_object >actual && + test_cmp expect actual && + + echo "A^0" >expect && + tagged_commit=$(git rev-parse "refs/tags/A^0") && + git describe --contains $tagged_commit >actual && + test_cmp expect actual +' + test_done diff --git a/t/t7301-clean-interactive.sh b/t/t7301-clean-interactive.sh new file mode 100755 index 0000000000..4e6055d06a --- /dev/null +++ b/t/t7301-clean-interactive.sh @@ -0,0 +1,439 @@ +#!/bin/sh + +test_description='git clean -i basic tests' + +. ./test-lib.sh + +test_expect_success 'setup' ' + + mkdir -p src && + touch src/part1.c Makefile && + echo build >.gitignore && + echo \*.o >>.gitignore && + git add . && + git commit -m setup && + touch src/part2.c README && + git add . + +' + +test_expect_success 'git clean -i (clean)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + echo c | git clean -i && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test -f docs/manual.txt && + test ! -f src/part3.c && + test ! -f src/part3.h && + test ! -f src/part4.c && + test ! -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -i (quit)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + echo q | git clean -i && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test -f docs/manual.txt && + test -f src/part3.c && + test -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -i (Ctrl+D)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + echo "\04" | git clean -i && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test -f docs/manual.txt && + test -f src/part3.c && + test -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (filter all)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo f; echo "*"; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test -f docs/manual.txt && + test -f src/part3.c && + test -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (filter patterns)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo f; echo "part3.* *.out"; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test ! -f docs/manual.txt && + test -f src/part3.c && + test -f src/part3.h && + test ! -f src/part4.c && + test ! -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (filter patterns 2)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo f; echo "* !*.out"; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test -f docs/manual.txt && + test -f src/part3.c && + test -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (select - all)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo s; echo "*"; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test ! -f docs/manual.txt && + test ! -f src/part3.c && + test ! -f src/part3.h && + test ! -f src/part4.c && + test ! -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (select - none)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo s; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test -f docs/manual.txt && + test -f src/part3.c && + test -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (select - number)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo s; echo 3; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test -f docs/manual.txt && + test ! -f src/part3.c && + test -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (select - number 2)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo s; echo 2 3; echo 5; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test ! -f docs/manual.txt && + test ! -f src/part3.c && + test -f src/part3.h && + test ! -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (select - number 3)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo s; echo 3,4 5; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test -f docs/manual.txt && + test ! -f src/part3.c && + test ! -f src/part3.h && + test ! -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (select - range)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo s; echo 1,3-4; echo 2; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test ! -f src/part3.c && + test ! -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test ! -f docs/manual.txt && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (select - range 2)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo s; echo 4- 1; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test -f docs/manual.txt && + test -f src/part3.c && + test ! -f src/part3.h && + test ! -f src/part4.c && + test ! -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (inverse select)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo s; echo "*"; echo -5- 1 -2; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test -f docs/manual.txt && + test ! -f src/part3.c && + test ! -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (ask)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo a; echo Y; echo y; echo no; echo yes; echo bad; echo) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test ! -f docs/manual.txt && + test -f src/part3.c && + test ! -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (ask - Ctrl+D)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo a; echo Y; echo no; echo yes; echo "\04") | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test -f docs/manual.txt && + test ! -f src/part3.c && + test -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id with prefix and path (filter)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (cd build/ && \ + (echo f; echo "docs"; echo "*.h"; echo ; echo c) | \ + git clean -id ..) && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test -f docs/manual.txt && + test ! -f src/part3.c && + test -f src/part3.h && + test ! -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id with prefix and path (select by name)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (cd build/ && \ + (echo s; echo "../docs/"; echo "../src/part3.c"; \ + echo "../src/part4.c"; echo; echo c) | \ + git clean -id ..) && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test ! -f docs/manual.txt && + test ! -f src/part3.c && + test -f src/part3.h && + test ! -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id with prefix and path (ask)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (cd build/ && \ + (echo a; echo Y; echo y; echo no; echo yes; echo bad; echo) | \ + git clean -id ..) && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test ! -f docs/manual.txt && + test -f src/part3.c && + test ! -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_done diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 3ff5fb853c..10aa028017 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -502,7 +502,7 @@ test_expect_success 'option --ff-only overwrites --no-ff' ' test_must_fail git merge --no-ff --ff-only c2 ' -test_expect_success 'option --ff-only overwrites merge.ff=only config' ' +test_expect_success 'option --no-ff overrides merge.ff=only config' ' git reset --hard c0 && test_config merge.ff only && git merge --no-ff c1 diff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh index 41962f04a7..72176e42c1 100755 --- a/t/t8001-annotate.sh +++ b/t/t8001-annotate.sh @@ -6,9 +6,9 @@ test_description='git annotate' PROG='git annotate' . "$TEST_DIRECTORY"/annotate-tests.sh -test_expect_success 'Annotating an old revision works' ' - git annotate file master >result && - awk "{ print \$3; }" authors && +test_expect_success 'annotate old revision' ' + git annotate file master >actual && + awk "{ print \$3; }" authors && test 2 = $(grep A " 1 "" 1 "" 1 "" 1 "" 1 "" 1 "" 1 "" 1 +test_expect_success 'blame --show-email' ' + check_count \ + "" 1 \ + "" 1 \ + "" 1 \ + "" 1 \ + "" 1 \ + "" 1 \ + "" 1 \ + "" 1 ' test_done diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh index 9730821c30..2bf142d09c 100755 --- a/t/t9801-git-p4-branch.sh +++ b/t/t9801-git-p4-branch.sh @@ -469,9 +469,11 @@ test_expect_success 'use-client-spec detect-branches skips branches setup' ' View: //depot/usecs/b1/... //depot/usecs/b3/... EOF - echo b3/b3-file3 >b3/b3-file3 && - p4 add b3/b3-file3 && - p4 submit -d "b3/b3-file3" + echo b3/b3-file3_1 >b3/b3-file3_1 && + echo b3/b3-file3_2 >b3/b3-file3_2 && + p4 add b3/b3-file3_1 && + p4 add b3/b3-file3_2 && + p4 submit -d "b3/b3-file3_1 b3/b3-file3_2" ) ' @@ -487,6 +489,21 @@ test_expect_success 'use-client-spec detect-branches skips branches' ' ) ' +test_expect_success 'use-client-spec detect-branches skips files in branches' ' + client_view "//depot/usecs/... //client/..." \ + "-//depot/usecs/b3/b3-file3_1 //client/b3/b3-file3_1" && + test_when_finished cleanup_git && + test_create_repo "$git" && + ( + cd "$git" && + git p4 sync --detect-branches --use-client-spec //depot/usecs@all && + git checkout -b master p4/usecs/b3 && + test_path_is_file b1-file1 && + test_path_is_file b3-file3_2 && + test_path_is_missing b3-file3_1 + ) +' + test_expect_success 'kill p4d' ' kill_p4d ' diff --git a/t/test-lib.sh b/t/test-lib.sh index b490283182..1aa27bdbbf 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -825,6 +825,7 @@ case $(uname -s) in test_set_prereq MINGW test_set_prereq NOT_CYGWIN test_set_prereq SED_STRIPS_CR + test_set_prereq GREP_STRIPS_CR ;; *CYGWIN*) test_set_prereq POSIXPERM @@ -832,6 +833,7 @@ case $(uname -s) in test_set_prereq NOT_MINGW test_set_prereq CYGWIN test_set_prereq SED_STRIPS_CR + test_set_prereq GREP_STRIPS_CR ;; *) test_set_prereq POSIXPERM diff --git a/templates/hooks--pre-commit.sample b/templates/hooks--pre-commit.sample index 18c4829765..586e3bf94d 100755 --- a/templates/hooks--pre-commit.sample +++ b/templates/hooks--pre-commit.sample @@ -15,13 +15,13 @@ else against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi -# If you want to allow non-ascii filenames set this variable to true. +# If you want to allow non-ASCII filenames set this variable to true. allownonascii=$(git config hooks.allownonascii) # Redirect output to stderr. exec 1>&2 -# Cross platform projects tend to avoid non-ascii filenames; prevent +# Cross platform projects tend to avoid non-ASCII filenames; prevent # them from being added to the repository. We exploit the fact that the # printable range starts at the space character and ends with tilde. if [ "$allownonascii" != "true" ] && @@ -31,18 +31,17 @@ if [ "$allownonascii" != "true" ] && test $(git diff --cached --name-only --diff-filter=A -z $against | LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 then - echo "Error: Attempt to add a non-ascii file name." - echo - echo "This can cause problems if you want to work" - echo "with people on other platforms." - echo - echo "To be portable it is advisable to rename the file ..." - echo - echo "If you know what you are doing you can disable this" - echo "check using:" - echo - echo " git config hooks.allownonascii true" - echo + cat <<\EOF +Error: Attempt to add a non-ASCII file name. + +This can cause problems if you want to work with people on other platforms. + +To be portable it is advisable to rename the file. + +If you know what you are doing you can disable this check using: + + git config hooks.allownonascii true +EOF exit 1 fi diff --git a/test-dump-cache-tree.c b/test-dump-cache-tree.c index a6ffdf39d5..47eab9765f 100644 --- a/test-dump-cache-tree.c +++ b/test-dump-cache-tree.c @@ -59,6 +59,8 @@ int main(int ac, char **av) struct cache_tree *another = cache_tree(); if (read_cache() < 0) die("unable to read index file"); - cache_tree_update(another, active_cache, active_nr, WRITE_TREE_DRY_RUN); + cache_tree_update(another, + (const struct cache_entry * const *)active_cache, + active_nr, WRITE_TREE_DRY_RUN); return dump_cache_tree(active_cache_tree, another, ""); } diff --git a/test-path-utils.c b/test-path-utils.c index 0092cbf354..bb975e4d3e 100644 --- a/test-path-utils.c +++ b/test-path-utils.c @@ -28,6 +28,19 @@ static int normalize_ceiling_entry(struct string_list_item *item, void *unused) return 1; } +static void normalize_argv_string(const char **var, const char *input) +{ + if (!strcmp(input, "")) + *var = NULL; + else if (!strcmp(input, "")) + *var = ""; + else + *var = input; + + if (*var && (**var == '<' || **var == '(')) + die("Bad value: %s\n", input); +} + int main(int argc, char **argv) { if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) { @@ -103,6 +116,25 @@ int main(int argc, char **argv) return 0; } + if (argc == 3 && !strcmp(argv[1], "mingw_path")) { + puts(argv[2]); + return 0; + } + + if (argc == 4 && !strcmp(argv[1], "relative_path")) { + struct strbuf sb = STRBUF_INIT; + const char *in, *prefix, *rel; + normalize_argv_string(&in, argv[2]); + normalize_argv_string(&prefix, argv[3]); + rel = relative_path(in, prefix, &sb); + if (!rel) + puts("(null)"); + else + puts(strlen(rel) > 0 ? rel : "(empty)"); + strbuf_release(&sb); + return 0; + } + fprintf(stderr, "%s: unknown function name: %s\n", argv[0], argv[1] ? argv[1] : "(there was none)"); return 1; diff --git a/trace.c b/trace.c index 5ec0e3bd16..3d744d1d4d 100644 --- a/trace.c +++ b/trace.c @@ -75,6 +75,7 @@ static void trace_vprintf(const char *key, const char *fmt, va_list ap) strbuf_release(&buf); } +__attribute__((format (printf, 2, 3))) static void trace_printf_key(const char *key, const char *fmt, ...) { va_list ap; diff --git a/transport-helper.c b/transport-helper.c index db9bd18298..45a35df66e 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -982,6 +982,7 @@ int transport_helper_init(struct transport *transport, const char *name) #define PBUFFERSIZE 8192 /* Print bidirectional transfer loop debug message. */ +__attribute__((format (printf, 1, 2))) static void transfer_debug(const char *fmt, ...) { va_list args; @@ -1067,7 +1068,7 @@ static int udt_do_read(struct unidirectional_transfer *t) return -1; } else if (bytes == 0) { transfer_debug("%s EOF (with %i bytes in buffer)", - t->src_name, t->bufuse); + t->src_name, (int)t->bufuse); t->state = SSTATE_FLUSHING; } else if (bytes > 0) { t->bufuse += bytes; diff --git a/tree-walk.c b/tree-walk.c index 6e30ef9d04..c626135234 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -323,7 +323,6 @@ static inline int prune_traversal(struct name_entry *e, int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info) { - int ret = 0; int error = 0; struct name_entry *entry = xmalloc(n*sizeof(*entry)); int i; @@ -341,6 +340,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info) strbuf_setlen(&base, info->pathlen); } for (;;) { + int trees_used; unsigned long mask, dirmask; const char *first = NULL; int first_len = 0; @@ -404,15 +404,14 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info) if (interesting < 0) break; if (interesting) { - ret = info->fn(n, mask, dirmask, entry, info); - if (ret < 0) { - error = ret; + trees_used = info->fn(n, mask, dirmask, entry, info); + if (trees_used < 0) { + error = trees_used; if (!info->show_all_errors) break; } - mask &= ret; + mask &= trees_used; } - ret = 0; for (i = 0; i < n; i++) if (mask & (1ul << i)) update_extended_entry(tx + i, entry + i); diff --git a/tree.c b/tree.c index 62fed632d8..c0e568c08f 100644 --- a/tree.c +++ b/tree.c @@ -159,7 +159,7 @@ int read_tree(struct tree *tree, int stage, struct pathspec *match) * sort at the end. */ for (i = 0; !fn && i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; + const struct cache_entry *ce = active_cache[i]; if (ce_stage(ce) == stage) fn = read_one_entry; } diff --git a/unpack-trees.c b/unpack-trees.c index b27f2a62e8..bf01717015 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -179,7 +179,7 @@ static void display_error_msgs(struct unpack_trees_options *o) * Unlink the last component and schedule the leading directories for * removal, such that empty directories get removed. */ -static void unlink_entry(struct cache_entry *ce) +static void unlink_entry(const struct cache_entry *ce) { if (!check_leading_path(ce->name, ce_namelen(ce))) return; @@ -199,7 +199,7 @@ static int check_updates(struct unpack_trees_options *o) if (o->update && o->verbose_update) { for (total = cnt = 0; cnt < index->cache_nr; cnt++) { - struct cache_entry *ce = index->cache[cnt]; + const struct cache_entry *ce = index->cache[cnt]; if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE)) total++; } @@ -212,7 +212,7 @@ static int check_updates(struct unpack_trees_options *o) if (o->update) git_attr_set_direction(GIT_ATTR_CHECKOUT, &o->result); for (i = 0; i < index->cache_nr; i++) { - struct cache_entry *ce = index->cache[i]; + const struct cache_entry *ce = index->cache[i]; if (ce->ce_flags & CE_WT_REMOVE) { display_progress(progress, ++cnt); @@ -376,7 +376,7 @@ static struct cache_entry *next_cache_entry(struct unpack_trees_options *o) return NULL; } -static void add_same_unmerged(struct cache_entry *ce, +static void add_same_unmerged(const struct cache_entry *ce, struct unpack_trees_options *o) { struct index_state *index = o->src_index; @@ -650,7 +650,7 @@ static int find_cache_pos(struct traverse_info *info, int p_len = tree_entry_len(p); for (pos = o->cache_bottom; pos < index->cache_nr; pos++) { - struct cache_entry *ce = index->cache[pos]; + const struct cache_entry *ce = index->cache[pos]; const char *ce_name, *ce_slash; int cmp, ce_len; @@ -1353,7 +1353,7 @@ static int verify_clean_subdirectory(const struct cache_entry *ce, */ static int icase_exists(struct unpack_trees_options *o, const char *name, int len, struct stat *st) { - struct cache_entry *src; + const struct cache_entry *src; src = index_name_exists(o->src_index, name, len, 1); return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE); @@ -1364,7 +1364,7 @@ static int check_ok_to_remove(const char *name, int len, int dtype, enum unpack_trees_error_types error_type, struct unpack_trees_options *o) { - struct cache_entry *result; + const struct cache_entry *result; /* * It may be that the 'lstat()' succeeded even though diff --git a/utf8.h b/utf8.h index 32a7bfb987..65d0e42b96 100644 --- a/utf8.h +++ b/utf8.h @@ -10,6 +10,7 @@ int utf8_strwidth(const char *string); int is_utf8(const char *text); int is_encoding_utf8(const char *name); int same_encoding(const char *, const char *); +__attribute__((format (printf, 2, 3))) int utf8_fprintf(FILE *, const char *, ...); void strbuf_add_wrapped_text(struct strbuf *buf, diff --git a/wrapper.c b/wrapper.c index dd7ecbb115..6a015de5f0 100644 --- a/wrapper.c +++ b/wrapper.c @@ -322,7 +322,7 @@ int git_mkstemps_mode(char *pattern, int suffix_len, int mode) template[5] = letters[v % num_letters]; v /= num_letters; fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, mode); - if (fd > 0) + if (fd >= 0) return fd; /* * Fatal error (EPERM, ENOSPC etc). diff --git a/wt-status.c b/wt-status.c index 9c4d086b51..cb24f1fa9b 100644 --- a/wt-status.c +++ b/wt-status.c @@ -244,7 +244,7 @@ static void wt_status_print_unmerged_data(struct wt_status *s, struct strbuf onebuf = STRBUF_INIT; const char *one, *how = _("bug"); - one = quote_path(it->string, -1, &onebuf, s->prefix); + one = quote_path(it->string, s->prefix, &onebuf); status_printf(s, color(WT_STATUS_HEADER, s), "\t"); switch (d->stagemask) { case 1: how = _("both deleted:"); break; @@ -298,8 +298,8 @@ static void wt_status_print_change_data(struct wt_status *s, change_type); } - one = quote_path(one_name, -1, &onebuf, s->prefix); - two = quote_path(two_name, -1, &twobuf, s->prefix); + one = quote_path(one_name, s->prefix, &onebuf); + two = quote_path(two_name, s->prefix, &twobuf); status_printf(s, color(WT_STATUS_HEADER, s), "\t"); switch (status) { @@ -372,7 +372,7 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q, static int unmerged_mask(const char *path) { int pos, mask; - struct cache_entry *ce; + const struct cache_entry *ce; pos = cache_name_pos(path, strlen(path)); if (0 <= pos) @@ -476,7 +476,7 @@ static void wt_status_collect_changes_initial(struct wt_status *s) for (i = 0; i < active_nr; i++) { struct string_list_item *it; struct wt_status_change_data *d; - struct cache_entry *ce = active_cache[i]; + const struct cache_entry *ce = active_cache[i]; if (!ce_path_match(ce, &pathspec)) continue; @@ -707,8 +707,7 @@ static void wt_status_print_other(struct wt_status *s, struct string_list_item *it; const char *path; it = &(l->items[i]); - path = quote_path(it->string, strlen(it->string), - &buf, s->prefix); + path = quote_path(it->string, s->prefix, &buf); if (column_active(s->colopts)) { string_list_append(&output, path); continue; @@ -1293,7 +1292,7 @@ static void wt_shortstatus_unmerged(struct string_list_item *it, } else { struct strbuf onebuf = STRBUF_INIT; const char *one; - one = quote_path(it->string, -1, &onebuf, s->prefix); + one = quote_path(it->string, s->prefix, &onebuf); printf(" %s\n", one); strbuf_release(&onebuf); } @@ -1321,7 +1320,7 @@ static void wt_shortstatus_status(struct string_list_item *it, struct strbuf onebuf = STRBUF_INIT; const char *one; if (d->head_path) { - one = quote_path(d->head_path, -1, &onebuf, s->prefix); + one = quote_path(d->head_path, s->prefix, &onebuf); if (*one != '"' && strchr(one, ' ') != NULL) { putchar('"'); strbuf_addch(&onebuf, '"'); @@ -1330,7 +1329,7 @@ static void wt_shortstatus_status(struct string_list_item *it, printf("%s -> ", one); strbuf_release(&onebuf); } - one = quote_path(it->string, -1, &onebuf, s->prefix); + one = quote_path(it->string, s->prefix, &onebuf); if (*one != '"' && strchr(one, ' ') != NULL) { putchar('"'); strbuf_addch(&onebuf, '"'); @@ -1349,7 +1348,7 @@ static void wt_shortstatus_other(struct string_list_item *it, } else { struct strbuf onebuf = STRBUF_INIT; const char *one; - one = quote_path(it->string, -1, &onebuf, s->prefix); + one = quote_path(it->string, s->prefix, &onebuf); color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), "%s", sign); printf(" %s\n", one); strbuf_release(&onebuf); diff --git a/wt-status.h b/wt-status.h index 4121bc208d..fb7152e187 100644 --- a/wt-status.h +++ b/wt-status.h @@ -96,9 +96,9 @@ void wt_status_get_state(struct wt_status_state *state, int get_detached_from); void wt_shortstatus_print(struct wt_status *s); void wt_porcelain_print(struct wt_status *s); -void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...) - ; -void status_printf(struct wt_status *s, const char *color, const char *fmt, ...) - ; +__attribute__((format (printf, 3, 4))) +void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...); +__attribute__((format (printf, 3, 4))) +void status_printf(struct wt_status *s, const char *color, const char *fmt, ...); #endif /* STATUS_H */